1 /*
2  * main.c -- main loop, and interface with user
3  *
4  * Copyright (c) 1988-1993 Miguel Santana
5  * Copyright (c) 1995-2000 Akim Demaille, Miguel Santana
6  */
7 
8 /*
9  * This file is part of a2ps.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2, or (at your option)
14  * any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; see the file COPYING.  If not, write to
23  * the Free Software Foundation, 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  */
26 
27 
28 /************************************************************************/
29 /*                                                                      */
30 /*                      I n c l u d e   f i l e s                       */
31 /*                                                                      */
32 /************************************************************************/
33 #include <assert.h>
34 
35 #include "a2ps.h"
36 #include "argmatch.h"
37 #include "confg.h"
38 #include "options.h"
39 #include "pathwalk.h"
40 #include "select.h"
41 #include "generate.h"
42 #include "printers.h"
43 #include "delegate.h"
44 #include "metaseq.h"
45 #include "regex.h"
46 #include "buffer.h"
47 #include "psgen.h"
48 #include "prolog.h"
49 #include "stream.h"
50 #include "getnum.h"
51 #include "title.h"
52 #include "useropt.h"
53 #include "main.h"
54 #include "lexps.h"
55 #include <signal.h>
56 #include "signame.h"
57 #include "long-options.h"
58 #include "version-etc.h"
59 
60 /* From basename.c */
61 char *base_name PARAMS ((const char *path));
62 
63 /************************************************************************/
64 /*                                                                      */
65 /*                 G l o b a l   d e f i n i t i o n s                  */
66 /*                                                                      */
67 /************************************************************************/
68 /************************************************************************
69  *	The various global behaviors
70  */
71 enum behavior
72   {
73     b_ps,		/* postscript generator (usual PS converter)    */
74     b_guess,		/* do as file(1) does: return the ssh file name */
75     b_help,
76     b_version,
77     b_expand,		/* Expand the strings given as arguments. */
78     b_which,		/* Look for the args in the path, and report. */
79     b_glob,		/* Same, but with globbing. */
80     b_list_options,
81     b_list_features,
82     b_list_media,
83     b_list_style_sheets,
84     b_list_html_style_sheets,
85     b_list_texinfo_style_sheets,
86     b_list_printers,
87     b_list_delegations,
88     b_list_macro_meta_sequences,
89     b_list_encodings,
90     b_list_texinfo_encodings,
91     b_list_user_options,
92     b_list_prologues,
93     b_list_texinfo_prologues,
94     b_list_ppd
95   };
96 
97 /* Stores the task to execute.  Default: a2ps. */
98 
99 enum behavior behavior = b_ps;
100 
101 /* Name under which this program is called.  To understand why it is
102    defined twice, see lib/confg.gperf, handling of `Options:'. */
103 
104 char *program_name;
105 const char *program_invocation_name;
106 
107 /* Stores the data of liba2ps.  */
108 
109 a2ps_job *job = NULL;
110 
111 
112 /* Syntax table for regex. */
113 
114 char *re_syntax_table = NULL;
115 
116 #define RE_SYNTAX_A2PS \
117   (/* Allow char classes. */					\
118     RE_CHAR_CLASSES						\
119   /* Be picky. */						\
120   | RE_CONTEXT_INVALID_OPS					\
121   /* Allow intervals with `{' and `}', forbid invalid ranges. */\
122   | RE_INTERVALS | RE_NO_BK_BRACES | RE_NO_EMPTY_RANGES		\
123   /* `(' and `)' are the grouping operators. */			\
124   | RE_NO_BK_PARENS						\
125   /* `|' is the alternation. */					\
126   | RE_NO_BK_VBAR)
127 
128 
129 /************************************************************************/
130 /*      Related to the config files                                     */
131 /************************************************************************/
132 /*
133  * Hash table of the delegations
134  */
135 struct hash_table_s *delegation_table;
136 
137 /*
138  * Content of sheets.map
139  */
140 struct darray *sheets_map = NULL;
141 
142 /*
143  * Hash table of the sheet yet read
144  */
145 struct hash_table_s *style_sheets = NULL;
146 
147 /************************************************************************/
148 /*      Related to the options                                          */
149 /************************************************************************/
150 /*
151  * Delegate files to other applications.
152  */
153 bool delegate_p = true;
154 
155 /*
156  * --toc[=format], generate a table of content
157  */
158 uchar *toc = NULL;
159 
160 /*
161  * -E: style sheet to use. NULL => automated
162  */
163 char *style_request = NULL;
164 
165 /*
166  * -g/--highlight-level: 0, 1 or 2
167  */
168 int highlight_level = 1;
169 
170 /*
171  * --strip=NUM, don't write the comments
172  */
173 int strip_level = 0;
174 
175 /*
176  * --end-of-line=TYPE, specify what are the sequences of chars to
177  * interpret as end of line
178  */
179 enum eol_e end_of_line = eol_auto;
180 
181 /************************************************************************/
182 /*                             Service routines                         */
183 /************************************************************************/
184 /*
185  * Unlink all the used files.  Used for atexit
186  */
187 static void
exit_handler(void)188 exit_handler (void)
189 {
190   if (job)
191     a2ps_job_unlink_tmpfiles (job);
192   if (sample_tmpname)
193     unlink (sample_tmpname);
194 }
195 
196 static RETSIGTYPE
signal_handler(int signum)197 signal_handler (int signum)
198 {
199   /* Error calls exit which calls atexit which removes the files. */
200   error (EXIT_FAILURE, 0,
201 	 _("received signal %d: %s"), signum, strsignal (signum));
202 }
203 
204 /************************************************************************
205  * Read the highlighting level
206  */
207 static const char *const highlight_level_args[] =
208 {
209   "none", "off", "0",
210   "normal", "light", "1",
211   "heavy", "symbols", "2",
212   0
213 };
214 
215 static const int highlight_level_types[] =
216 {
217   0, 0, 0,
218   1, 1, 1,
219   2, 2, 2
220 };
221 
222 /*
223  * Return the highlight_level value
224  */
225 static int
get_highlight_level(const char * option,const char * arg)226 get_highlight_level (const char *option, const char *arg)
227 {
228   ARGMATCH_ASSERT (highlight_level_args, highlight_level_types);
229   return XARGCASEMATCH (option, arg,
230 			highlight_level_args, highlight_level_types);
231 }
232 
233 static char *
highlight_level_to_string(int level)234 highlight_level_to_string (int level)
235 {
236   switch (level)
237     {
238     case 2:
239       /* TRANS: highlighting level = heavy (2/2) */
240       return _("heavy");
241 
242     case 1:
243       /* TRANS: highlighting level = normal (1/2) */
244       return _("normal");
245 
246     case 0:
247       /* TRANS: highlighting level = none (0/2) */
248       return _("none");
249     }
250   return NULL;			/* For -Wall */
251 }
252 
253 /************************************************************************
254  * Read the --list argument
255  */
256 static const char *const behavior_args[] =
257 {
258   "defaults", "options", "settings",
259   "features", "plugins",
260   "delegations",
261   "encodings", "charsets",
262   "variables", "macro-meta-sequences",
263   "media",
264   "printers", "outputs",
265   "style-sheets", "languages",
266   "user-options", "shortcuts",
267   "prologues",
268   "texinfo-style-sheets", "ssh-texi",
269   "html-style-sheets", "ssh-html",
270   "texinfo-encodings", "edf-texi",
271   "texinfo-prologues", "pro-texi",
272   "ppd",
273   "version", "release",
274   "help", "usage",
275   "expand",
276   "which", "find",
277   "glob",
278   "ps",
279   NULL
280 };
281 
282 static const enum behavior behavior_types[] =
283 {
284   b_list_options, b_list_options, b_list_options,
285   b_list_features, b_list_features,
286   b_list_delegations,
287   b_list_encodings, b_list_encodings,
288   b_list_macro_meta_sequences, b_list_macro_meta_sequences,
289   b_list_media,
290   b_list_printers, b_list_printers,
291   b_list_style_sheets, b_list_style_sheets,
292   b_list_user_options, b_list_user_options,
293   b_list_prologues,
294   b_list_texinfo_style_sheets, b_list_texinfo_style_sheets,
295   b_list_html_style_sheets, b_list_html_style_sheets,
296   b_list_texinfo_encodings, b_list_texinfo_encodings,
297   b_list_texinfo_prologues, b_list_texinfo_prologues,
298   b_list_ppd,
299   b_version, b_version,
300   b_help, b_help,
301   b_expand,
302   b_which, b_which,
303   b_glob,
304   b_ps
305 };
306 
307 /************************************************************************/
308 /*                              Interface                               */
309 /************************************************************************/
310 
311 /*------------------------------------------------------------------.
312 | Print information depending on the installation.  It is also used |
313 | to store useful information in the output to help us debuging the |
314 | users :).                                                         |
315 `------------------------------------------------------------------*/
316 
317 static void
list_options(struct a2ps_job * a_job,FILE * stream)318 list_options (struct a2ps_job *a_job, FILE *stream)
319 {
320 #if 0
321   /* This is just so that gettext knows I use those two strings. */
322   static char *yes = N_("yes");
323   static char *no  = N_("no");
324 #endif
325 
326 #define bool_to_string(bool) ((bool) ? _("yes") : _("no"))
327   uchar buf[256], buf2[256];
328   const char *cp = NULL;
329   uchar *ucp = NULL;
330 
331   /* Title of --list-options (%s%s is `a2ps' `version' */
332   title (stream, '=', true,
333 	 _("Configuration status of %s %s\n"),
334 	 program_invocation_name, VERSION);
335   putc ('\n', stream);
336 
337   title (stream, '-', false, _("Sheets:\n"));
338   fprintf (stream, _("\
339   medium          = %s%s, %s\n\
340   page layout     = %d x %d, %s\n\
341   borders         = %s\n\
342   file alignment  = %s\n\
343   interior margin = %d\n"),
344 	   a_job->medium->name,
345 	   (a2ps_medium_libpaper_p (job, job->medium_request)
346 	    ? " (libpaper)" : ""),
347 	   (a_job->orientation == portrait) ? _("portrait") : _("landscape"),
348 	   a_job->columns, a_job->rows,
349 	   madir_to_string (a_job->madir),
350 	   bool_to_string (a_job->border),
351 	   file_align_to_string (a_job->file_align),
352 	   a_job->margin);
353   putc ('\n', stream);
354 
355   if (a_job->columns_requested > 0)
356     sprintf ((char *) buf, _("%d characters per line"),
357 	     a_job->columns_requested);
358   else if (a_job->lines_requested > 0)
359     sprintf ((char *) buf, _("%d lines per page"),
360 	     a_job->lines_requested);
361   else
362     sprintf ((char *) buf, _("font size is %gpt"), a_job->fontsize);
363 
364   switch (a_job->numbering)
365     {
366     case 0:
367       ustrcpy (buf2, _("no"));
368       break;
369     case 1:
370       /* number line: each line */
371       ustrcpy (buf2, _("each line"));
372       break;
373     default:
374       /* number line: each %d line */
375       sprintf ((char *) buf2, _("each %d lines"), a_job->numbering);
376     }
377 
378   title (stream, '-', false, _("Virtual pages:\n"));
379   fprintf (stream, _("\
380   number lines         = %s\n\
381   format               = %s\n\
382   tabulation size      = %d\n\
383   non printable format = %s\n"),
384 	   buf2,
385 	   buf,
386 	   a_job->tabsize,
387 	   unprintable_format_to_string (a_job->unprintable_format));
388   putc ('\n', stream);
389 
390   title (stream, '-', false, _("Headers:\n"));
391   fprintf (stream, _("\
392   header       = %s\n\
393   left footer  = %s\n\
394   footer       = %s\n\
395   right footer = %s\n\
396   left title   = %s\n\
397   center title = %s\n\
398   right title  = %s\n\
399   under lay    = %s\n"),
400 	   UNNULL (a_job->header),
401 	   UNNULL (a_job->left_footer),
402 	   UNNULL (a_job->footer),
403 	   UNNULL (a_job->right_footer),
404 	   UNNULL (a_job->left_title),
405 	   UNNULL (a_job->center_title),
406 	   UNNULL (a_job->right_title),
407 	   UNNULL (a_job->water));
408   putc ('\n', stream);
409 
410   title (stream, '-', false, _("Input:\n"));
411   fprintf (stream, _("\
412   truncate lines = %s\n\
413   interpret      = %s\n\
414   end of line    = %s\n\
415   encoding       = %s\n\
416   document title = %s\n\
417   prologue       = %s\n\
418   print anyway   = %s\n\
419   delegating     = %s\n"),
420 	   bool_to_string (!a_job->folding),
421 	   bool_to_string (a_job->interpret),
422 	   eol_to_string (end_of_line),
423 	   encoding_get_name (a_job->requested_encoding),
424 	   a_job->title,
425 	   a_job->prolog,
426 	   bool_to_string (a_job->print_binaries),
427 	   bool_to_string (delegate_p));
428   putc ('\n', stream);
429 
430   /*
431    * Pretty printing
432    */
433   if (IS_EMPTY (style_request))
434     /* TRANS: a2ps -E --list=options.  Warning, this answer is also
435        used for the PPD file.  Make it compatible with both.  */
436     ustrcpy (buf, _("selected automatically"));
437   else
438     ustrcpy (buf, style_request);
439   title (stream, '-', false, _("Pretty-printing:\n"));
440   fprintf (stream, _("\
441   style sheet     = %s\n\
442   highlight level = %s\n\
443   strip level     = %d\n"),
444 	   buf,
445 	   highlight_level_to_string (highlight_level),
446 	   strip_level);
447   putc ('\n', stream);
448 
449   /*
450    * Information on where will go the output
451    */
452 
453   /* Make a nice message to tell where the output is sent */
454   ucp = a2ps_flag_destination_to_string (a_job);
455 
456   /* Make a nice message to tell what version control is used */
457   switch (a_job->backup_type)
458     {
459     case none:
460       cp = _("never make backups");
461       break;
462 
463     case simple:
464       cp = _("simple backups of every file");
465       break;
466 
467     case numbered_existing:
468       /* appears in a2ps --version-=existing --list=defaults */
469       cp = _("numbered backups of files already numbered,\n\
470                             and simple of others");
471       break;
472 
473     case numbered:
474       cp = _("numbered backups of every file");
475       break;
476     }
477 
478   title (stream, '-', false, _("Output:\n"));
479   fprintf (stream, _("\
480   destination     = %s\n\
481   version control = %s\n\
482   backup suffix   = %s\n"),
483 	   ucp, cp, simple_backup_suffix);
484   putc ('\n', stream);
485   free (ucp);
486 
487   /*
488    * PostScript report.
489    * TRANS: to be aligned with `page prefeed ='
490    */
491   cp = a2ps_printers_request_ppdkey_get (a_job->printers);
492   title (stream, '-', false, _("PostScript:\n"));
493   fprintf (stream, _("\
494   magic number              = %s\n\
495   Printer Description (PPD) = %s\n\
496   default PPD               = %s\n\
497   page label format         = %s\n\
498   number of copies          = %d\n\
499   sides per sheet           = %s\n\
500   page device definitions   = "),
501 	   a_job->status->magic_number,
502 	   cp ? cp : _("selected automatically"),
503 	   a2ps_printers_default_ppdkey_get (a_job->printers),
504 	   a_job->status->page_label_format,
505 	   a_job->copies,
506 	   (a_job->duplex == simplex
507 	    ? "Simplex"
508 	    : (a_job->duplex == duplex
509 	       ? "Duplex"
510 	       : "DuplexTumble")));
511 
512   list_pagedevice (a_job, stream);
513   fprintf (stream, _("\
514   statusdict definitions    = "));
515   list_statusdict (a_job, stream);
516   fprintf (stream, _("\
517   page prefeed              = %s\n"),
518 	   bool_to_string (a_job->page_prefeed));
519   putc ('\n', stream);
520 
521 
522   /*
523    * Internal Details
524    */
525   title (stream, '-', false, _("Internals:\n"));
526   fprintf (stream, _("\
527   verbosity level     = %d\n\
528   file command        = %s\n\
529   library path        = \n"),
530 	   msg_verbosity,
531 	   UNNULL (a_job->file_command));
532   pw_fprintf_path (stream, "\t%s\n", a_job->common.path);
533 }
534 
535 
536 /*------------------------------------------------------------------.
537 | This is used in psgen to push into the PostScript the report of   |
538 | the state of a2ps when it produced the file.  I'm tired of        |
539 | fighting with users who don't really say everything on the state  |
540 | of their a2ps when something goes wrong.  Now I can ask a ps file |
541 | generated with --debug, and got everything I need.                |
542 `------------------------------------------------------------------*/
543 
544 static void
spy_user(struct a2ps_job * a_job,FILE * stream)545 spy_user (struct a2ps_job *a_job, FILE * stream)
546 {
547 #define PREFIX "% "
548   FILE *spy;
549   char *spyname;
550   char buf[BUFSIZ];
551 
552   /* Use one of the temp file names so that cleanup can be correctly
553      done. */
554   tempname_ensure (job->tmp_filenames[0]);
555   spyname = job->tmp_filenames[0];
556   spy = fopen (spyname, "w");
557   if (!spy)
558     error (1, errno, _("cannot open file `%s'"), quotearg (spyname));
559 
560 
561   /* Well, this is the information I've been fighting with some users
562      to get them exact...  I hate doing that, but I need to save part
563      of my time.  */
564   fputs ("SPY-BEGIN\n", spy);
565   fputs ((char *) expand_user_string (job, CURRENT_FILE (job),
566 				      (const uchar *) "Debugging info",
567 				      (const uchar *) "%V was called with #!$|| |\n\n"),
568 	 spy);
569 
570   list_options (a_job, spy);
571   putc ('\n', spy);
572   macro_meta_sequences_list_long (a_job, spy);
573   fputs ("SPY-END\n", spy);
574 
575   /* Yes, I know, there are certainly better means.  Just teach them
576      to me...  */
577   fclose (spy);
578   fopen (spyname, "r");
579   if (!spy)
580     error (1, errno, _("cannot open file `%s'"), quotearg (spyname));
581 
582   while (fgets (buf, sizeof (buf), spy))
583     {
584       fputs (PREFIX, stream);
585       fputs (buf, stream);
586     }
587   fputs (PREFIX, stream);
588   putc ('\n', stream);
589   fclose (spy);
590   unlink (spyname);
591 }
592 
593 
594 /*--------------------------------------------------.
595 | Print information depending on the installation.  |
596 `--------------------------------------------------*/
597 
598 static void
list_features(struct a2ps_job * a_job,FILE * stream)599 list_features (struct a2ps_job *a_job, FILE * stream)
600 {
601   /* Known languages */
602   list_style_sheets_short (stream);
603   putc ('\n', stream);
604 
605   /* Known char sets */
606   list_encodings_short (a_job, stream);
607   putc ('\n', stream);
608 
609   /* Known media */
610   list_media_short (a_job, stream);
611   putc ('\n', stream);
612 
613   /* Known prologues */
614   prologues_list_short (a_job, stream);
615   putc ('\n', stream);
616 
617   /* Known PPD files */
618   a2ps_ppd_list_short (a_job, stream);
619   putc ('\n', stream);
620 
621   /* Known "printers" */
622   a2ps_printers_list_short (a_job, stream);
623   putc ('\n', stream);
624 
625   /* Known "delegates" */
626   delegations_list_short (delegation_table, stream);
627   putc ('\n', stream);
628 
629   /* Known user options */
630   user_options_list_short (a_job, stream);
631   putc ('\n', stream);
632 
633   /* Macro meta seq. */
634   macro_meta_sequences_list_short (a_job, stream);
635 }
636 
637 /*------------------------.
638 | Print a usage message.  |
639 `------------------------*/
640 
641 #define sfputs(String)	fputs (String, stream)
642 #define sputc(Char)	putc (Char, stream)
643 
644 static void
usage(int status)645 usage (int status)
646 {
647   /* Currently, there seem to be no use in being able to use another
648      stream than STDOUT.  */
649   FILE *stream = stdout;
650 
651   fprintf (stream, _("\
652 Usage: %s [OPTION]... [FILE]...\n\
653 \n\
654 Convert FILE(s) or standard input to PostScript.\n\
655 \n\
656 Mandatory arguments to long options are mandatory for short options too.\n\
657 Long options marked with * require a yes/no argument, corresponding\n\
658 short options stand for `yes'.\n"),
659 	   program_invocation_name);
660 
661   /*
662    * Does not print, and exits with success
663    */
664   sputc ('\n');
665   sfputs (_("Tasks:\n"));
666   sfputs (_("\
667   --version        display version\n\
668   --help           display this help\n\
669   --guess          report guessed types of FILES\n\
670   --which          report the full path of library files named FILES\n\
671   --glob           report the full path of library files matching FILES\n\
672   --list=defaults  display default settings and parameters\n\
673   --list=TOPIC     detailed list on TOPIC (delegations, encodings, features,\n\
674                    variables, media, ppd, printers, prologues, style-sheets,\n\
675                    user-options)\n"));
676   sputc ('\n');
677   sfputs (_("\
678 After having performed the task, exit successfully.  Detailed lists may\n\
679 provide additional help on specific features.\n"));
680 
681   /*
682    * Applies to the whole behavior
683    */
684   sputc ('\n');
685   sfputs (_("Global:\n"));
686   sfputs (_("\
687   -q, --quiet, --silent      be really quiet\n\
688   -v, --verbose[=LEVEL]      set verbosity on, or to LEVEL\n\
689   -=, --user-option=OPTION   use the user defined shortcut OPTION\n\
690       --debug                enable debugging features\n\
691   -D, --define=KEY[:VALUE]   unset variable KEY or set to VALUE\n"));
692 
693   sputc ('\n');
694   sfputs (_("Sheets:\n"));
695   sfputs (_("\
696   -M, --medium=NAME      use output medium NAME\n\
697   -r, --landscape        print in landscape mode\n\
698   -R, --portrait         print in portrait mode\n\
699       --columns=NUM      number of columns per sheet\n\
700       --rows=NUM         number of rows per sheet\n\
701       --major=DIRECTION  first fill (DIRECTION=) rows, or columns\n\
702   -1, -2, ..., -9        predefined font sizes and layouts for 1.. 9 virtuals\n\
703   -A, --file-align=MODE  align separate files according to MODE (fill, rank\n\
704                          page, sheet, or a number)\n\
705   -j, --borders*         print borders around columns\n\
706       --margin[=NUM]     define an interior margin of size NUM\n"));
707   sputc ('\n');
708   sfputs (_("\
709 The options -1.. -9 affect several primitive parameters to set up predefined\n\
710 layouts with 80 columns.  Therefore the order matters: `-R -f40 -2' is\n\
711 equivalent to `-2'.  To modify the layout, use `-2Rf40', or compose primitive\n\
712 options (`--columns', `--font-size' etc.).\n"));
713 
714   sputc ('\n');
715   sfputs (_("Virtual pages:\n"));
716   sfputs (_("\
717       --line-numbers=NUM     precede each NUM lines with its line number\n\
718   -C                         alias for --line-numbers=5\n\
719   -f, --font-size=SIZE       use font SIZE (float) for the body text\n\
720   -L, --lines-per-page=NUM   scale the font to print NUM lines per virtual\n\
721   -l, --chars-per-line=NUM   scale the font to print NUM columns per virtual\n\
722   -m, --catman               process FILE as a man page (same as -L66)\n\
723   -T, --tabsize=NUM          set tabulator size to NUM\n\
724   --non-printable-format=FMT specify how non-printable chars are printed\n"));
725 
726   sputc ('\n');
727   sfputs (_("Headings:\n"));
728   /* xgettext:no-c-format */
729   sfputs (_("\
730   -B, --no-header        no page headers at all\n\
731   -b, --header[=TEXT]    set page header\n\
732   -u, --underlay[=TEXT]  print TEXT under every page\n\
733   --center-title[=TEXT]  set page title to TITLE\n\
734   --left-title[=TEXT]    set left and right page title to TEXT\n\
735   --right-title[=TEXT]\n\
736   --left-footer[=TEXT]   set sheet footers to TEXT\n\
737   --footer[=TEXT]\n\
738   --right-footer[=TEXT]\n"));
739   sputc ('\n');
740   sfputs (_("\
741 The TEXTs may use special escapes.\n"));
742 
743   sputc ('\n');
744   sfputs (_("Input:\n"));
745   sfputs (_("\
746   -a, --pages[=RANGE]        select the pages to print\n\
747   -c, --truncate-lines*      cut long lines\n\
748   -i, --interpret*           interpret tab, bs and ff chars\n\
749       --end-of-line=TYPE     specify the eol char (TYPE: r, n, nr, rn, any)\n\
750   -X, --encoding=NAME        use input encoding NAME\n\
751   -t, --title=NAME           set the name of the job\n\
752       --stdin=NAME           set the name of the input file stdin\n\
753       --print-anyway*        force binary printing\n\
754   -Z, --delegate*            delegate files to another application\n\
755       --toc[=TEXT]           generate a table of content\n"));
756   sputc ('\n');
757   sfputs (_("\
758 When delegations are enabled, a2ps may use other applications to handle the\n\
759 processing of files that should not be printed as raw information, e.g., HTML\n\
760 PostScript, PDF etc.\n"));
761 
762   sputc ('\n');
763   sfputs (_("Pretty-printing:\n"));
764   sfputs (_("\
765   -E, --pretty-print[=LANG]  enable pretty-printing (set style to LANG)\n\
766   --highlight-level=LEVEL    set pretty printing highlight LEVEL\n\
767                              LEVEL can be none, normal or heavy\n\
768   -g                         alias for --highlight-level=heavy\n\
769   --strip-level=NUM          level of comments stripping\n"));
770 
771   sputc ('\n');
772   sfputs (_("Output:\n"));
773   sfputs (_("\
774   -o, --output=FILE          leave output to file FILE.  If FILE is `-',\n\
775                              leave output to stdout.\n\
776   --version-control=WORD     override the usual version control\n\
777   --suffix=SUFFIX            override the usual backup suffix\n\
778   -P, --printer=NAME         send output to printer NAME\n\
779   -d                         send output to the default printer\n"));
780 
781   sputc ('\n');
782   sfputs (_("PostScript:\n"));
783   sfputs (_("\
784       --prologue=FILE        include FILE.pro as PostScript prologue\n\
785       --ppd[=KEY]            automatic PPD selection or set to KEY\n\
786   -n, --copies=NUM           print NUM copies of each page\n\
787   -s, --sides=MODE           set the duplex MODE (`1' or `simplex',\n\
788                              `2' or `duplex', `tumble')\n\
789   -S, --setpagedevice=K[:V]  pass a page device definition to output\n\
790       --statusdict=K[:[:]V]  pass a statusdict definition to the output\n\
791   -k, --page-prefeed         enable page prefeed\n\
792   -K, --no-page-prefeed      disable page prefeed\n"));
793 
794 
795   /* A short documentation. */
796   sputc ('\n');
797   sfputs (_("\
798 By default a2ps is tuned to do what you want to, so trust it.  To pretty\n\
799 print the content of the `src' directory and a table of content, and send the\n\
800 result to the printer `lw',\n\
801 \n\
802     $ a2ps -P lw --toc src/*\n\
803 \n\
804 To process the files `sample.ps' and `sample.html' and display the result,\n\
805 \n\
806     $ a2ps -P display sample.ps sample.html\n\
807 \n\
808 To process a mailbox in 4 up,\n\
809 \n\
810     $ a2ps -=mail -4 mailbox\n\
811 \n\
812 To print as a booklet on the default printer, which is Duplex capable,\n\
813 \n\
814     $ a2ps -=book paper.dvi.gz -d\n"));
815 
816   /* Finally, some addresses. */
817   sputc ('\n');
818   sfputs (_("\
819 News, updates and documentation: visit http://www.inf.enst.fr/~demaille/a2ps/.\n"));
820   sfputs (_("Report bugs to <bug-a2ps@gnu.org>.\n"));
821 
822   exit (status);
823 }
824 
825 
826 /*----------------------------------------------------------------.
827 | Handle the options that a2ps understands (not liba2ps) Return 1 |
828 | for success                                                     |
829 `----------------------------------------------------------------*/
830 
831 static int
handle_a2ps_option(int option,char * optional_arg)832 handle_a2ps_option (int option, char *optional_arg)
833 {
834   switch (option)
835     {
836     case 'E':			/* --pretty-print select language */
837       xstrcpy (style_request, optional_arg);
838       break;
839 
840     case 'g':			/* Symbol translation */
841       highlight_level = 2;
842       break;
843 
844     case 154:			/* Symbol translation */
845       highlight_level =
846 	a2ps_get_bool ("--graphic-symbols", optional_arg);
847       break;
848 
849     case 173:			/* Level of high lighting */
850       highlight_level =
851 	get_highlight_level ("--highlight-level", optional_arg);
852       break;
853 
854     case 'h':			/* --help */
855       behavior = b_help;
856       break;
857 
858     case 'V':			/* version and configuration info */
859       behavior = b_version;
860       break;
861 
862     case 'Z':			/* --delegate */
863       delegate_p = true;
864       break;
865 
866     case 138:
867       behavior = b_guess;
868       break;
869 
870     case 137:
871       behavior = b_which;
872       break;
873 
874     case 150:
875       behavior = b_glob;
876       break;
877 
878     case 139:
879       behavior = b_list_options;
880       break;
881 
882     case 145:
883       ARGMATCH_ASSERT (behavior_args, behavior_types);
884       behavior = XARGCASEMATCH ("--list", optional_arg,
885 				behavior_args, behavior_types);
886       break;
887 
888     case 148:			/* --strip-level */
889       strip_level =
890 	get_integer_in_range ("--strip-level", optional_arg,
891 			      0, 3, range_min_max);
892       break;
893 
894     case 160:			/* --delegate=BOOL */
895       delegate_p = a2ps_get_bool ("--delegate", optional_arg);
896       break;
897 
898     case 161:			/* --list-media */
899       behavior = b_list_media;
900       break;
901 
902     case 162:			/* --list-style-sheets  */
903       behavior = b_list_style_sheets;
904       break;
905 
906     case 167:			/* --toc[=toc format]           */
907       /* If no argument is given, use #{toc}. */
908       xustrcpy (toc, optional_arg ? optional_arg : "#{toc}");
909       break;
910 
911     case 169:			/* --end-of-line=TYPE           */
912       end_of_line = option_string_to_eol ("--end-of-line", optional_arg);
913       break;
914 
915     default:
916       return 0;
917     }
918   return 1;
919 }
920 
921 /************************************************************************/
922 /*                      Main routine for this program.                  */
923 /************************************************************************/
924 int
main(int argc,char * argv[])925 main (int argc, char *argv[])
926 {
927   int argn;
928 
929   /* Architecture specific initialization. */
930 #ifdef __EMX__
931   /* Wildcard expansion for OS/2 */
932   _wildcard (&argc, &argv);
933 #endif
934 
935   /* Name under which this program was called. */
936   program_name = base_name (argv[0]);
937   program_invocation_name = xstrdup (program_name);
938   version_etc_copyright = N_("\
939 Copyright (c) 1988-1993 Miguel Santana\n\
940 Copyright (c) 1995-2000 Akim Demaille, Miguel Santana");
941 
942   /* Set the NLS on */
943   setlocale (LC_TIME, "");
944 #ifdef HAVE_LC_MESSAGES
945   setlocale (LC_MESSAGES, "");
946 #endif
947   setlocale (LC_CTYPE, "");
948 
949   bindtextdomain (PACKAGE, LOCALEDIR);
950   textdomain (PACKAGE);
951 
952   /* People don't want initializations when they just request a --help
953      or --version. */
954   parse_long_options (argc, argv,
955 		      NULL, GNU_PACKAGE, VERSION,
956 		      "Akim Demaille, Miguel Santana",
957 		      usage);
958 
959   /* Catch the exits and signals to cleanup the mess.
960 
961      We do it now, though there is no tmp files before long, because I
962      find it beautiful to see `received signal blah blah' even if it
963      is even before a2ps could make a move.  */
964   atexit (exit_handler);
965   signame_init ();
966 #define signal_set(Sig, Handler)		\
967  do {						\
968    if (signal (Sig, Handler) == SIG_IGN)	\
969      signal (Sig, SIG_IGN);			\
970  } while (0)
971   /* There are warnings on Solaris.  This is due to their definition
972      of SIG_IGN as `(void (*)())1'.  Please ignore ;) */
973   signal_set (SIGINT, signal_handler);
974 #ifdef SIGHUP
975   signal_set (SIGHUP, signal_handler);
976 #endif
977   signal_set (SIGTERM, signal_handler);
978 #ifdef SIGPIPE
979   signal (SIGPIPE, signal_handler);
980 #endif
981 
982   /* Hooks for reading the config files */
983   delegation_hook = add_delegation;
984 
985   /* Hook when reading the options */
986   handle_option_hook = handle_a2ps_option;
987 
988   /* Prepare to receive in the hash table of the delegations */
989   delegation_table = delegation_table_new ();
990 
991   /* Set the syntax that has be chosen for regexp */
992   re_set_syntax (RE_SYNTAX_A2PS);
993 
994   /* The spine of a2ps */
995   job = a2ps_job_new ();
996 
997   /* System's config.    */
998   a2_read_sys_config (job);
999 
1000   /* Personal config.: only when installed, because there may be
1001      incompatibilities between config files versions. */
1002   if (!getenv ("NO_HOME_CONF"))
1003     /* Home's */
1004     a2_read_config (job,
1005 		    macro_meta_sequence_get (job, VAR_USER_HOME),
1006 		    ".a2ps/a2psrc");
1007 
1008   /* Local config. */
1009   a2_read_config (job, ".", ".a2psrc");
1010 
1011   /* Prepare the sheets map structure and the hash table that receives
1012      yet read sheets. */
1013   sheets_map = sheets_map_new ();
1014   style_sheets = new_style_sheets ();
1015 
1016   /* Process the command line options. */
1017   argn = a2ps_handle_options (job, argc, argv);
1018 
1019   /* Once a2ps.cfg is read, finish the building of a2ps_job */
1020   a2ps_job_finalize (job);
1021 
1022   /* If we are debugging, then install a hook called after having
1023      generated the PostScript comments. */
1024   if (job->debug)
1025     ps_comment_hook = spy_user;
1026 
1027   /* Attach the arguments to the JOB */
1028   job->argv = argv;
1029   job->argc = argc;
1030 
1031   switch (behavior)
1032     {
1033     case b_guess:
1034       /* Act like file(1) does: report guessed ssh key */
1035       if (argn < argc)
1036 	for (; argn < argc; argn++)
1037 	  guess ((uchar *) argv[argn]);
1038       else
1039 	/* A guess is asked upon stdin */
1040 	guess (UNULL);
1041       break;
1042 
1043       /* FIXME: for expand, which, and glob, should we give an error
1044          when no arguments are given? */
1045 
1046     case b_expand:
1047       /* Expand the strings given as arguments. */
1048       for (; argn < argc; argn++)
1049 	{
1050 	  fputs (expand_user_string (job, FIRST_FILE (job),
1051 				     "--list=expand", (uchar *) argv[argn]),
1052 		 stdout);
1053 	  putc ('\n', stdout);
1054 	}
1055       break;
1056 
1057     case b_which:
1058       /* Look for the arguments in the library, and report the full
1059          paths. */
1060       for (; argn < argc; argn++)
1061 	{
1062 	  char *cp;
1063 	  cp = pw_find_file (job->common.path, (uchar *) argv[argn], NULL);
1064 	  if (cp)
1065 	    {
1066 	      fputs (cp, stdout);
1067 	      putc ('\n', stdout);
1068 	    }
1069 	}
1070       break;
1071 
1072     case b_glob:
1073       /* Glob the arguments in the library, and report the full
1074          paths. */
1075       for (; argn < argc; argn++)
1076 	pw_glob_print (job->common.path, (uchar *) argv[argn], stdout);
1077       break;
1078 
1079     case b_version:
1080       version_etc (stdout, NULL, GNU_PACKAGE, VERSION,
1081 		   "Akim Demaille, Miguel Santana");
1082       break;
1083 
1084     case b_help:
1085       usage (0);
1086       break;
1087 
1088     case b_list_options:
1089       list_options (job, stdout);
1090       break;
1091 
1092     case b_list_features:
1093       list_features (job, stdout);
1094       break;
1095 
1096     case b_list_media:
1097       list_media_long (job, stdout);
1098       break;
1099 
1100     case b_list_style_sheets:
1101       list_style_sheets_long (stdout);
1102       break;
1103 
1104     case b_list_html_style_sheets:
1105       /* This is done to ease the update of a2ps' web page */
1106       list_style_sheets_html (stdout);
1107       break;
1108 
1109     case b_list_texinfo_style_sheets:
1110       /* This is done to ease the update of a2ps' Texinfo doc */
1111       list_style_sheets_texinfo (stdout);
1112       break;
1113 
1114     case b_list_printers:
1115       a2ps_printers_list_long (job, stdout);
1116       break;
1117 
1118     case b_list_delegations:
1119       delegations_list_long (delegation_table, stdout);
1120       break;
1121 
1122     case b_list_macro_meta_sequences:
1123       macro_meta_sequences_list_long (job, stdout);
1124       break;
1125 
1126     case b_list_encodings:
1127       list_encodings_long (job, stdout);
1128       break;
1129 
1130     case b_list_texinfo_encodings:
1131       list_texinfo_encodings_long (job, stdout);
1132       break;
1133 
1134     case b_list_user_options:
1135       user_options_list_long (job, stdout);
1136       break;
1137 
1138     case b_list_prologues:
1139       prologues_list_long (job, stdout);
1140       break;
1141 
1142     case b_list_texinfo_prologues:
1143       prologues_list_texinfo (job, stdout);
1144       break;
1145 
1146     case b_list_ppd:
1147       /* Report PPD files     */
1148       a2ps_ppd_list_long (job, stdout);
1149       break;
1150 
1151       /*
1152        * Text to PostScript generator
1153        */
1154     case b_ps:
1155       {
1156 	/* Count the number of jobs done */
1157 	int delegated_jobs = 0, native_jobs = 0;
1158 
1159 	a2ps_open_output_session (job);
1160 
1161 	if (argn == argc)	/* Print stdin */
1162 	  print (UNULL, &native_jobs, &delegated_jobs);
1163 	else			/* Print following files */
1164 	  for (; argn < argc; argn++)
1165 	    print ((uchar *) argv[argn], &native_jobs, &delegated_jobs);
1166 
1167 	if (!IS_EMPTY (toc))
1168 	  print_toc ((uchar *) _("Table of Content"), toc, &native_jobs);
1169 
1170 	if ((native_jobs == 0) && (delegated_jobs == 1))
1171 	  {
1172 	    /* a2ps has only been used to delegate a single job.
1173 	     * Hence its prologue is superfluous */
1174 	    /* FIXME: if there were other files but which failed,
1175 	     * then there is _no_reason_ that the file we're interested
1176 	     * in is this one!
1177 	     * To this end, we need to put more information in file_job
1178 	     * on how its processing went. */
1179 	    a2ps_open_output_stream (job);
1180 	    pslex_dump (job->output_stream->fp,
1181 			CURRENT_FILE (job)->delegation_tmpname);
1182 	    unlink (CURRENT_FILE (job)->delegation_tmpname);
1183 	    a2ps_close_output_stream (job);
1184 	    msg_job_pages_printed (job);
1185 	  }
1186 	else if (native_jobs || delegated_jobs)
1187 	  {
1188 	    /* The whole stuff is needed */
1189 	    a2ps_close_output_session (job);
1190 	    msg_job_pages_printed (job);
1191 	  }
1192 	else
1193 	  {
1194 	    /* Nothing has been printed.
1195 	     * Don't close the job, so that nothing is sent to the printer,
1196 	     * not even the PS prologue */
1197 	    msg_nothing_printed ();
1198 	  }
1199       }
1200       break;
1201 
1202     default:
1203       /* A case has not been recognized. */
1204       abort ();
1205     }
1206 
1207   a2ps_job_free (job);
1208   job = NULL;
1209 
1210   return (EXIT_SUCCESS);
1211 }
1212