/*
 * Argument handling and main.
 * Copyright (c) 1995-2003 Markku Rossi.
 *
 * Author: Markku Rossi <mtr@iki.fi>
 */

/*
 * This file is part of GNU Enscript.
 *
 * Enscript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Enscript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Enscript.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "gsint.h"
#include "getopt.h"

/*
 * Prototypes for static functions.
 */

/*
 * Open output file according to user options.  Void if output file
 * has already been opened.
 */
static void open_output_file ();

/* Close output file. */
static void close_output_file ();

/* Handle options from environment variable <var> */
static void handle_env_options ___P ((char *var));

/* Handle options from <argv> array. */
static void handle_options ___P ((int argc, char *argv[]));

/* Print usage info. */
static void usage ();

/* Print version info. */
static void version ();


/*
 * Global variables.
 */

char *program;			/* Program's name, used for messages. */
FILE *ofp = NULL;		/* Output file. */
void *printer_context;		/* Context for the printer. */
char *date_string = NULL;	/* Preformatted time string. */
struct tm run_tm;		/* Time when program is run. */
struct tm mod_tm;		/* Last modification time for current file. */
struct passwd *passwd;		/* Passwd entry for the user running this
				   program. */

/* Path to our library. */
char *enscript_library = LIBRARY;

/* Library lookup path. */
char *libpath = NULL;

/* AFM library lookup path. */
char *afm_path = NULL;

MediaEntry *media_names = NULL;	/* List of known media. */
MediaEntry *media = NULL;	/* Entry for used media. */
int bs = 8;			/* The backspace character. */

/* Statistics. */
int total_pages = 0;		/* Total number of pages printed. */
int num_truncated_lines = 0;	/* Number of lines truncated. */
int num_missing_chars = 0;	/* Number of unknown characters. */
int missing_chars[256] = {0};	/* Table of unknown characters. */
int num_non_printable_chars = 0; /* Number of non-printable characters. */
int non_printable_chars[256] = {0}; /* Table of non-printable characters. */

/* Output media dimensions that are used during PostScript emission. */
int d_page_w = 0;		/* page's width */
int d_page_h = 0;		/* page's height */
int d_header_w = 0;		/* fancy header's width */
int d_header_h = 0;		/* fancy header's height */
int d_footer_h = 0;		/* fancy footer's height */
int d_output_w = 0;		/* output area's width */
int d_output_h = 0;		/* output area's height  */
int d_output_x_margin = 5;	/* output area's x marginal */
int d_output_y_margin = 2;	/* output area's y marginal */

/* Document needed resources. */
StringHashPtr res_fonts;	/* fonts */

/* Fonts to download. */
StringHashPtr download_fonts;

/* Additional key-value pairs, passed to the generated PostScript code. */
StringHashPtr pagedevice;	/* for setpagedevice */
StringHashPtr statusdict;	/* for statusdict */

/* User defined strings. */
StringHashPtr user_strings;

/* Cache for AFM files. */
StringHashPtr afm_cache = NULL;
StringHashPtr afm_info_cache = NULL;

/* AFM library handle. */
AFMHandle afm = NULL;


/* Options. */

/*
 * Free single-letter options are: Q, x, y, Y
 */

/*
 * -#
 *
 * An alias for -n, --copies.
 */

/*
 * -1, -2, -3, -4, -5, -6, -7, -8, -9, --columns=NUM
 *
 * Number of columns per page.  The default is 1 column.
 */
int num_columns = 1;

/*
 * -a PAGES, --pages=PAGES
 *
 * Specify which pages are printed.
 */
PageRange *page_ranges = NULL;

/*
 * -A ALIGN, --file-align=ALIGN
 *
 * Align input files to start from ALIGN page count.  This is handy
 * for two-side printings.
 */
unsigned int file_align = 1;

/*
 * -b STRING, --header=STRING
 *
 * Set the string that is used as the page header.  As a default, page
 * header is constructed from filename, date and page number.
 */
char *page_header = NULL;

/*
 * -B, --no-header
 *
 * Do not print page headers.
 */

/*
 * -c, --truncate-lines
 *
 * Truncate lines that are longer than the page width.  Default is character
 * wrap.
 */
LineEndType line_end = LE_CHAR_WRAP;

/*
 * -C [START], --line-numbers[=START]
 *
 * Precede each line with its line number.  As a default, do not mark
 * line numbers.  If the optional argument START is given, it
 * specifies the number from which the line numbers are assumed to
 * start in the file.  This is useful if the file contains a region
 * of a bigger file.
 */
int line_numbers = 0;
unsigned int start_line_number = 1;

/*
 * -d, -P, --printer
 *
 * Name of the printer to which output is send.  Defaults to system's
 * default printer.
 */
char *printer = NULL;

/*
 * -e [CHAR], --escapes[=CHAR]
 *
 * Enable special escape ('\000') interpretation.  If option CHAR is given
 * it is assumed to specify the escape character.
 */
int special_escapes = 0;
int escape_char = '\0';
int default_escape_char;

/*
 * -E [LANG], --highlight=[LANG] (deprecated --pretty-print[=LANG])
 *
 * Highlight program source code.  Highlighting is handled by creating
 * an input filter with the states-program.  States makes an educated
 * guess about the start state but sometimes it fails, so the start
 * state can also be specified to be LANG.  This option overwrites
 * input filter and enables special escapes.
 */

int highlight = 0;
char *hl_start_state = NULL;

/*
 * -f, --font
 *
 * Select body font.
 */
char *Fname = "Courier";
FontPoint Fpt = {10.0, 10.0};
FontPoint default_Fpt;		/* Point size of the original font. */
char *default_Fname;		/* Name of the original font. */
InputEncoding default_Fencoding; /* The encoding of the original font. */
int user_body_font_defined = 0;	/* Has user defined new body font? */

double font_widths[256];	/* Width array for body font. */
char font_ctype[256];		/* Font character types. */
int font_is_fixed;		/* Is body font a fixed pitch font? */
double font_bbox_lly;		/* Font's bounding box's lly-coordinate. */

/*
 * -F, --header-font
 *
 * Select font to be used to print the standard simple header.
 */
char *HFname = "Courier-Bold";
FontPoint HFpt = {10.0, 10.0};

/*
 * -g, --print-anyway
 *
 * Print document even it contains binary data.  This does nothing
 * since enscript prints files anyway.
 */

/*
 * -G, --fancy-header
 *
 * Add a fancy header to top of every page.  There are several header styles
 * but the default is 'no fancy header'.
 */
HeaderType header = HDR_SIMPLE;
char *fancy_header_name = NULL;
char *fancy_header_default = NULL;

/*
 * -h, --no-job-header
 *
 * Supress the job header page.
 */
static int no_job_header = 0;

/*
 * -H num, --highlight-bars=num
 *
 * Print highlight bars under text.  Bars will be <num> lines high.
 * As a default, do not print bars.
 */
unsigned int highlight_bars = 0;

/*
 * -i, --indent
 *
 * Indent every line this many characters.
 */
double line_indent = 0.0;
char *line_indent_spec = "0";

/*
 * -I CMD, --filter=CMD
 *
 * Read input files through input filter CMD.
 */
char *input_filter = NULL;

/*
 * -j, --borders
 *
 * Print borders around columns.
 */
int borders = 0;

/*
 * -J
 *
 * An alias for -t, --title.
 */

/*
 * -k, --page-prefeed
 * -K, --no-page-prefeed
 *
 * Control page prefeed.
 */
int page_prefeed = 0;

/*
 * -l, --lineprinter
 *
 * Emulate lineprinter -  make pages 66 lines long and omit headers.
 */

/*
 * -L, --lines-per-page
 *
 * Specify how many lines should be printed on a single page.  Normally
 * enscript counts it from font point sizes.
 */
unsigned int lines_per_page = (unsigned int) -1;

/*
 * -m, --mail
 *
 * Send mail notification to user after print job has been completed.
 */
int mail = 0;

/*
 * -M, --media
 *
 * Name of the output media.  Default is A4.
 */
char *media_name = NULL;

/*
 * -n, --copies
 *
 * Number of copies to print.
 */
int num_copies = 1;

/*
 * -N, --newline
 *
 * Set the newline character: '\n' or '\r'.  As a default, the newline
 * character is specified by the input encoding.
 */
int nl = -1;

/*
 * -o, -p, --output
 *
 * Leave output to the specified file.  As a default result is spooled to
 * printer.
 */
char *output_file = OUTPUT_FILE_NONE;

/*
 * -O, --missing-characters
 *
 * List all missing characters.  Default is no listing.
 */
int list_missing_characters = 0;

/*
 * -q, --quiet
 *
 * Do not tell what we are doing.  Default is to tell something but
 * not --verbose.
 */
int quiet = 0;

/*
 * -r, --landscape
 * -R, --portrait
 *
 * Print with page rotated 90 degrees (landscape mode).  Default is
 * portrait.
 */
int landscape = 0;

/*
 * -s, --baselineskip
 *
 * Specify baselineskip value that is used when enscript moves to
 * a new line.  Current point movement is font_point_size + baselineskip.
 */
double baselineskip = 1.0;

/*
 * -t, --title
 *
 * Title which is printed to the banner page.  If this option is given
 * from the command line, this sets also the name of the stdin which
 * is by the default "".
 */
char *title = "Enscript Output";
int title_given = 0;

/*
 * -T, --tabsize
 *
 * Specify tabulator size.
 */
int tabsize = 8;

/*
 * -u, --underlay
 *
 * Place text under every page.  Default is no underlay.
 */
double ul_gray = .8;
FontPoint ul_ptsize = {200.0, 200.0};
char *ul_font = "Times-Roman";
char *underlay = NULL;
char *ul_position = NULL;	/* Position info as a string. */
double ul_x;			/* Position x-coordinate. */
double ul_y;			/* Position y-coordinate. */
double ul_angle;
unsigned int ul_style = UL_STYLE_OUTLINE;
char *ul_style_str = NULL;
int ul_position_p = 0;		/* Is ul-position given? */
int ul_angle_p = 0;		/* Is ul-angle given? */

/*
 * -U NUM, --nup=NUM
 *
 * Print NUM PostScript pages on each output page (n-up printing).
 */
unsigned int nup = 1;
unsigned int nup_exp = 0;
unsigned int nup_rows = 1;
unsigned int nup_columns = 1;
int nup_landscape = 0;
unsigned int nup_width;
unsigned int nup_height;
double nup_scale;

/*
 * -v, --verbose
 *
 * Tell what we are doing.  Default is no verbose outputs.
 */
int verbose = 0;

/*
 * -V, --version
 *
 * Print version information.
 */

/*
 * -w LANGUAGE, --language=LANGUAGE
 *
 * Generate output for language LANGUAGE.  The default is PostScript.
 */
char *output_language = "PostScript";
int output_language_pass_through = 0;

/*
 * -W APP,option, --options=APP,OPTION
 *
 * Pass additional option to enscript's helper applications.  The
 * first part of the option's argument (APP) specifies the
 * helper application to which the options are added.  Currently the
 * following helper application are defined:
 *
 *   s	states
 */
Buffer *helper_options[256] = {0};

/*
 * -X, --encoding
 *
 * Specifies input encoding.  Default is ISO-8859.1.
 */
InputEncoding encoding = ENC_ISO_8859_1;
char *encoding_name = NULL;

/*
 * -z, --no-formfeed
 *
 * Do not interpret form feed characters.  As a default, form feed
 * characters are interpreted.
 */
int interpret_formfeed = 1;

/*
 * -Z, --pass-through
 *
 * Pass through all PostScript and PCL files without any modifications.
 * As a default, don't.
 */
int pass_through = 0;

/*
 * --color[=bool]
 *
 * Create color output with states?
 */

/*
 * --continuous-page-numbers
 *
 * Count page numbers across input files.  Don't restart numbering
 * at beginning of each file.
 */
int continuous_page_numbers = 0;

/*
 * --download-font=FONT
 *
 * Download font FONT to printer.
 */

/*
 * --extended-return-values
 *
 * Enable extended return values.
 */
int extended_return_values = 0;

/*
 * --filter-stdin=STR
 *
 * How stdin is shown to the filter command.  The default is "" but
 * some utilities might want it as "-".
 */
char *input_filter_stdin = "";

/*
 * --footer=STRING
 *
 * Set the string that is used as the page footer.  As a default, the
 * page has no footer.  Setting this option does not necessary show
 * any footer strings in the output.  It depends on the selected
 * header (`.hdr' file) whether it supports footer strings or not.
 */
char *page_footer = NULL;

/*
 * --h-column-height=HEIGHT
 *
 * Set the horizontal column (channel) height to be HEIGHT.  This option
 * also sets the FormFeedType to `hcolumn'.  The default value is set to be
 * big enough to cause a jump to the next vertical column (100m).
 */
double horizontal_column_height = 283465.0;

/*
 * --help-highlight (deprecated --help-pretty-print)
 *
 * Descript all supported -E, --highlight languages and file formats.
 */
int help_highlight = 0;

/*
 * --highlight-bar-gray=val
 *
 * Specify the gray level for highlight bars.
 */
double highlight_bar_gray = .97;

/*
 * --list-media
 *
 * List all known media.  As a default do not list media names.
 */
int list_media = 0;

/*
 * --margins=LEFT:RIGHT:TOP:BOTTOM
 *
 * Adjust page marginals.
 */
char *margins_spec = NULL;

/*
 * --mark-wrapped-lines[=STYLE]
 *
 * Mark wrapped lines so that they can be easily detected from the printout.
 * Optional parameter STYLE specifies the marking style, the system default
 * is black box.
 */
char *mark_wrapped_lines_style_name = NULL;
MarkWrappedLinesStyle mark_wrapped_lines_style = MWLS_NONE;

/*
 * --non-printable-format=FORMAT
 *
 * Format in which non-printable characters are printed.
 */
char *npf_name = NULL;
NonPrintableFormat non_printable_format = NPF_OCTAL;

/*
 * --nup-columnwise
 *
 * Layout N-up pages colunwise instead of row-wise.
 */
int nup_columnwise = 0;

/*
 * --nup-xpad=NUM
 *
 * The x-padding between N-up subpages.
 */
unsigned int nup_xpad = 10;

/*
 * --nup-ypad=NUM
 *
 * The y-padding between N-up subpages.
 */
unsigned int nup_ypad = 10;

/*
 * --page-label-format=FORMAT
 *
 * Format in which page labels are printed; the default is "short".
 */
char *page_label_format = NULL;
PageLabelFormat page_label;

/*
 * --ps-level=LEVEL
 *
 * The PostScript language level that enscript should use; the default is 2.
 */
unsigned int pslevel = 2;

/*
 * --printer-options=OPTIONS
 *
 * Pass extra options OPTIONS to the printer spooler.
 */
char *printer_options = NULL;

/*
 * --rotate-even-pages
 *
 * Rotate each even-numbered page 180 degrees.  This might be handy in
 * two-side printing when the resulting pages are bind from some side.
 * Greetings to Jussi-Pekka Sairanen.
 */
int rotate_even_pages = 0;

/*
 * --slice=NUM
 *
 * Horizontal input slicing.  Print only NUMth wrapped input pages.
 */
int slicing = 0;
unsigned int slice = 1;

/*
 * --swap-even-page-margins
 *
 * Swap left and right side margins for each even numbered page.  This
 * might be handy in two-side printing.
 */
int swap_even_page_margins = 0;

/*
 * --toc
 *
 * Print Table of Contents page.
 */
int toc = 0;
FILE *toc_fp;
char *toc_fmt_string;

/*
 * --word-wrap
 *
 * Wrap long lines from word boundaries.  The default is character wrap.
 */

/*
 * AcceptCompositeCharacters: bool
 *
 * Specify whatever we accept composite characters or should them be
 * considered as non-existent.  As a default, do not accept them.
 */
int accept_composites = 0;

/*
 * AppendCtrlD: bool
 *
 * Append ^D character to the end of the output.  Some printers require this
 * but the default is false.
 */
int append_ctrl_D = 0;

/*
 * Clean7Bit: bool
 *
 * Specify how characters greater than 127 are printed.
 */
int clean_7bit = 1;

/*
 * FormFeedType: type
 *
 * Specify what to do when a formfeed character is encountered from the
 * input stream.  The default action is to jump to the beginning of the
 * next column.
 */
FormFeedType formfeed_type = FORMFEED_COLUMN;

/*
 * GeneratePageSize: bool
 *
 * Specify whether the `PageSize' pagedevice definitions should be
 * generated to the output.
 */
int generate_PageSize = 1;

/*
 * NoJobHeaderSwitch: switch
 *
 * Spooler switch to suppress the job header (-h).
 */
char *no_job_header_switch = NULL;

/*
 * OutputFirstLine: line
 *
 * Set the PostScript output's first line to something your system can handle.
 * The default is "%!PS-Adobe-3.0"
 */
char *output_first_line = NULL;

/*
 * QueueParam: param
 *
 * The spooler command switch to select the printer queue (-P).
 */
char *queue_param = NULL;

/*
 * Spooler: command
 *
 * The spooler command name (lpr).
 */
char *spooler_command = NULL;

/*
 * StatesBinary: path
 *
 * An absolute path to the `states' binary.
 */

char *states_binary = NULL;

/*
 * StatesColor: bool
 *
 * Should the States program generate color outputs.
 */
int states_color = 0;

/*
 * StatesConfigFile: file
 *
 * The name of the states' configuration file.
 */
char *states_config_file = NULL;

/*
 * StatesHighlightStyle: style
 *
 * The highlight style.
 */
char *states_highlight_style = NULL;

/*
 * StatesPath: path
 *
 * Define the path for the states program.  The states program will
 * lookup its state definition files from this path.
 */
char *states_path = NULL;

/* ^@shade{GRAY}, set the line highlight gray. */
double line_highlight_gray = 1.0;

/* ^@bggray{GRAY}, set the text background gray. */
double bggray = 1.0;

EncodingRegistry encodings[] =
{
  {{"88591", "latin1", NULL},		ENC_ISO_8859_1,		'\n', 8},
  {{"88592", "latin2", NULL},		ENC_ISO_8859_2,		'\n', 8},
  {{"88593", "latin3", NULL},		ENC_ISO_8859_3,		'\n', 8},
  {{"88594", "latin4", NULL},		ENC_ISO_8859_4,		'\n', 8},
  {{"88595", "cyrillic", NULL},		ENC_ISO_8859_5,		'\n', 8},
  {{"88597", "greek", NULL},		ENC_ISO_8859_7,		'\n', 8},
  {{"88599", "latin5", NULL},		ENC_ISO_8859_9,		'\n', 8},
  {{"885910", "latin6", NULL},		ENC_ISO_8859_10,	'\n', 8},
  {{"ascii", NULL, NULL},		ENC_ASCII, 		'\n', 8},
  {{"asciifise", "asciifi", "asciise"},	ENC_ASCII_FISE,		'\n', 8},
  {{"asciidkno", "asciidk", "asciino"},	ENC_ASCII_DKNO,		'\n', 8},
  {{"ibmpc", "pc", "dos"},		ENC_IBMPC, 		'\n', 8},
  {{"mac", NULL, NULL},			ENC_MAC, 		'\r', 8},
  {{"vms", NULL, NULL},			ENC_VMS, 		'\n', 8},
  {{"hp8", NULL, NULL},			ENC_HP8,		'\n', 8},
  {{"koi8", NULL, NULL},		ENC_KOI8,		'\n', 8},
  {{"ps", "PS", NULL},			ENC_PS, 		'\n', 8},
  {{"pslatin1", "ISOLatin1Encoding", NULL},	ENC_ISO_8859_1,	'\n', 8},

  {{NULL, NULL, NULL}, 0, 0, 0},
};


/*
 * Static variables.
 */

static struct option long_options[] =
{
  {"columns",			required_argument,	0, 0},
  {"pages",			required_argument,	0, 'a'},
  {"file-align",		required_argument,	0, 'A'},
  {"header",			required_argument,	0, 'b'},
  {"no-header",			no_argument,		0, 'B'},
  {"truncate-lines",		no_argument,		0, 'c'},
  {"line-numbers",		optional_argument,	0, 'C'},
  {"printer",			required_argument,	0, 'd'},
  {"setpagedevice",		required_argument,	0, 'D'},
  {"escapes",			optional_argument,	0, 'e'},
  {"highlight",			optional_argument,	0, 'E'},
  {"font",			required_argument,	0, 'f'},
  {"header-font",		required_argument,	0, 'F'},
  {"print-anyway",		no_argument,		0, 'g'},
  {"fancy-header",		optional_argument,	0, 'G'},
  {"no-job-header",		no_argument, 		0, 'h'},
  {"highlight-bars",		optional_argument,	0, 'H'},
  {"indent",			required_argument,	0, 'i'},
  {"filter",			required_argument,	0, 'I'},
  {"borders",			no_argument,		0, 'j'},
  {"page-prefeed",		no_argument,		0, 'k'},
  {"no-page-prefeed",		no_argument,		0, 'K'},
  {"lineprinter",		no_argument,		0, 'l'},
  {"lines-per-page",		required_argument,	0, 'L'},
  {"mail",			no_argument,		0, 'm'},
  {"media",			required_argument,	0, 'M'},
  {"copies",			required_argument,	0, 'n'},
  {"newline",			required_argument,	0, 'N'},
  {"output",			required_argument,	0, 'p'},
  {"missing-characters",	no_argument,		0, 'O'},
  {"quiet",			no_argument,		0, 'q'},
  {"silent",			no_argument,		0, 'q'},
  {"landscape",			no_argument,		0, 'r'},
  {"portrait",			no_argument,		0, 'R'},
  {"baselineskip",		required_argument,	0, 's'},
  {"statusdict",		required_argument,	0, 'S'},
  {"title",			required_argument,	0, 't'},
  {"tabsize",			required_argument,	0, 'T'},
  {"underlay",			optional_argument,	0, 'u'},
  {"nup",			required_argument,	0, 'U'},
  {"verbose",			optional_argument,	0, 'v'},
  {"version",			no_argument,		0, 'V'},
  {"language",			required_argument,	0, 'w'},
  {"option",			required_argument,	0, 'W'},
  {"encoding",			required_argument,	0, 'X'},
  {"no-formfeed",		no_argument,		0, 'z'},
  {"pass-through",		no_argument,		0, 'Z'},

  /* Long options without short counterparts.  Next free is 157. */
  {"color",			optional_argument,	0, 142},
  {"continuous-page-numbers",	no_argument,		0, 156},
  {"download-font",		required_argument,	0, 131},
  {"extended-return-values",	no_argument,		0, 154},
  {"filter-stdin",		required_argument,	0, 138},
  {"footer",			required_argument,	0, 155},
  {"h-column-height", 		required_argument,	0, 148},
  {"help", 			no_argument, 		0, 135},
  {"help-highlight",	 	no_argument, 		0, 141},
  {"highlight-bar-gray",	required_argument, 	0, 136},
  {"list-media",		no_argument,		&list_media, 1},
  {"margins",			required_argument,	0, 144},
  {"mark-wrapped-lines",	optional_argument,	0, 143},
  {"non-printable-format",	required_argument,	0, 134},
  {"nup-columnwise",		no_argument,		0, 152},
  {"nup-xpad",			required_argument,	0, 145},
  {"nup-ypad",			required_argument,	0, 146},
  {"page-label-format",		required_argument,	0, 130},
  {"ps-level",			required_argument,	0, 149},
  {"printer-options",		required_argument,	0, 139},
  {"rotate-even-pages",		no_argument,		0, 150},
  {"slice",			required_argument,	0, 140},
  {"style",			required_argument,	0, 151},
  {"swap-even-page-margins",	no_argument,		0, 153},
  {"toc",			no_argument,		&toc, 1},
  {"word-wrap",			no_argument,		0, 147},
  {"ul-angle",			required_argument,	0, 132},
  {"ul-font",			required_argument,	0, 128},
  {"ul-gray",			required_argument,	0, 129},
  {"ul-position",		required_argument,	0, 133},
  {"ul-style",			required_argument,	0, 137},

  /* Backwards compatiblity options. */
  {"pretty-print",		optional_argument,	0, 'E'},
  {"help-pretty-print", 	no_argument, 		0, 141},

  {NULL, 0, 0, 0},
};


/*
 * Global functions.
 */

int
main (int argc, char *argv[])
{
  InputStream is;
  time_t tim;
  struct tm *tm;
  int i, j, found;
  unsigned int ui;
  MediaEntry *mentry;
  AFMError afm_error;
  char *cp, *cp2;
  int retval = 0;
  Buffer buffer;

  /* Init our dynamic memory buffer. */
  buffer_init (&buffer);

  /* Get program's name. */
  program = strrchr (argv[0], '/');
  if (program == NULL)
    program = argv[0];
  else
    program++;

  /* Make getopt_long() to use our modified programname. */
  argv[0] = program;

  /* Create the default TOC format string.  Wow, this is cool! */
  /* xgettext:no-c-format */
  toc_fmt_string = _("$3v $-40N $3% pages $4L lines  $E $C");

  /* Internationalization. */
#if HAVE_SETLOCALE
  /*
   * We want to change only messages (gs do not like decimals in 0,1
   * format ;)
   */
#if HAVE_LC_MESSAGES
  setlocale (LC_MESSAGES, "");
#endif
#endif
#if ENABLE_NLS
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
#endif

  /* Create date string. */

  tim = time (NULL);
  tm = localtime (&tim);
  memcpy (&run_tm, tm, sizeof (*tm));

  date_string = xstrdup (asctime (&run_tm));
  i = strlen (date_string);
  date_string[i - 1] = '\0';

  /* Get user's passwd entry. */
  passwd = getpwuid (getuid ());
  if (passwd == NULL)
    FATAL ((stderr, _("couldn't get passwd entry for uid=%d: %s"), getuid (),
	    strerror (errno)));

  /* Defaults for some options. */
  media_name 		= xstrdup ("A4");
  encoding_name		= xstrdup ("88591");
  npf_name		= xstrdup ("octal");
  page_label_format	= xstrdup ("short");
  ul_style_str		= xstrdup ("outline");
  ul_position		= xstrdup ("+0-0");
  spooler_command 	= xstrdup ("lpr");
  queue_param 		= xstrdup ("-P");
  no_job_header_switch 	= xstrdup ("-h");
  fancy_header_default 	= xstrdup ("enscript");
  output_first_line 	= xstrdup ("%!PS-Adobe-3.0");

  /* Check ENSCRIPT_LIBRARY for custom library location. */
  cp = getenv ("ENSCRIPT_LIBRARY");
  if (cp)
    enscript_library = cp;

  /* Fill up build-in libpath. */

  cp = getenv ("HOME");
  if (cp == NULL)
    cp = passwd->pw_dir;

  buffer_clear (&buffer);
  buffer_append (&buffer, enscript_library);
  buffer_append (&buffer, PATH_SEPARATOR_STR);
  buffer_append (&buffer, cp);
  buffer_append (&buffer, "/.enscript");
  libpath = buffer_copy (&buffer);

  /* Defaults for the states filter. */

  states_binary = xstrdup ("states"); /* Take it from the user path. */

  buffer_clear (&buffer);
  buffer_append (&buffer, enscript_library);
  buffer_append (&buffer, "/hl/enscript.st");
  states_config_file = buffer_copy (&buffer);

  states_highlight_style = xstrdup ("emacs");

  /* The <cp> holds the user's home directory. */
  buffer_clear (&buffer);
  buffer_append (&buffer, cp);
  buffer_append (&buffer, "/.enscript");
  buffer_append (&buffer, PATH_SEPARATOR_STR);
  buffer_append (&buffer, enscript_library);
  buffer_append (&buffer, "/hl");
  states_path = buffer_copy (&buffer);

  /* Initialize resource sets. */
  res_fonts = strhash_init ();
  download_fonts = strhash_init ();
  pagedevice = strhash_init ();
  statusdict = strhash_init ();
  user_strings = strhash_init ();


  /*
   * Read configuration files.
   */

  /* Global config. */
#define CFG_FILE_NAME "enscript.cfg"
  if (!read_config (SYSCONFDIR, CFG_FILE_NAME))
    {
      int saved_errno = errno;

      /* Try to read it from our library directory.  This is mostly
	 the case for the micro ports.  */
      if (!read_config (enscript_library, CFG_FILE_NAME))
	{
	  /* Try `enscript_library/../../etc/'.  This is the case for
             installations which set the prefix after the compilation
             and our SYSCONFDIR points to wrong directory. */

	  buffer_clear (&buffer);
	  buffer_append (&buffer, enscript_library);
	  buffer_append (&buffer, "/../../etc");

	  if (!read_config (buffer_ptr (&buffer), CFG_FILE_NAME))
	    {
	      /* Maybe we are not installed yet, let's try `../lib'
                 and `../../lib'. */
	      if (!read_config ("../lib", CFG_FILE_NAME)
		  && !read_config ("../../lib", CFG_FILE_NAME))
		{
		  /* No luck, report error from the original config file. */
		  ERROR ((stderr, _("couldn't read config file \"%s/%s\": %s"),
			  enscript_library, CFG_FILE_NAME,
			  strerror (saved_errno)));
		  ERROR ((stderr,
			  _("I did also try the following directories:")));
		  ERROR ((stderr, _("\t%s"), SYSCONFDIR));
		  ERROR ((stderr, _("\t%s"), enscript_library));
		  ERROR ((stderr, _("\t%s"), buffer_ptr (&buffer)));
		  ERROR ((stderr, _("\t../lib")));
		  ERROR ((stderr, _("\t../../lib")));
		  ERROR ((stderr,
_("This is probably an installation error.  Please, try to rebuild:")));
		  ERROR ((stderr, _("\tmake distclean")));
		  ERROR ((stderr, _("\t./configure --prefix=PREFIX")));
		  ERROR ((stderr, _("\tmake")));
		  ERROR ((stderr, _("\tmake check")));
		  ERROR ((stderr, _("\tmake install")));
		  ERROR ((stderr, _("or set the environment variable `ENSCRIPT_LIBRARY'"
			" to point to your library directory.")));
		  exit (1);
		}

	      /* Ok, we are not installed yet.  Here is a small kludge
		 to conform the GNU coding standards: we must be able
		 to run without being installed, so we must append the
		 `../lib' and `../../lib' directories to the libpath.
		 The later allows us to be run form the `src/tests'
		 directory.  */
	      buffer_clear (&buffer);
	      buffer_append (&buffer, libpath);
	      buffer_append (&buffer, PATH_SEPARATOR_STR);
	      buffer_append (&buffer, "../lib");
	      buffer_append (&buffer, PATH_SEPARATOR_STR);
	      buffer_append (&buffer, "../../lib");

	      xfree (libpath);
	      libpath = buffer_copy (&buffer);
	    }
	}
    }

  /* Site config. */
  read_config (SYSCONFDIR, "enscriptsite.cfg");

  /* Personal config. */
  read_config (cp, ".enscriptrc");

  /*
   * Options.
   */

  /* Environment variables. */
  handle_env_options ("ENSCRIPT");
  handle_env_options ("GENSCRIPT");

  /* Command line arguments. */
  handle_options (argc, argv);

  /*
   * Check options which have some validity conditions.
   */

  /*
   * Save the user-specified escape char so ^@escape{default} knows
   * what to set.
   */
  default_escape_char = escape_char;

  /* Input encoding. */

  found = 0;
  for (i = 0; !found && encodings[i].names[0]; i++)
    for (j = 0; j < 3; j++)
      if (encodings[i].names[j] != NULL && MATCH (encodings[i].names[j],
						  encoding_name))
	{
	  /* Found a match for this encoding.  Use the first
             "official" name. */

	  encoding = encodings[i].encoding;
	  xfree (encoding_name);
	  encoding_name = xstrdup (encodings[i].names[0]);

	  if (nl < 0)
	    nl = encodings[i].nl;
	  bs = encodings[i].bs;
	  found = 1;
	  break;
	}
  if (!found)
    FATAL ((stderr, _("unknown encoding: %s"), encoding_name));

  /* Fonts. */

  /* Default font for landscape, 2 column printing is Courier 7. */
  if (!user_body_font_defined && landscape && num_columns > 1)
    Fpt.w = Fpt.h = 7.0;

  /* Cache for font AFM information. */
  afm_cache = strhash_init ();
  afm_info_cache = strhash_init ();

  /* Open AFM library. */
  afm_error = afm_create (afm_path, verbose, &afm);
  if (afm_error != AFM_SUCCESS)
    {
      char buf[256];

      afm_error_to_string (afm_error, buf);
      FATAL ((stderr, _("couldn't open AFM library: %s"), buf));
    }

  /*
   * Save default Fpt and Fname since special escape 'font' can change
   * it and later we might want to switch back to the "default" font.
   */
  default_Fpt.w = Fpt.w;
  default_Fpt.h = Fpt.h;
  default_Fname = Fname;
  default_Fencoding = encoding;

  /* Register that document uses at least these fonts. */
  strhash_put (res_fonts, Fname, strlen (Fname) + 1, NULL, NULL);
  strhash_put (res_fonts, HFname, strlen (HFname) + 1, NULL, NULL);

  /* As a default, download both named fonts. */
  strhash_put (download_fonts, Fname, strlen (Fname) + 1, NULL, NULL);
  strhash_put (download_fonts, HFname, strlen (HFname) + 1, NULL, NULL);

  /* Read font's character widths and character types. */
  read_font_info ();

  /* Count the line indentation. */
  line_indent = parse_float (line_indent_spec, 1, 1);

  /* List media names. */
  if (list_media)
    {
      printf (_("known media:\n\
name             width\theight\tllx\tlly\turx\tury\n\
------------------------------------------------------------\n"));
      for (mentry = media_names; mentry; mentry = mentry->next)
	printf ("%-16s %d\t%d\t%d\t%d\t%d\t%d\n",
		mentry->name, mentry->w, mentry->h,
		mentry->llx, mentry->lly, mentry->urx, mentry->ury);
      /* Exit after listing. */
      exit (0);
    }

  /* Output media. */
  for (mentry = media_names; mentry; mentry = mentry->next)
    if (strcmp (media_name, mentry->name) == 0)
      {
	media = mentry;
	break;
      }
  if (media == NULL)
    FATAL ((stderr, _("do not know anything about media \"%s\""), media_name));

  if (margins_spec)
    {
      /* Adjust marginals. */
      for (i = 0; i < 4; i++)
	{
	  if (*margins_spec == '\0')
	    /* All done. */
	    break;

	  if (*margins_spec == ':')
	    {
	      margins_spec++;
	      continue;
	    }

	  j = atoi (margins_spec);
	  for (; *margins_spec != ':' && *margins_spec != '\0'; margins_spec++)
	    ;
	  if (*margins_spec == ':')
	    margins_spec++;

	  switch (i)
	    {
	    case 0:		/* left */
	      media->llx = j;
	      break;

	    case 1:		/* right */
	      media->urx = media->w - j;
	      break;

	    case 2:		/* top */
	      media->ury = media->h - j;
	      break;

	    case 3:		/* bottom */
	      media->lly = j;
	      break;
	    }
	}
      MESSAGE (1,
	       (stderr,
		_("set new marginals for media `%s' (%dx%d): llx=%d, lly=%d, urx=%d, ury=%d\n"),
		media->name, media->w, media->h, media->llx, media->lly,
		media->urx, media->ury));
    }

  /* Page label format. */
  if (MATCH (page_label_format, "short"))
    page_label = LABEL_SHORT;
  else if (MATCH (page_label_format, "long"))
    page_label = LABEL_LONG;
  else
    FATAL ((stderr, _("illegal page label format \"%s\""), page_label_format));

  /* Non-printable format. */
  if (MATCH (npf_name, "space"))
    non_printable_format = NPF_SPACE;
  else if (MATCH (npf_name, "questionmark"))
    non_printable_format = NPF_QUESTIONMARK;
  else if (MATCH (npf_name, "caret"))
    non_printable_format = NPF_CARET;
  else if (MATCH (npf_name, "octal"))
    non_printable_format = NPF_OCTAL;
  else
    FATAL ((stderr, _("illegal non-printable format \"%s\""), npf_name));

  /* Mark wrapped lines style. */
  if (mark_wrapped_lines_style_name)
    {
      if (MATCH (mark_wrapped_lines_style_name, "none"))
	mark_wrapped_lines_style = MWLS_NONE;
      else if (MATCH (mark_wrapped_lines_style_name, "plus"))
	mark_wrapped_lines_style = MWLS_PLUS;
      else if (MATCH (mark_wrapped_lines_style_name, "box"))
	mark_wrapped_lines_style = MWLS_BOX;
      else if (MATCH (mark_wrapped_lines_style_name, "arrow"))
	mark_wrapped_lines_style = MWLS_ARROW;
      else
	FATAL ((stderr, _("illegal style for wrapped line marker: \"%s\""),
		mark_wrapped_lines_style_name));
    }

  /* Count N-up stuffs. */
  for (i = 0; ; i++)
    {
      ui = nup >> i;

      if (ui == 0)
	FATAL ((stderr, _("illegal N-up argument: %d"), nup));

      if (ui & 0x1)
	{
	  if (ui != 1)
	    FATAL ((stderr, _("N-up argument must be power of 2: %d"), nup));

	  nup_exp = i;
	  break;
	}
    }

  nup_rows = nup_exp / 2 * 2;
  if (nup_rows == 0)
    nup_rows = 1;
  nup_columns = (nup_exp + 1) / 2 * 2;
  if (nup_columns == 0)
    nup_columns = 1;

  nup_landscape = nup_exp & 0x1;


  /*
   * Count output media dimensions.
   */

  if (landscape)
    {
      d_page_w = media->ury - media->lly;
      d_page_h = media->urx - media->llx;
    }
  else
    {
      d_page_w = media->urx - media->llx;
      d_page_h = media->ury - media->lly;
    }

  /*
   * Count N-up page width, height and scale.
   */

  if (nup_landscape)
    {
      nup_width = media->ury - media->lly;
      nup_height = media->urx - media->llx;
    }
  else
    {
      nup_width = media->urx - media->llx;
      nup_height = media->ury - media->lly;
    }

  {
    double w, h;

    w = ((double) nup_width - (nup_columns - 1) * nup_xpad) / nup_columns;
    h = ((double) nup_height - (nup_rows - 1) * nup_ypad) / nup_rows;

    nup_width = w;
    nup_height = h;

    w = w / (media->urx - media->llx);
    h = h / (media->ury - media->lly);

    nup_scale = w < h ? w : h;
  }

  /*
   * Underlay (this must come after output media dimensions, because
   * `underlay position' needs them).
   */
  if (underlay != NULL)
    {
      strhash_put (res_fonts, ul_font, strlen (ul_font) + 1, NULL, NULL);
      underlay = escape_string (underlay);
    }

  /* Underlay X-coordinate. */
  ul_x = strtod (ul_position, &cp);
  if (cp == ul_position)
    {
    malformed_position:
      FATAL ((stderr, _("malformed underlay position: %s"), ul_position));
    }
  if (ul_position[0] == '-')
    ul_x += d_page_w;

  /* Underlay Y-coordinate. */
  ul_y = strtod (cp, &cp2);
  if (cp2 == cp)
    goto malformed_position;
  if (cp[0] == '-')
    ul_y += d_page_h;

  /* Underlay Angle. */
  if (!ul_angle_p)
    /* No angle given, count the default. */
    ul_angle = (atan2 (-d_page_h, d_page_w) / 3.14159265 * 180);

  /* Underlay style. */
  if (strcmp (ul_style_str, "outline") == 0)
    ul_style = UL_STYLE_OUTLINE;
  else if (strcmp (ul_style_str, "filled") == 0)
    ul_style = UL_STYLE_FILLED;
  else
    FATAL ((stderr, _("illegal underlay style: %s"), ul_style_str));

  /*
   * Header.  Note! The header attributes can be changed from
   * the `.hdr' files, these are only the defaults.
   */

  d_header_w = d_page_w;
  switch (header)
    {
    case HDR_NONE:
      d_header_h = 0;
      break;

    case HDR_SIMPLE:
      d_header_h = HFpt.h * 1.5;
      break;

    case HDR_FANCY:
      d_header_h = 36;
      break;
    }

  /* Help highlight. */
  if (help_highlight)
    {
      /* Create description with states. */
      printf (_("Highlighting is supported for the following languages and file formats:\n\n"));
      fflush (stdout);

      buffer_clear (&buffer);
      buffer_append (&buffer, states_binary);
      buffer_append (&buffer, " -f \"");
      buffer_append (&buffer, states_config_file);
      buffer_append (&buffer, "\" -p \"");
      buffer_append (&buffer, states_path);
      buffer_append (&buffer, "\" -s describe_languages ");
      buffer_append (&buffer, enscript_library);
      buffer_append (&buffer, "/hl/*.st");

      system (buffer_ptr (&buffer));
      exit (0);
    }

  /*
   * And now to the main business.  The actual input file processing
   * is divided to two parts: PostScript generation and everything else.
   * The PostScript generation is handled in the conventional way, we
   * process the input and generate PostScript.  However all other input
   * languages will be handled with States, we only pass enscript's
   * options to the states pre-filter and dump output.
   */
  if (output_language_pass_through)
    {
      char *start_state;
      Buffer cmd;
      char intbuf[256];

      /* The States output generation. */

      /* Resolve the start state. */
      if (hl_start_state)
	start_state = hl_start_state;
      else if (highlight)
	start_state = NULL;
      else
	start_state = "passthrough";

      /* Create the states command. */

      buffer_init (&cmd);

      buffer_append (&cmd, states_binary);
      buffer_append (&cmd, " -f \"");
      buffer_append (&cmd, states_config_file);
      buffer_append (&cmd, "\" -p \"");
      buffer_append (&cmd, states_path);
      buffer_append (&cmd, "\" ");

      if (verbose > 0)
	buffer_append (&cmd, "-v ");

      if (start_state)
	{
	  buffer_append (&cmd, "-s");
	  buffer_append (&cmd, start_state);
	  buffer_append (&cmd, " ");
	}

      buffer_append (&cmd, "-Dcolor=");
      buffer_append (&cmd, states_color ? "1" : "0");
      buffer_append (&cmd, " ");

      buffer_append (&cmd, "-Dstyle=");
      buffer_append (&cmd, states_highlight_style);
      buffer_append (&cmd, " ");

      buffer_append (&cmd, "-Dlanguage=");
      buffer_append (&cmd, output_language);
      buffer_append (&cmd, " ");

      buffer_append (&cmd, "-Dnum_input_files=");
      sprintf (intbuf, "%d", optind == argc ? 1 : argc - optind);
      buffer_append (&cmd, intbuf);
      buffer_append (&cmd, " ");

      buffer_append (&cmd, "-Ddocument_title=\'");
      if ((cp = shell_escape (title)) != NULL)
	{
	  buffer_append (&cmd, cp);
	  free (cp);
	}
      buffer_append (&cmd, "\' ");

      buffer_append (&cmd, "-Dtoc=");
      buffer_append (&cmd, toc ? "1" : "0");

      /* Additional options for states? */
      if (helper_options['s'])
	{
	  Buffer *opts = helper_options['s'];

	  buffer_append (&cmd, " ");
	  buffer_append_len (&cmd, buffer_ptr (opts), buffer_len (opts));
	}

      /* Append input files. */
      for (i = optind; i < argc; i++)
	{
	  char *cp;
	  if ((cp = shell_escape (argv[i])) != NULL)
	    {
	      buffer_append (&cmd, " \'");
	      buffer_append (&cmd, cp);
	      buffer_append (&cmd, "\'");
	      free (cp);
	    }
	}

      /* And do the job. */
      if (is_open (&is, stdin, NULL, buffer_ptr (&cmd)))
	{
	  open_output_file ();
	  process_file ("unused", &is, 0);
	  is_close (&is);
	}

      buffer_uninit (&cmd);
    }
  else
    {
      /* The conventional way. */

      /* Highlighting. */
      if (highlight)
	{
	  char fbuf[256];

	  /* Create a highlight input filter. */
	  buffer_clear (&buffer);
	  buffer_append (&buffer, states_binary);
	  buffer_append (&buffer, " -f \"");
	  buffer_append (&buffer, states_config_file);
	  buffer_append (&buffer, "\" -p \"");
	  buffer_append (&buffer, states_path);
	  buffer_append (&buffer, "\"");

	  if (verbose > 0)
	    buffer_append (&buffer, " -v");

	  if (hl_start_state)
	    {
	      buffer_append (&buffer, " -s ");
	      buffer_append (&buffer, hl_start_state);
	    }

	  buffer_append (&buffer, " -Dcolor=");
	  buffer_append (&buffer, states_color ? "1" : "0");

	  buffer_append (&buffer, " -Dstyle=");
	  buffer_append (&buffer, states_highlight_style);

	  buffer_append (&buffer, " -Dfont_spec=");
	  buffer_append (&buffer, Fname);
	  sprintf (fbuf, "@%g/%g", Fpt.w, Fpt.h);
	  buffer_append (&buffer, fbuf);

	  /* Additional options for states? */
	  if (helper_options['s'])
	    {
	      Buffer *opts = helper_options['s'];

	      buffer_append (&buffer, " ");
	      buffer_append_len (&buffer,
				 buffer_ptr (opts), buffer_len (opts));
	    }

	  buffer_append (&buffer, " \'%s\'");

	  input_filter = buffer_copy (&buffer);
	  input_filter_stdin = "-";
	}

      /* Table of Contents. */
      if (toc)
	{
	  toc_fp = tmpfile ();
	  if (toc_fp == NULL)
	    FATAL ((stderr, _("couldn't create temporary toc file: %s"),
		    strerror (errno)));
	}


      /*
       * Process files.
       */

      if (optind == argc)
	{
	  /* stdin's modification time is the current time. */
	  memcpy (&mod_tm, &run_tm, sizeof (run_tm));

	  if (is_open (&is, stdin, NULL, input_filter))
	    {
	      /* Open output file. */
	      open_output_file ();
	      process_file (title_given ? title : "", &is, 0);
	      is_close (&is);
	    }
	}
      else
	{
	  for (; optind < argc; optind++)
	    {
	      if (is_open (&is, NULL, argv[optind], input_filter))
		{
		  struct stat stat_st;

		  /* Get modification time. */
		  if (stat (argv[optind], &stat_st) == 0)
		    {
		      tim = stat_st.st_mtime;
		      tm = localtime (&tim);
		      memcpy (&mod_tm, tm, sizeof (*tm));

		      /*
		       * Open output file.  Output file opening is delayed to
		       * this point so we can optimize the case when a
		       * non-existing input file is printed => we do nothing.
		       */
		      open_output_file ();

		      process_file (argv[optind], &is, 0);
		    }
		  else
		    ERROR ((stderr, _("couldn't stat input file \"%s\": %s"),
			    argv[optind],
			    strerror (errno)));

		  is_close (&is);
		}
	    }
	}

      /* Table of Contents. */
      if (toc)
	{
	  /* This is really cool... */

	  /* Set the printing options for toc. */
	  toc = 0;
	  special_escapes = 1;
	  line_numbers = 0;

	  if (fseek (toc_fp, 0, SEEK_SET) != 0)
	    FATAL ((stderr, _("couldn't rewind toc file: %s"),
		    strerror (errno)));

	  memcpy (&mod_tm, &run_tm, sizeof (run_tm));
	  if (is_open (&is, toc_fp, NULL, NULL))
	    {
	      process_file (_("Table of Contents"), &is, 1);
	      is_close (&is);
	    }
	}

      /* Give trailer a chance to dump itself. */
      dump_ps_trailer ();

      /*
       * Append ^D to the end of the output?  Note! It must be ^D followed
       * by a newline.
       */
      if (ofp != NULL && append_ctrl_D)
	fprintf (ofp, "\004\n");
    }

  /* Close output file. */
  close_output_file ();

  /* Tell how things went. */
  if (ofp == NULL)
    {
      /*
       * The value of <ofp> is not reset in close_output_file(),
       * this is ugly but it saves one flag.
       */
      MESSAGE (0, (stderr, _("no output generated\n")));
    }
  else if (output_language_pass_through)
    {
      if (output_file == OUTPUT_FILE_NONE)
	MESSAGE (0, (stderr, _("output sent to %s\n"),
		     printer ? printer : _("printer")));
      else
	MESSAGE (0, (stderr, _("output left in %s\n"),
		     output_file == OUTPUT_FILE_STDOUT ? "-" : output_file));
    }
  else
    {
      unsigned int real_total_pages;

      if (nup > 1)
	{
	  if (total_pages > 0)
	    real_total_pages = (total_pages - 1) / nup + 1;
	  else
	    real_total_pages = 0;
	}
      else
	real_total_pages = total_pages;

      /* We did something, tell what.  */
      char message[80];
      snprintf(message, sizeof message, "%s%s%s%s%s",
	       "[ ",
	       ngettext("%d page", "%d pages", real_total_pages),
	       " * ",
	       ngettext("%d copy", "%d copies", num_copies),
	       " ]");
      MESSAGE (0, (stderr, message, real_total_pages, num_copies));

      if (output_file == OUTPUT_FILE_NONE)
	MESSAGE (0, (stderr, _(" sent to %s\n"),
		     printer ? printer : _("printer")));
      else
	MESSAGE (0, (stderr, _(" left in %s\n"),
		     output_file == OUTPUT_FILE_STDOUT ? "-" : output_file));
      if (num_truncated_lines)
	{
	  retval |= 2;
	  MESSAGE (0, (stderr,
		       ngettext("%d line was %s\n",
				"%d lines were %s\n",
				num_truncated_lines),
		       num_truncated_lines,
		       line_end == LE_TRUNCATE
		       ? _("truncated") : _("wrapped")));
	}

      if (num_missing_chars)
	{
	  retval |= 4;
	  MESSAGE (0, (stderr,
		       ngettext("%d character was missing\n",
				"%d characters were missing\n",
				num_missing_chars),
		       num_missing_chars));
	  if (list_missing_characters)
	    {
	      MESSAGE (0, (stderr, _("missing character codes (decimal):\n")));
	      do_list_missing_characters (missing_chars);
	    }
	}

      if (num_non_printable_chars)
	{
	  retval |= 8;
	  MESSAGE (0, (stderr,
		       ngettext("%d non-printable character\n",
				"%d non-printable characters\n",
				num_non_printable_chars),
		       num_non_printable_chars));
	  if (list_missing_characters)
	    {
	      MESSAGE (0, (stderr,
			   _("non-printable character codes (decimal):\n")));
	      do_list_missing_characters (non_printable_chars);
	    }
	}
    }

  /* Uninit our dynamic memory buffer. */
  buffer_uninit (&buffer);

  /* Return the extended return values only if requested. */
  if (!extended_return_values)
    retval = 0;

  /* This is the end. */
  return retval;
}


/*
 * Static functions.
 */

static void
open_output_file ()
{
  if (ofp)
    /* Output file has already been opened, do nothing. */
    return;

  if (output_file == OUTPUT_FILE_NONE)
    {
      char spooler_options[512];

      /* Format spooler options. */
      spooler_options[0] = '\0';
      if (mail)
	strcat (spooler_options, "-m ");
      if (no_job_header)
	{
	  strcat (spooler_options, no_job_header_switch);
	  strcat (spooler_options, " ");
	}
      if (printer_options)
	strcat (spooler_options, printer_options);

      /* Open printer. */
      ofp = printer_open (spooler_command, spooler_options, queue_param,
			  printer, &printer_context);
      if (ofp == NULL)
	FATAL ((stderr, _("couldn't open printer `%s': %s"), printer,
		strerror (errno)));
    }
  else if (output_file == OUTPUT_FILE_STDOUT)
    ofp = stdout;
  else
    {
      ofp = fopen (output_file, "w");
      if (ofp == NULL)
	FATAL ((stderr, _("couldn't create output file \"%s\": %s"),
		output_file, strerror (errno)));
    }
}


static void
close_output_file ()
{
  if (ofp == NULL)
    /* Output file hasn't been opened, we are done. */
    return;

  if (output_file == OUTPUT_FILE_NONE)
    printer_close (printer_context);
  else if (output_file != OUTPUT_FILE_STDOUT)
    if (fclose (ofp))
      FATAL ((stderr, _("couldn't close output file \"%s\": %s"),
	      output_file, strerror (errno)));

  /* We do not reset <ofp> since its value is needed in diagnostigs. */
}


static void
handle_env_options (char *var)
{
  int argc;
  char **argv;
  char *string;
  char *str;
  int i;

  string = getenv (var);
  if (string == NULL)
    return;

  MESSAGE (2, (stderr, "handle_env_options(): %s=\"%s\"\n", var, string));

  /* Copy string so we can modify it in place. */
  str = xstrdup (string);

  /*
   * We can count this, each option takes at least 1 character and one
   * space.  We also need one for program's name and one for the
   * trailing NULL.
   */
  argc = (strlen (str) + 1) / 2 + 2;
  argv = xcalloc (argc, sizeof (char *));

  /* Set program name. */
  argc = 0;
  argv[argc++] = program;

  /* Split string and set arguments to argv array. */
  i = 0;
  while (str[i])
    {
      /* Skip leading whitespace. */
      for (; str[i] && isspace (str[i]); i++)
	;
      if (!str[i])
	break;

      /* Check for quoted arguments. */
      if (str[i] == '"' || str[i] == '\'')
	{
	  int endch = str[i++];

	  argv[argc++] = str + i;

	  /* Skip until we found the end of the quotation. */
	  for (; str[i] && str[i] != endch; i++)
	    ;
	  if (!str[i])
	    FATAL ((stderr, _("syntax error in option string %s=\"%s\":\n\
missing end of quotation: %c"), var, string, endch));

	  str[i++] = '\0';
	}
      else
	{
	  argv[argc++] = str + i;

	  /* Skip until whitespace if found. */
	  for (; str[i] && !isspace (str[i]); i++)
	    ;
	  if (str[i])
	    str[i++] = '\0';
	}
    }

  /* argv[argc] must be NULL. */
  argv[argc] = NULL;

  MESSAGE (2, (stderr, "found following options (argc=%d):\n", argc));
  for (i = 0; i < argc; i++)
    MESSAGE (2, (stderr, "%3d = \"%s\"\n", i, argv[i]));

  /* Process options. */
  handle_options (argc, argv);

  /* Check that all got processed. */
  if (optind != argc)
    {
      MESSAGE (0,
	       (stderr,
		_("warning: didn't process following options from \
environment variable %s:\n"),
		var));
      for (; optind < argc; optind++)
	MESSAGE (0, (stderr, _("  option %d = \"%s\"\n"), optind,
		     argv[optind]));
    }

  /* Cleanup. */
  xfree (argv);

  /*
   * <str> must not be freed, since some global variables can point to
   * its elements
   */
}


static void
handle_options (int argc, char *argv[])
{
  int c;
  PageRange *prange;

  /* Reset optind. */
  optind = 0;

  while (1)
    {
      int option_index = 0;
      const char *cp;
      int i;

      c = getopt_long (argc, argv,
		       "#: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:W:X:zZ",
		       long_options, &option_index);

      if (c == -1)
	break;

      switch (c)
	{
	case 0:			/* Long option found. */
	  cp = long_options[option_index].name;

	  if (strcmp (cp, "columns") == 0)
	    {
	      num_columns = atoi (optarg);
	      if (num_columns < 1)
		FATAL ((stderr,
			_("number of columns must be larger than zero")));
	    }
	  break;

	  /* Short options. */

	case '1':		/* 1 column */
	case '2':		/* 2 columns */
	case '3':		/* 3 columns */
	case '4':		/* 4 columns */
	case '5':		/* 5 columns */
	case '6':		/* 6 columns */
	case '7':		/* 7 columns */
	case '8':		/* 8 columns */
	case '9':		/* 9 columns */
	  num_columns = c - '0';
	  break;

	case 'a':		/* pages */
	  prange = (PageRange *) xcalloc (1, sizeof (PageRange));

	  if (strcmp (optarg, "odd") == 0)
	    prange->odd = 1;
	  else if (strcmp (optarg, "even") == 0)
	    prange->even = 1;
	  else
	    {
	      cp = strchr (optarg, '-');
	      if (cp)
		{
		  if (optarg[0] == '-')
		    /* -end */
		    prange->end = atoi (optarg + 1);
		  else if (cp[1] == '\0')
		    {
		      /* begin- */
		      prange->start = atoi (optarg);
		      prange->end = (unsigned int) -1;
		    }
		  else
		    {
		      /* begin-end */
		      prange->start = atoi (optarg);
		      prange->end = atoi (cp + 1);
		    }
		}
	      else
		/* pagenumber */
		prange->start = prange->end = atoi (optarg);
	    }

	  prange->next = page_ranges;
	  page_ranges = prange;
	  break;

	case 'A':		/* file alignment */
	  file_align = atoi (optarg);
	  if (file_align == 0)
	    FATAL ((stderr, _("file alignment must be larger than zero")));
	  break;

	case 'b':		/* page header */
	  page_header = optarg;
	  break;

	case 'B':		/* no page headers */
	  header = HDR_NONE;
	  break;

	case 'c':		/* truncate (cut) long lines */
	  line_end = LE_TRUNCATE;
	  break;

	case 'C':		/* line numbers */
	  line_numbers = 1;
	  if (optarg)
	    start_line_number = atoi (optarg);
	  break;

	case 'd':		/* specify printer */
	case 'P':
	  xfree (printer);
	  printer = xstrdup (optarg);
	  output_file = OUTPUT_FILE_NONE;
	  break;

	case 'D':		/* setpagedevice */
	  parse_key_value_pair (pagedevice, optarg);
	  break;

	case 'e':		/* special escapes */
	  special_escapes = 1;
	  if (optarg)
	    {
	      /* Specify the escape character. */
	      if (isdigit (optarg[0]))
		/* As decimal, octal, or hexadicimal number. */
		escape_char = (int) strtoul (optarg, NULL, 0);
	      else
		/* As character directly. */
		escape_char = ((unsigned char *) optarg)[0];
	    }
	  break;

	case 'E':		/* highlight */
	  highlight = 1;
	  special_escapes = 1;
	  escape_char = '\0';
	  hl_start_state = optarg;
	  break;

	case 'f':		/* font */
	  if (!parse_font_spec (optarg, &Fname, &Fpt, NULL))
	    FATAL ((stderr, _("malformed font spec: %s"), optarg));
	  user_body_font_defined = 1;
	  break;

	case 'F':		/* header font */
	  if (!parse_font_spec (optarg, &HFname, &HFpt, NULL))
	    FATAL ((stderr, _("malformed font spec: %s"), optarg));
	  break;

	case 'g':		/* print anyway */
	  /* nothing. */
	  break;

	case 'G':		/* fancy header */
	  header = HDR_FANCY;
	  if (optarg)
	    fancy_header_name = optarg;
	  else
	    fancy_header_name = fancy_header_default;

	  if (!file_existsp (fancy_header_name, ".hdr"))
	    FATAL ((stderr,
		    _("couldn't find header definition file \"%s.hdr\""),
		    fancy_header_name));
	  break;

	case 'h':		/* no job header */
	  no_job_header = 1;
	  break;

	case 'H':		/* highlight bars */
	  if (optarg)
	    highlight_bars = atoi (optarg);
	  else
	    highlight_bars = 2;
	  break;

	case 'i':		/* line indent */
	  line_indent_spec = optarg;
	  break;

	case 'I':		/* input filter */
	  input_filter = optarg;
	  break;

	case 'j':		/* borders */
	  borders = 1;
	  break;

	case 'k':		/* enable page prefeed */
	  page_prefeed = 1;
	  break;

	case 'K':		/* disable page prefeed */
	  page_prefeed = 0;
	  break;

	case 'l':		/* emulate lineprinter */
	  lines_per_page = 66;
	  header = HDR_NONE;
	  break;

	case 'L':		/* lines per page */
	  lines_per_page = atoi (optarg);
	  if (lines_per_page <= 0)
	    FATAL ((stderr,
		    _("must print at least one line per each page: %s"),
		    argv[optind]));
	  break;

	case 'm':		/* send mail upon completion */
	  mail = 1;
	  break;

	case 'M':		/* select output media */
	  media_name = xstrdup (optarg);
	  break;

	case 'n':		/* num copies */
	case '#':
	  num_copies = atoi (optarg);
	  break;

	case 'N':		/* newline character */
	  if (!(optarg[0] == 'n' || optarg[0] == 'r') || optarg[1] != '\0')
	    {
	      fprintf (stderr, _("%s: illegal newline character specifier: \
'%s': expected 'n' or 'r'\n"),
		       program, optarg);
	      goto option_error;
	    }
	  if (optarg[0] == 'n')
	    nl = '\n';
	  else
	    nl = '\r';
	  break;

	case 'o':
	case 'p':		/* output file */
	  /* Check output file "-". */
	  if (strcmp (optarg, "-") == 0)
	    output_file = OUTPUT_FILE_STDOUT;
	  else
	    output_file = optarg;
	  break;

	case 'O':		/* list missing characters */
	  list_missing_characters = 1;
	  break;

	case 'q':		/* quiet */
	  quiet = 1;
	  verbose = 0;
	  break;

	case 'r':		/* landscape */
	  landscape = 1;
	  break;

	case 'R':		/* portrait */
	  landscape = 0;
	  break;

	case 's':		/* baselineskip */
	  baselineskip = atof (optarg);
	  break;

	case 'S':		/* statusdict */
	  parse_key_value_pair (statusdict, optarg);
	  break;

	case 't':		/* title */
	case 'J':
	  title = optarg;
	  title_given = 1;
	  break;

	case 'T':		/* tabulator size */
	  tabsize = atoi (optarg);
	  if (tabsize <= 0)
	    tabsize = 1;
	  break;

	case 'u':		/* underlay */
	  underlay = optarg;
	  break;

	case 'U':		/* nup */
	  nup = atoi (optarg);
	  break;

	case 'v':		/* verbose */
	  if (optarg)
	    verbose = atoi (optarg);
	  else
	    verbose++;
	  quiet = 0;
	  break;

	case 'V':		/* version */
	  version ();
	  exit (0);
	  break;

	case 'w':		/* output language */
	  output_language = optarg;
	  if (strcmp (output_language, "PostScript") != 0)
	    /* Other output languages are handled with states. */
	    output_language_pass_through = 1;
	  break;

	case 'W':		/* a helper application option */
	  cp = strchr (optarg, ',');
	  if (cp == NULL)
	    FATAL ((stderr,
		    _("malformed argument `%s' for option -W, --option: \
no comma found"),
		      optarg));

	  if (cp - optarg != 1)
	    FATAL ((stderr, _("helper application specification must be \
single character: %s"),
			      optarg));

	  /* Take the index of the helper application and update `cp'
             to point to the beginning of the option. */
	  i = *optarg;
	  cp++;

	  if (helper_options[i] == NULL)
	    helper_options[i] = buffer_alloc ();
	  else
	    {
	      /* We already had some options for this helper
                 application.  Let's separate these arguments. */
	      buffer_append (helper_options[i], " ");
	    }

	  /* Add this new option. */
	  buffer_append (helper_options[i], cp);
	  break;

	case 'X':		/* input encoding */
	  xfree (encoding_name);
	  encoding_name = xstrdup (optarg);
	  break;

	case 'z':		/* no form feeds */
	  interpret_formfeed = 0;
	  break;

	case 'Z':		/* pass through */
	  pass_through = 1;
	  break;

	case 128:		/* underlay font */
	  if (!parse_font_spec (optarg, &ul_font, &ul_ptsize, NULL))
	    FATAL ((stderr, _("malformed font spec: %s"), optarg));
	  break;

	case 129:		/* underlay gray */
	  ul_gray = atof (optarg);
	  break;

	case 130:		/* page label format */
	  xfree (page_label_format);
	  page_label_format = xstrdup (optarg);
	  break;

	case 131:		/* download font */
	  strhash_put (download_fonts, optarg, strlen (optarg) + 1, NULL,
		       NULL);
	  break;

	case 132:		/* underlay angle */
	  ul_angle = atof (optarg);
	  ul_angle_p = 1;
	  break;

	case 133:		/* underlay position */
	  xfree (ul_position);
	  ul_position = xstrdup (optarg);
	  ul_position_p = 1;
	  break;

	case 134:		/* non-printable format */
	  xfree (npf_name);
	  npf_name = xstrdup (optarg);
	  break;

	case 135:		/* help */
	  usage ();
	  exit (0);
	  break;

	case 136:		/* highlight bar gray */
	  highlight_bar_gray = atof (optarg);
	  break;

	case 137:		/* underlay style */
	  xfree (ul_style_str);
	  ul_style_str = xstrdup (optarg);
	  break;

	case 138:		/* filter stdin */
	  input_filter_stdin = optarg;
	  break;

	case 139:		/* extra options for the printer spooler */
	  printer_options = optarg;
	  break;

	case 140:		/* slicing */
	  slicing = 1;
	  slice = atoi (optarg);
	  if (slice <= 0)
	    FATAL ((stderr, _("slice must be greater than zero")));
	  break;

	case 141:		/* help-highlight */
	  help_highlight = 1;
	  break;

	case 142:		/* States color? */
	  if (optarg == NULL)
	    states_color = 1;
	  else
	    states_color = atoi (optarg);
	  break;

	case 143:		/* mark-wrapped-lines */
	  if (optarg)
	    {
	      xfree (mark_wrapped_lines_style_name);
	      mark_wrapped_lines_style_name = xstrdup (optarg);
	    }
	  else
	    /* Set the system default. */
	    mark_wrapped_lines_style = MWLS_BOX;
	  break;

	case 144:		/* adjust margins */
	  margins_spec = optarg;
	  break;

	case 145:		/* N-up x-pad */
	  nup_xpad = atoi (optarg);
	  break;

	case 146:		/* N-up y-pad */
	  nup_ypad = atoi (optarg);
	  break;

	case 147:		/* word wrap */
	  line_end = LE_WORD_WRAP;
	  break;

	case 148:		/* horizontal column height */
	  horizontal_column_height = atof (optarg);
	  formfeed_type = FORMFEED_HCOLUMN;
	  break;

	case 149:		/* PostScript language level */
	  pslevel = atoi (optarg);
	  break;

	case 150:		/* rotate even-numbered pages */
	  rotate_even_pages = 1;
	  break;

	case 151:		/* highlight style */
	  xfree (states_highlight_style);
	  states_highlight_style = xstrdup (optarg);
	  break;

	case 152:		/* N-up colunwise */
	  nup_columnwise = 1;
	  break;

	case 153:		/* swap even page margins */
	  swap_even_page_margins = 1;
	  break;

	case 154:		/* extended return values */
	  extended_return_values = 1;
	  break;

	case 155:		/* footer */
	  page_footer = optarg;
	  break;

	case 156:		/* continuous page numbers */
	  continuous_page_numbers = 1;
	  break;

	case '?':		/* Errors found during getopt_long(). */
	option_error:
	  fprintf (stderr, _("Try `%s --help' for more information.\n"),
		   program);
	  exit (1);
	  break;

	default:
	  printf ("Hey!  main() didn't handle option \"%c\" (%d)", c, c);
	  if (optarg)
	    printf (" with arg %s", optarg);
	  printf ("\n");
	  FATAL ((stderr, "This is a bug!"));
	  break;
	}
    }
}


static void
usage ()
{
  printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
Mandatory arguments to long options are mandatory for short options too.\n\
  -#                         an alias for option -n, --copies\n\
  -1                         same as --columns=1\n\
  -2                         same as --columns=2\n\
      --columns=NUM          specify the number of columns per page\n\
  -a, --pages=PAGES          specify which pages are printed\n\
  -A, --file-align=ALIGN     align separate input files to ALIGN\n\
  -b, --header=HEADER        set page header\n\
  -B, --no-header            no page headers\n\
  -c, --truncate-lines       cut long lines (default is to wrap)\n\
  -C[START], --line-numbers[=START]\n\
                             precede each line with its line number\n\
  -d                         an alias for option --printer\n\
  -D, --setpagedevice=KEY[:VALUE]\n\
                             pass a page device definition to output\n\
  -e[CHAR], --escapes[=CHAR]       enable special escape interpretation\n"),
          program);

  printf (_("\
  -E[LANG], --highlight[=LANG]     highlight source code\n"));

  printf (_("\
  -f, --font=NAME            use font NAME for body text\n\
  -F, --header-font=NAME     use font NAME for header texts\n\
  -g, --print-anyway         nothing (compatibility option)\n\
  -G                         same as --fancy-header\n\
      --fancy-header[=NAME]  select fancy page header\n\
  -h, --no-job-header        suppress the job header page\n\
  -H[NUM], --highlight-bars[=NUM]  specify how high highlight bars are\n\
  -i, --indent=NUM           set line indent to NUM characters\n\
  -I, --filter=CMD           read input files through input filter CMD\n\
  -j, --borders              print borders around columns\n\
  -J,                        an alias for option --title\n\
  -k, --page-prefeed         enable page prefeed\n\
  -K, --no-page-prefeed      disable page prefeed\n\
  -l, --lineprinter          simulate lineprinter, this is an alias for:\n\
                               --lines-per-page=66, --no-header, --portrait,\n\
                               --columns=1\n"));

  printf (_("\
  -L, --lines-per-page=NUM   specify how many lines are printed on each page\n\
  -m, --mail                 send mail upon completion\n\
  -M, --media=NAME           use output media NAME\n\
  -n, --copies=NUM           print NUM copies of each page\n\
  -N, --newline=NL           select the newline character.  Possible\n\
                             values for NL are: n (`\\n') and r (`\\r').\n\
  -o                         an alias for option --output\n\
  -O, --missing-characters   list missing characters\n\
  -p, --output=FILE          leave output to file FILE.  If FILE is `-',\n\
                             leave output to stdout.\n\
  -P, --printer=NAME         print output to printer NAME\n\
  -q, --quiet, --silent      be really quiet\n\
  -r, --landscape            print in landscape mode\n\
  -R, --portrait             print in portrait mode\n"));

  printf (_("\
  -s, --baselineskip=NUM     set baselineskip to NUM\n\
  -S, --statusdict=KEY[:VALUE]\n\
                             pass a statusdict definition to the output\n\
  -t, --title=TITLE          set banner page's job title to TITLE.  Option\n\
                             sets also the name of the input file stdin.\n\
  -T, --tabsize=NUM          set tabulator size to NUM\n\
  -u[TEXT], --underlay[=TEXT]      print TEXT under every page\n\
  -U, --nup=NUM              print NUM logical pages on each output page\n\
  -v, --verbose              tell what we are doing\n\
  -V, --version              print version number\n\
  -w, --language=LANG        set output language to LANG\n\
  -W, --options=APP,OPTION   pass option OPTION to helper application APP\n\
  -X, --encoding=NAME        use input encoding NAME\n\
  -z, --no-formfeed          do not interpret form feed characters\n\
  -Z, --pass-through         pass through PostScript and PCL files\n\
                             without any modifications\n"));

  printf (_("Long-only options:\n\
  --color[=bool]             create color outputs with states\n\
  --continuous-page-numbers  count page numbers across input files.  Don't\n\
                             restart numbering at beginning of each file.\n\
  --download-font=NAME       download font NAME\n\
  --extended-return-values   enable extended return values\n\
  --filter-stdin=NAME        specify how stdin is shown to the input filter\n\
  --footer=FOOTER            set page footer\n\
  --h-column-height=HEIGHT   set the horizontal column height to HEIGHT\n\
  --help                     print this help and exit\n"));

  printf (_("\
  --help-highlight           describe all supported --highlight languages\n\
                             and file formats\n\
  --highlight-bar-gray=NUM   print highlight bars with gray NUM (0 - 1)\n\
  --list-media               list names of all known media\n\
  --margins=LEFT:RIGHT:TOP:BOTTOM\n\
                             adjust page marginals\n\
  --mark-wrapped-lines[STYLE]\n\
                             mark wrapped lines in the output with STYLE\n\
  --non-printable-format=FMT specify how non-printable chars are printed\n"));

  printf (_("\
  --nup-columnwise           layout pages in the N-up printing columnwise\n\
  --nup-xpad=NUM             set the page x-padding of N-up printing to NUM\n\
  --nup-ypad=NUM             set the page y-padding of N-up printing to NUM\n\
  --page-label-format=FMT    set page label format to FMT\n\
  --ps-level=LEVEL           set the PostScript language level that enscript\n\
                             should use\n\
  --printer-options=OPTIONS  pass extra options to the printer command\n\
  --rotate-even-pages        rotate even-numbered pages 180 degrees\n"));

  printf (_("\
  --slice=NUM                print vertical slice NUM\n\
  --style=STYLE              use highlight style STYLE\n\
  --swap-even-page-margins   swap left and right side margins for each even\n\
                             numbered page\n\
  --toc                      print table of contents\n\
  --ul-angle=ANGLE           set underlay text's angle to ANGLE\n\
  --ul-font=NAME             print underlays with font NAME\n\
  --ul-gray=NUM              print underlays with gray value NUM\n\
  --ul-position=POS          set underlay's starting position to POS\n\
  --ul-style=STYLE           print underlays with style STYLE\n\
  --word-wrap                wrap long lines from word boundaries\n\
"));

  printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
}


static void
version ()
{
  printf ("%s\n\
Copyright (C) 1995-2003, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.\n\
%s comes with NO WARRANTY, to the extent permitted by law.\n\
You may redistribute copies of %s under the terms of the GNU\n\
General Public License, version 3 or, at your option, any later version.\n\
For more information about these matters, see the files named COPYING.\n",
	  PACKAGE_STRING,
	  PACKAGE_NAME,
	  PACKAGE_NAME);
}
