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