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