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