Respect $HOME when searching for ~/.enscriptrc
[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       buffer_append (&cmd, title);
1549       buffer_append (&cmd, "\" ");
1550
1551       buffer_append (&cmd, "-Dtoc=");
1552       buffer_append (&cmd, toc ? "1" : "0");
1553
1554       /* Additional options for states? */
1555       if (helper_options['s'])
1556         {
1557           Buffer *opts = helper_options['s'];
1558
1559           buffer_append (&cmd, " ");
1560           buffer_append_len (&cmd, buffer_ptr (opts), buffer_len (opts));
1561         }
1562
1563       /* Append input files. */
1564       for (i = optind; i < argc; i++)
1565         {
1566           buffer_append (&cmd, " ");
1567           buffer_append (&cmd, argv[i]);
1568         }
1569
1570       /* And do the job. */
1571       if (is_open (&is, stdin, NULL, buffer_ptr (&cmd)))
1572         {
1573           open_output_file ();
1574           process_file ("unused", &is, 0);
1575           is_close (&is);
1576         }
1577
1578       buffer_uninit (&cmd);
1579     }
1580   else
1581     {
1582       /* The conventional way. */
1583
1584       /* Highlighting. */
1585       if (highlight)
1586         {
1587           char fbuf[256];
1588
1589           /* Create a highlight input filter. */
1590           buffer_clear (&buffer);
1591           buffer_append (&buffer, states_binary);
1592           buffer_append (&buffer, " -f \"");
1593           buffer_append (&buffer, states_config_file);
1594           buffer_append (&buffer, "\" -p \"");
1595           buffer_append (&buffer, states_path);
1596           buffer_append (&buffer, "\"");
1597
1598           if (verbose > 0)
1599             buffer_append (&buffer, " -v");
1600
1601           if (hl_start_state)
1602             {
1603               buffer_append (&buffer, " -s ");
1604               buffer_append (&buffer, hl_start_state);
1605             }
1606
1607           buffer_append (&buffer, " -Dcolor=");
1608           buffer_append (&buffer, states_color ? "1" : "0");
1609
1610           buffer_append (&buffer, " -Dstyle=");
1611           buffer_append (&buffer, states_highlight_style);
1612
1613           buffer_append (&buffer, " -Dfont_spec=");
1614           buffer_append (&buffer, Fname);
1615           sprintf (fbuf, "@%g/%g", Fpt.w, Fpt.h);
1616           buffer_append (&buffer, fbuf);
1617
1618           /* Additional options for states? */
1619           if (helper_options['s'])
1620             {
1621               Buffer *opts = helper_options['s'];
1622
1623               buffer_append (&buffer, " ");
1624               buffer_append_len (&buffer,
1625                                  buffer_ptr (opts), buffer_len (opts));
1626             }
1627
1628           buffer_append (&buffer, " \"%s\"");
1629
1630           input_filter = buffer_copy (&buffer);
1631           input_filter_stdin = "-";
1632         }
1633
1634       /* Table of Contents. */
1635       if (toc)
1636         {
1637           toc_fp = tmpfile ();
1638           if (toc_fp == NULL)
1639             FATAL ((stderr, _("couldn't create temporary toc file: %s"),
1640                     strerror (errno)));
1641         }
1642
1643
1644       /*
1645        * Process files.
1646        */
1647
1648       if (optind == argc)
1649         {
1650           /* stdin's modification time is the current time. */
1651           memcpy (&mod_tm, &run_tm, sizeof (run_tm));
1652
1653           if (is_open (&is, stdin, NULL, input_filter))
1654             {
1655               /* Open output file. */
1656               open_output_file ();
1657               process_file (title_given ? title : "", &is, 0);
1658               is_close (&is);
1659             }
1660         }
1661       else
1662         {
1663           for (; optind < argc; optind++)
1664             {
1665               if (is_open (&is, NULL, argv[optind], input_filter))
1666                 {
1667                   struct stat stat_st;
1668
1669                   /* Get modification time. */
1670                   if (stat (argv[optind], &stat_st) == 0)
1671                     {
1672                       tim = stat_st.st_mtime;
1673                       tm = localtime (&tim);
1674                       memcpy (&mod_tm, tm, sizeof (*tm));
1675
1676                       /*
1677                        * Open output file.  Output file opening is delayed to
1678                        * this point so we can optimize the case when a
1679                        * non-existing input file is printed => we do nothing.
1680                        */
1681                       open_output_file ();
1682
1683                       process_file (argv[optind], &is, 0);
1684                     }
1685                   else
1686                     ERROR ((stderr, _("couldn't stat input file \"%s\": %s"),
1687                             argv[optind],
1688                             strerror (errno)));
1689
1690                   is_close (&is);
1691                 }
1692             }
1693         }
1694
1695       /* Table of Contents. */
1696       if (toc)
1697         {
1698           /* This is really cool... */
1699
1700           /* Set the printing options for toc. */
1701           toc = 0;
1702           special_escapes = 1;
1703           line_numbers = 0;
1704
1705           if (fseek (toc_fp, 0, SEEK_SET) != 0)
1706             FATAL ((stderr, _("couldn't rewind toc file: %s"),
1707                     strerror (errno)));
1708
1709           memcpy (&mod_tm, &run_tm, sizeof (run_tm));
1710           if (is_open (&is, toc_fp, NULL, NULL))
1711             {
1712               process_file (_("Table of Contents"), &is, 1);
1713               is_close (&is);
1714             }
1715         }
1716
1717       /* Give trailer a chance to dump itself. */
1718       dump_ps_trailer ();
1719
1720       /*
1721        * Append ^D to the end of the output?  Note! It must be ^D followed
1722        * by a newline.
1723        */
1724       if (ofp != NULL && append_ctrl_D)
1725         fprintf (ofp, "\004\n");
1726     }
1727
1728   /* Close output file. */
1729   close_output_file ();
1730
1731   /* Tell how things went. */
1732   if (ofp == NULL)
1733     {
1734       /*
1735        * The value of <ofp> is not reset in close_output_file(),
1736        * this is ugly but it saves one flag.
1737        */
1738       MESSAGE (0, (stderr, _("no output generated\n")));
1739     }
1740   else if (output_language_pass_through)
1741     {
1742       if (output_file == OUTPUT_FILE_NONE)
1743         MESSAGE (0, (stderr, _("output sent to %s\n"),
1744                      printer ? printer : _("printer")));
1745       else
1746         MESSAGE (0, (stderr, _("output left in %s\n"),
1747                      output_file == OUTPUT_FILE_STDOUT ? "-" : output_file));
1748     }
1749   else
1750     {
1751       unsigned int real_total_pages;
1752
1753       if (nup > 1)
1754         {
1755           if (total_pages > 0)
1756             real_total_pages = (total_pages - 1) / nup + 1;
1757           else
1758             real_total_pages = 0;
1759         }
1760       else
1761         real_total_pages = total_pages;
1762
1763       /* We did something, tell what.  */
1764       MESSAGE (0, (stderr, _("[ %d pages * %d copy ]"), real_total_pages,
1765                    num_copies));
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, _("%d lines were %s\n"), num_truncated_lines,
1776                        line_end == LE_TRUNCATE
1777                        ? _("truncated") : _("wrapped")));
1778         }
1779
1780       if (num_missing_chars)
1781         {
1782           retval |= 4;
1783           MESSAGE (0, (stderr, _("%d characters were missing\n"),
1784                        num_missing_chars));
1785           if (list_missing_characters)
1786             {
1787               MESSAGE (0, (stderr, _("missing character codes (decimal):\n")));
1788               do_list_missing_characters (missing_chars);
1789             }
1790         }
1791
1792       if (num_non_printable_chars)
1793         {
1794           retval |= 8;
1795           MESSAGE (0, (stderr, _("%d non-printable characters\n"),
1796                        num_non_printable_chars));
1797           if (list_missing_characters)
1798             {
1799               MESSAGE (0, (stderr,
1800                            _("non-printable character codes (decimal):\n")));
1801               do_list_missing_characters (non_printable_chars);
1802             }
1803         }
1804     }
1805
1806   /* Uninit our dynamic memory buffer. */
1807   buffer_uninit (&buffer);
1808
1809   /* Return the extended return values only if requested. */
1810   if (!extended_return_values)
1811     retval = 0;
1812
1813   /* This is the end. */
1814   return retval;
1815 }
1816
1817
1818 /*
1819  * Static functions.
1820  */
1821
1822 static void
1823 open_output_file ()
1824 {
1825   if (ofp)
1826     /* Output file has already been opened, do nothing. */
1827     return;
1828
1829   if (output_file == OUTPUT_FILE_NONE)
1830     {
1831       char spooler_options[512];
1832
1833       /* Format spooler options. */
1834       spooler_options[0] = '\0';
1835       if (mail)
1836         strcat (spooler_options, "-m ");
1837       if (no_job_header)
1838         {
1839           strcat (spooler_options, no_job_header_switch);
1840           strcat (spooler_options, " ");
1841         }
1842       if (printer_options)
1843         strcat (spooler_options, printer_options);
1844
1845       /* Open printer. */
1846       ofp = printer_open (spooler_command, spooler_options, queue_param,
1847                           printer, &printer_context);
1848       if (ofp == NULL)
1849         FATAL ((stderr, _("couldn't open printer `%s': %s"), printer,
1850                 strerror (errno)));
1851     }
1852   else if (output_file == OUTPUT_FILE_STDOUT)
1853     ofp = stdout;
1854   else
1855     {
1856       ofp = fopen (output_file, "w");
1857       if (ofp == NULL)
1858         FATAL ((stderr, _("couldn't create output file \"%s\": %s"),
1859                 output_file, strerror (errno)));
1860     }
1861 }
1862
1863
1864 static void
1865 close_output_file ()
1866 {
1867   if (ofp == NULL)
1868     /* Output file hasn't been opened, we are done. */
1869     return;
1870
1871   if (output_file == OUTPUT_FILE_NONE)
1872     printer_close (printer_context);
1873   else if (output_file != OUTPUT_FILE_STDOUT)
1874     if (fclose (ofp))
1875       FATAL ((stderr, _("couldn't close output file \"%s\": %s"),
1876               output_file, strerror (errno)));
1877
1878   /* We do not reset <ofp> since its value is needed in diagnostigs. */
1879 }
1880
1881
1882 static void
1883 handle_env_options (char *var)
1884 {
1885   int argc;
1886   char **argv;
1887   char *string;
1888   char *str;
1889   int i;
1890
1891   string = getenv (var);
1892   if (string == NULL)
1893     return;
1894
1895   MESSAGE (2, (stderr, "handle_env_options(): %s=\"%s\"\n", var, string));
1896
1897   /* Copy string so we can modify it in place. */
1898   str = xstrdup (string);
1899
1900   /*
1901    * We can count this, each option takes at least 1 character and one
1902    * space.  We also need one for program's name and one for the
1903    * trailing NULL.
1904    */
1905   argc = (strlen (str) + 1) / 2 + 2;
1906   argv = xcalloc (argc, sizeof (char *));
1907
1908   /* Set program name. */
1909   argc = 0;
1910   argv[argc++] = program;
1911
1912   /* Split string and set arguments to argv array. */
1913   i = 0;
1914   while (str[i])
1915     {
1916       /* Skip leading whitespace. */
1917       for (; str[i] && isspace (str[i]); i++)
1918         ;
1919       if (!str[i])
1920         break;
1921
1922       /* Check for quoted arguments. */
1923       if (str[i] == '"' || str[i] == '\'')
1924         {
1925           int endch = str[i++];
1926
1927           argv[argc++] = str + i;
1928
1929           /* Skip until we found the end of the quotation. */
1930           for (; str[i] && str[i] != endch; i++)
1931             ;
1932           if (!str[i])
1933             FATAL ((stderr, _("syntax error in option string %s=\"%s\":\n\
1934 missing end of quotation: %c"), var, string, endch));
1935
1936           str[i++] = '\0';
1937         }
1938       else
1939         {
1940           argv[argc++] = str + i;
1941
1942           /* Skip until whitespace if found. */
1943           for (; str[i] && !isspace (str[i]); i++)
1944             ;
1945           if (str[i])
1946             str[i++] = '\0';
1947         }
1948     }
1949
1950   /* argv[argc] must be NULL. */
1951   argv[argc] = NULL;
1952
1953   MESSAGE (2, (stderr, "found following options (argc=%d):\n", argc));
1954   for (i = 0; i < argc; i++)
1955     MESSAGE (2, (stderr, "%3d = \"%s\"\n", i, argv[i]));
1956
1957   /* Process options. */
1958   handle_options (argc, argv);
1959
1960   /* Check that all got processed. */
1961   if (optind != argc)
1962     {
1963       MESSAGE (0,
1964                (stderr,
1965                 _("warning: didn't process following options from \
1966 environment variable %s:\n"),
1967                 var));
1968       for (; optind < argc; optind++)
1969         MESSAGE (0, (stderr, _("  option %d = \"%s\"\n"), optind,
1970                      argv[optind]));
1971     }
1972
1973   /* Cleanup. */
1974   xfree (argv);
1975
1976   /*
1977    * <str> must not be freed, since some global variables can point to
1978    * its elements
1979    */
1980 }
1981
1982
1983 static void
1984 handle_options (int argc, char *argv[])
1985 {
1986   int c;
1987   PageRange *prange;
1988
1989   /* Reset optind. */
1990   optind = 0;
1991
1992   while (1)
1993     {
1994       int option_index = 0;
1995       const char *cp;
1996       int i;
1997
1998       c = getopt_long (argc, argv,
1999                        "#: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",
2000                        long_options, &option_index);
2001
2002       if (c == -1)
2003         break;
2004
2005       switch (c)
2006         {
2007         case 0:                 /* Long option found. */
2008           cp = long_options[option_index].name;
2009
2010           if (strcmp (cp, "columns") == 0)
2011             {
2012               num_columns = atoi (optarg);
2013               if (num_columns < 1)
2014                 FATAL ((stderr,
2015                         _("number of columns must be larger than zero")));
2016             }
2017           break;
2018
2019           /* Short options. */
2020
2021         case '1':               /* 1 column */
2022         case '2':               /* 2 columns */
2023         case '3':               /* 3 columns */
2024         case '4':               /* 4 columns */
2025         case '5':               /* 5 columns */
2026         case '6':               /* 6 columns */
2027         case '7':               /* 7 columns */
2028         case '8':               /* 8 columns */
2029         case '9':               /* 9 columns */
2030           num_columns = c - '0';
2031           break;
2032
2033         case 'a':               /* pages */
2034           prange = (PageRange *) xcalloc (1, sizeof (PageRange));
2035
2036           if (strcmp (optarg, "odd") == 0)
2037             prange->odd = 1;
2038           else if (strcmp (optarg, "even") == 0)
2039             prange->even = 1;
2040           else
2041             {
2042               cp = strchr (optarg, '-');
2043               if (cp)
2044                 {
2045                   if (optarg[0] == '-')
2046                     /* -end */
2047                     prange->end = atoi (optarg + 1);
2048                   else if (cp[1] == '\0')
2049                     {
2050                       /* begin- */
2051                       prange->start = atoi (optarg);
2052                       prange->end = (unsigned int) -1;
2053                     }
2054                   else
2055                     {
2056                       /* begin-end */
2057                       prange->start = atoi (optarg);
2058                       prange->end = atoi (cp + 1);
2059                     }
2060                 }
2061               else
2062                 /* pagenumber */
2063                 prange->start = prange->end = atoi (optarg);
2064             }
2065
2066           prange->next = page_ranges;
2067           page_ranges = prange;
2068           break;
2069
2070         case 'A':               /* file alignment */
2071           file_align = atoi (optarg);
2072           if (file_align == 0)
2073             FATAL ((stderr, _("file alignment must be larger than zero")));
2074           break;
2075
2076         case 'b':               /* page header */
2077           page_header = optarg;
2078           break;
2079
2080         case 'B':               /* no page headers */
2081           header = HDR_NONE;
2082           break;
2083
2084         case 'c':               /* truncate (cut) long lines */
2085           line_end = LE_TRUNCATE;
2086           break;
2087
2088         case 'C':               /* line numbers */
2089           line_numbers = 1;
2090           if (optarg)
2091             start_line_number = atoi (optarg);
2092           break;
2093
2094         case 'd':               /* specify printer */
2095         case 'P':
2096           xfree (printer);
2097           printer = xstrdup (optarg);
2098           output_file = OUTPUT_FILE_NONE;
2099           break;
2100
2101         case 'D':               /* setpagedevice */
2102           parse_key_value_pair (pagedevice, optarg);
2103           break;
2104
2105         case 'e':               /* special escapes */
2106           special_escapes = 1;
2107           if (optarg)
2108             {
2109               /* Specify the escape character. */
2110               if (isdigit (optarg[0]))
2111                 /* As decimal, octal, or hexadicimal number. */
2112                 escape_char = (int) strtoul (optarg, NULL, 0);
2113               else
2114                 /* As character directly. */
2115                 escape_char = ((unsigned char *) optarg)[0];
2116             }
2117           break;
2118
2119         case 'E':               /* highlight */
2120           highlight = 1;
2121           special_escapes = 1;
2122           escape_char = '\0';
2123           hl_start_state = optarg;
2124           break;
2125
2126         case 'f':               /* font */
2127           if (!parse_font_spec (optarg, &Fname, &Fpt, NULL))
2128             FATAL ((stderr, _("malformed font spec: %s"), optarg));
2129           user_body_font_defined = 1;
2130           break;
2131
2132         case 'F':               /* header font */
2133           if (!parse_font_spec (optarg, &HFname, &HFpt, NULL))
2134             FATAL ((stderr, _("malformed font spec: %s"), optarg));
2135           break;
2136
2137         case 'g':               /* print anyway */
2138           /* nothing. */
2139           break;
2140
2141         case 'G':               /* fancy header */
2142           header = HDR_FANCY;
2143           if (optarg)
2144             fancy_header_name = optarg;
2145           else
2146             fancy_header_name = fancy_header_default;
2147
2148           if (!file_existsp (fancy_header_name, ".hdr"))
2149             FATAL ((stderr,
2150                     _("couldn't find header definition file \"%s.hdr\""),
2151                     fancy_header_name));
2152           break;
2153
2154         case 'h':               /* no job header */
2155           no_job_header = 1;
2156           break;
2157
2158         case 'H':               /* highlight bars */
2159           if (optarg)
2160             highlight_bars = atoi (optarg);
2161           else
2162             highlight_bars = 2;
2163           break;
2164
2165         case 'i':               /* line indent */
2166           line_indent_spec = optarg;
2167           break;
2168
2169         case 'I':               /* input filter */
2170           input_filter = optarg;
2171           break;
2172
2173         case 'j':               /* borders */
2174           borders = 1;
2175           break;
2176
2177         case 'k':               /* enable page prefeed */
2178           page_prefeed = 1;
2179           break;
2180
2181         case 'K':               /* disable page prefeed */
2182           page_prefeed = 0;
2183           break;
2184
2185         case 'l':               /* emulate lineprinter */
2186           lines_per_page = 66;
2187           header = HDR_NONE;
2188           break;
2189
2190         case 'L':               /* lines per page */
2191           lines_per_page = atoi (optarg);
2192           if (lines_per_page <= 0)
2193             FATAL ((stderr,
2194                     _("must print at least one line per each page: %s"),
2195                     argv[optind]));
2196           break;
2197
2198         case 'm':               /* send mail upon completion */
2199           mail = 1;
2200           break;
2201
2202         case 'M':               /* select output media */
2203           media_name = xstrdup (optarg);
2204           break;
2205
2206         case 'n':               /* num copies */
2207         case '#':
2208           num_copies = atoi (optarg);
2209           break;
2210
2211         case 'N':               /* newline character */
2212           if (!(optarg[0] == 'n' || optarg[0] == 'r') || optarg[1] != '\0')
2213             {
2214               fprintf (stderr, _("%s: illegal newline character specifier: \
2215 '%s': expected 'n' or 'r'\n"),
2216                        program, optarg);
2217               goto option_error;
2218             }
2219           if (optarg[0] == 'n')
2220             nl = '\n';
2221           else
2222             nl = '\r';
2223           break;
2224
2225         case 'o':
2226         case 'p':               /* output file */
2227           /* Check output file "-". */
2228           if (strcmp (optarg, "-") == 0)
2229             output_file = OUTPUT_FILE_STDOUT;
2230           else
2231             output_file = optarg;
2232           break;
2233
2234         case 'O':               /* list missing characters */
2235           list_missing_characters = 1;
2236           break;
2237
2238         case 'q':               /* quiet */
2239           quiet = 1;
2240           verbose = 0;
2241           break;
2242
2243         case 'r':               /* landscape */
2244           landscape = 1;
2245           break;
2246
2247         case 'R':               /* portrait */
2248           landscape = 0;
2249           break;
2250
2251         case 's':               /* baselineskip */
2252           baselineskip = atof (optarg);
2253           break;
2254
2255         case 'S':               /* statusdict */
2256           parse_key_value_pair (statusdict, optarg);
2257           break;
2258
2259         case 't':               /* title */
2260         case 'J':
2261           title = optarg;
2262           title_given = 1;
2263           break;
2264
2265         case 'T':               /* tabulator size */
2266           tabsize = atoi (optarg);
2267           if (tabsize <= 0)
2268             tabsize = 1;
2269           break;
2270
2271         case 'u':               /* underlay */
2272           underlay = optarg;
2273           break;
2274
2275         case 'U':               /* nup */
2276           nup = atoi (optarg);
2277           break;
2278
2279         case 'v':               /* verbose */
2280           if (optarg)
2281             verbose = atoi (optarg);
2282           else
2283             verbose++;
2284           quiet = 0;
2285           break;
2286
2287         case 'V':               /* version */
2288           version ();
2289           exit (0);
2290           break;
2291
2292         case 'w':               /* output language */
2293           output_language = optarg;
2294           if (strcmp (output_language, "PostScript") != 0)
2295             /* Other output languages are handled with states. */
2296             output_language_pass_through = 1;
2297           break;
2298
2299         case 'W':               /* a helper application option */
2300           cp = strchr (optarg, ',');
2301           if (cp == NULL)
2302             FATAL ((stderr,
2303                     _("malformed argument `%s' for option -W, --option: \
2304 no comma found"),
2305                       optarg));
2306
2307           if (cp - optarg != 1)
2308             FATAL ((stderr, _("helper application specification must be \
2309 single character: %s"),
2310                               optarg));
2311
2312           /* Take the index of the helper application and update `cp'
2313              to point to the beginning of the option. */
2314           i = *optarg;
2315           cp++;
2316
2317           if (helper_options[i] == NULL)
2318             helper_options[i] = buffer_alloc ();
2319           else
2320             {
2321               /* We already had some options for this helper
2322                  application.  Let's separate these arguments. */
2323               buffer_append (helper_options[i], " ");
2324             }
2325
2326           /* Add this new option. */
2327           buffer_append (helper_options[i], cp);
2328           break;
2329
2330         case 'X':               /* input encoding */
2331           xfree (encoding_name);
2332           encoding_name = xstrdup (optarg);
2333           break;
2334
2335         case 'z':               /* no form feeds */
2336           interpret_formfeed = 0;
2337           break;
2338
2339         case 'Z':               /* pass through */
2340           pass_through = 1;
2341           break;
2342
2343         case 128:               /* underlay font */
2344           if (!parse_font_spec (optarg, &ul_font, &ul_ptsize, NULL))
2345             FATAL ((stderr, _("malformed font spec: %s"), optarg));
2346           break;
2347
2348         case 129:               /* underlay gray */
2349           ul_gray = atof (optarg);
2350           break;
2351
2352         case 130:               /* page label format */
2353           xfree (page_label_format);
2354           page_label_format = xstrdup (optarg);
2355           break;
2356
2357         case 131:               /* download font */
2358           strhash_put (download_fonts, optarg, strlen (optarg) + 1, NULL,
2359                        NULL);
2360           break;
2361
2362         case 132:               /* underlay angle */
2363           ul_angle = atof (optarg);
2364           ul_angle_p = 1;
2365           break;
2366
2367         case 133:               /* underlay position */
2368           xfree (ul_position);
2369           ul_position = xstrdup (optarg);
2370           ul_position_p = 1;
2371           break;
2372
2373         case 134:               /* non-printable format */
2374           xfree (npf_name);
2375           npf_name = xstrdup (optarg);
2376           break;
2377
2378         case 135:               /* help */
2379           usage ();
2380           exit (0);
2381           break;
2382
2383         case 136:               /* highlight bar gray */
2384           highlight_bar_gray = atof (optarg);
2385           break;
2386
2387         case 137:               /* underlay style */
2388           xfree (ul_style_str);
2389           ul_style_str = xstrdup (optarg);
2390           break;
2391
2392         case 138:               /* filter stdin */
2393           input_filter_stdin = optarg;
2394           break;
2395
2396         case 139:               /* extra options for the printer spooler */
2397           printer_options = optarg;
2398           break;
2399
2400         case 140:               /* slicing */
2401           slicing = 1;
2402           slice = atoi (optarg);
2403           if (slice <= 0)
2404             FATAL ((stderr, _("slice must be greater than zero")));
2405           break;
2406
2407         case 141:               /* help-highlight */
2408           help_highlight = 1;
2409           break;
2410
2411         case 142:               /* States color? */
2412           if (optarg == NULL)
2413             states_color = 1;
2414           else
2415             states_color = atoi (optarg);
2416           break;
2417
2418         case 143:               /* mark-wrapped-lines */
2419           if (optarg)
2420             {
2421               xfree (mark_wrapped_lines_style_name);
2422               mark_wrapped_lines_style_name = xstrdup (optarg);
2423             }
2424           else
2425             /* Set the system default. */
2426             mark_wrapped_lines_style = MWLS_BOX;
2427           break;
2428
2429         case 144:               /* adjust margins */
2430           margins_spec = optarg;
2431           break;
2432
2433         case 145:               /* N-up x-pad */
2434           nup_xpad = atoi (optarg);
2435           break;
2436
2437         case 146:               /* N-up y-pad */
2438           nup_ypad = atoi (optarg);
2439           break;
2440
2441         case 147:               /* word wrap */
2442           line_end = LE_WORD_WRAP;
2443           break;
2444
2445         case 148:               /* horizontal column height */
2446           horizontal_column_height = atof (optarg);
2447           formfeed_type = FORMFEED_HCOLUMN;
2448           break;
2449
2450         case 149:               /* PostScript language level */
2451           pslevel = atoi (optarg);
2452           break;
2453
2454         case 150:               /* rotate even-numbered pages */
2455           rotate_even_pages = 1;
2456           break;
2457
2458         case 151:               /* highlight style */
2459           xfree (states_highlight_style);
2460           states_highlight_style = xstrdup (optarg);
2461           break;
2462
2463         case 152:               /* N-up colunwise */
2464           nup_columnwise = 1;
2465           break;
2466
2467         case 153:               /* swap even page margins */
2468           swap_even_page_margins = 1;
2469           break;
2470
2471         case 154:               /* extended return values */
2472           extended_return_values = 1;
2473           break;
2474
2475         case 155:               /* footer */
2476           page_footer = optarg;
2477           break;
2478
2479         case 156:               /* continuous page numbers */
2480           continuous_page_numbers = 1;
2481           break;
2482
2483         case '?':               /* Errors found during getopt_long(). */
2484         option_error:
2485           fprintf (stderr, _("Try `%s --help' for more information.\n"),
2486                    program);
2487           exit (1);
2488           break;
2489
2490         default:
2491           printf ("Hey!  main() didn't handle option \"%c\" (%d)", c, c);
2492           if (optarg)
2493             printf (" with arg %s", optarg);
2494           printf ("\n");
2495           FATAL ((stderr, "This is a bug!"));
2496           break;
2497         }
2498     }
2499 }
2500
2501
2502 static void
2503 usage ()
2504 {
2505   printf (_("\
2506 Usage: %s [OPTION]... [FILE]...\n\
2507 Mandatory arguments to long options are mandatory for short options too.\n\
2508   -#                         an alias for option -n, --copies\n\
2509   -1                         same as --columns=1\n\
2510   -2                         same as --columns=2\n\
2511       --columns=NUM          specify the number of columns per page\n\
2512   -a, --pages=PAGES          specify which pages are printed\n\
2513   -A, --file-align=ALIGN     align separate input files to ALIGN\n\
2514   -b, --header=HEADER        set page header\n\
2515   -B, --no-header            no page headers\n\
2516   -c, --truncate-lines       cut long lines (default is to wrap)\n\
2517   -C, --line-numbers[=START]\n\
2518                              precede each line with its line number\n\
2519   -d                         an alias for option --printer\n\
2520   -D, --setpagedevice=KEY[:VALUE]\n\
2521                              pass a page device definition to output\n\
2522   -e, --escapes[=CHAR]       enable special escape interpretation\n"),
2523           program);
2524
2525   printf (_("\
2526   -E, --highlight[=LANG]     highlight source code\n"));
2527
2528   printf (_("\
2529   -f, --font=NAME            use font NAME for body text\n\
2530   -F, --header-font=NAME     use font NAME for header texts\n\
2531   -g, --print-anyway         nothing (compatibility option)\n\
2532   -G                         same as --fancy-header\n\
2533       --fancy-header[=NAME]  select fancy page header\n\
2534   -h, --no-job-header        suppress the job header page\n\
2535   -H, --highlight-bars=NUM   specify how high highlight bars are\n\
2536   -i, --indent=NUM           set line indent to NUM characters\n\
2537   -I, --filter=CMD           read input files through input filter CMD\n\
2538   -j, --borders              print borders around columns\n\
2539   -J,                        an alias for option --title\n\
2540   -k, --page-prefeed         enable page prefeed\n\
2541   -K, --no-page-prefeed      disable page prefeed\n\
2542   -l, --lineprinter          simulate lineprinter, this is an alias for:\n\
2543                                --lines-per-page=66, --no-header, --portrait,\n\
2544                                --columns=1\n"));
2545
2546   printf (_("\
2547   -L, --lines-per-page=NUM   specify how many lines are printed on each page\n\
2548   -m, --mail                 send mail upon completion\n\
2549   -M, --media=NAME           use output media NAME\n\
2550   -n, --copies=NUM           print NUM copies of each page\n\
2551   -N, --newline=NL           select the newline character.  Possible\n\
2552                              values for NL are: n (`\\n') and r (`\\r').\n\
2553   -o                         an alias for option --output\n\
2554   -O, --missing-characters   list missing characters\n\
2555   -p, --output=FILE          leave output to file FILE.  If FILE is `-',\n\
2556                              leave output to stdout.\n\
2557   -P, --printer=NAME         print output to printer NAME\n\
2558   -q, --quiet, --silent      be really quiet\n\
2559   -r, --landscape            print in landscape mode\n\
2560   -R, --portrait             print in portrait mode\n"));
2561
2562   printf (_("\
2563   -s, --baselineskip=NUM     set baselineskip to NUM\n\
2564   -S, --statusdict=KEY[:VALUE]\n\
2565                              pass a statusdict definition to the output\n\
2566   -t, --title=TITLE          set banner page's job title to TITLE.  Option\n\
2567                              sets also the name of the input file stdin.\n\
2568   -T, --tabsize=NUM          set tabulator size to NUM\n\
2569   -u, --underlay[=TEXT]      print TEXT under every page\n\
2570   -U, --nup=NUM              print NUM logical pages on each output page\n\
2571   -v, --verbose              tell what we are doing\n\
2572   -V, --version              print version number\n\
2573   -w, --language=LANG        set output language to LANG\n\
2574   -W, --options=APP,OPTION   pass option OPTION to helper application APP\n\
2575   -X, --encoding=NAME        use input encoding NAME\n\
2576   -z, --no-formfeed          do not interpret form feed characters\n\
2577   -Z, --pass-through         pass through PostScript and PCL files\n\
2578                              without any modifications\n"));
2579
2580   printf (_("Long-only options:\n\
2581   --color[=bool]             create color outputs with states\n\
2582   --continuous-page-numbers  count page numbers across input files.  Don't\n\
2583                              restart numbering at beginning of each file.\n\
2584   --download-font=NAME       download font NAME\n\
2585   --extended-return-values   enable extended return values\n\
2586   --filter-stdin=NAME        specify how stdin is shown to the input filter\n\
2587   --footer=FOOTER            set page footer\n\
2588   --h-column-height=HEIGHT   set the horizontal column height to HEIGHT\n\
2589   --help                     print this help and exit\n"));
2590
2591   printf (_("\
2592   --help-highlight           describe all supported --highlight languages\n\
2593                              and file formats\n\
2594   --highlight-bar-gray=NUM   print highlight bars with gray NUM (0 - 1)\n\
2595   --list-media               list names of all known media\n\
2596   --margins=LEFT:RIGHT:TOP:BOTTOM\n\
2597                              adjust page marginals\n\
2598   --mark-wrapped-lines[STYLE]\n\
2599                              mark wrapped lines in the output with STYLE\n\
2600   --non-printable-format=FMT specify how non-printable chars are printed\n"));
2601
2602   printf (_("\
2603   --nup-columnwise           layout pages in the N-up printing columnwise\n\
2604   --nup-xpad=NUM             set the page x-padding of N-up printing to NUM\n\
2605   --nup-ypad=NUM             set the page y-padding of N-up printing to NUM\n\
2606   --page-label-format=FMT    set page label format to FMT\n\
2607   --ps-level=LEVEL           set the PostScript language level that enscript\n\
2608                              should use\n\
2609   --printer-options=OPTIONS  pass extra options to the printer command\n\
2610   --rotate-even-pages        rotate even-numbered pages 180 degrees\n"));
2611
2612   printf (_("\
2613   --slice=NUM                print vertical slice NUM\n\
2614   --style=STYLE              use highlight style STYLE\n\
2615   --swap-even-page-margins   swap left and right side margins for each even\n\
2616                              numbered page\n\
2617   --toc                      print table of contents\n\
2618   --ul-angle=ANGLE           set underlay text's angle to ANGLE\n\
2619   --ul-font=NAME             print underlays with font NAME\n\
2620   --ul-gray=NUM              print underlays with gray value NUM\n\
2621   --ul-position=POS          set underlay's starting position to POS\n\
2622   --ul-style=STYLE           print underlays with style STYLE\n\
2623   --word-wrap                wrap long lines from word boundaries\n\
2624 "));
2625
2626   printf (_("\nReport bugs to %s.\n"), PACKAGE_BUGREPORT);
2627 }
2628
2629
2630 static void
2631 version ()
2632 {
2633   printf ("%s\n\
2634 Copyright (C) 2003 Markku Rossi.\n\
2635 GNU enscript comes with NO WARRANTY, to the extent permitted by law.\n\
2636 You may redistribute copies of GNU enscript under the terms of the GNU\n\
2637 General Public License.  For more information about these matters, see\n\
2638 the files named COPYING.\n",
2639           version_string);
2640 }