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