1 /* yat2m.c - Yet Another Texi 2 Man converter
2  *	Copyright (C) 2005, 2013, 2015, 2016, 2017 g10 Code GmbH
3  *      Copyright (C) 2006, 2008, 2011 Free Software Foundation, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <https://www.gnu.org/licenses/>.
17  */
18 
19 /*
20     This is a simple texinfo to man page converter.  It needs some
21     special markup in th e texinfo and tries best to get a create man
22     page.  It has been designed for the GnuPG man pages and thus only
23     a few texinfo commands are supported.
24 
25     To use this you need to add the following macros into your texinfo
26     source:
27 
28       @macro manpage {a}
29       @end macro
30       @macro mansect {a}
31       @end macro
32       @macro manpause
33       @end macro
34       @macro mancont
35       @end macro
36 
37     They are used by yat2m to select parts of the Texinfo which should
38     go into the man page. These macros need to be used without leading
39     left space. Processing starts after a "manpage" macro has been
40     seen.  "mansect" identifies the section and yat2m make sure to
41     emit the sections in the proper order.  Note that @mansect skips
42     the next input line if that line begins with @section, @subsection or
43     @chapheading.
44 
45     To insert verbatim troff markup, the following texinfo code may be
46     used:
47 
48       @ifset manverb
49       .B whateever you want
50       @end ifset
51 
52     alternativly a special comment may be used:
53 
54       @c man:.B whatever you want
55 
56     This is useful in case you need just one line. If you want to
57     include parts only in the man page but keep the texinfo
58     translation you may use:
59 
60       @ifset isman
61       stuff to be rendered only on man pages
62       @end ifset
63 
64     or to exclude stuff from man pages:
65 
66       @ifclear isman
67       stuff not to be rendered on man pages
68       @end ifclear
69 
70     the keyword @section is ignored, however @subsection gets rendered
71     as ".SS".  @menu is completely skipped. Several man pages may be
72     extracted from one file, either using the --store or the --select
73     option.
74 
75     If you want to indent tables in the source use this style:
76 
77       @table foo
78         @item
79         @item
80         @table
81           @item
82         @end
83       @end
84 
85     Don't change the indentation within a table and keep the same
86     number of white space at the start of the line.  yat2m simply
87     detects the number of white spaces in front of an @item and remove
88     this number of spaces from all following lines until a new @item
89     is found or there are less spaces than for the last @item.
90 
91     Note that @* does only work correctly if used at the end of an
92     input line.
93 
94 */
95 
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include <stddef.h>
99 #include <string.h>
100 #include <errno.h>
101 #include <stdarg.h>
102 #include <assert.h>
103 #include <ctype.h>
104 #include <time.h>
105 
106 
107 #if __GNUC__
108 # define MY_GCC_VERSION (__GNUC__ * 10000 \
109                          + __GNUC_MINOR__ * 100         \
110                          + __GNUC_PATCHLEVEL__)
111 #else
112 # define MY_GCC_VERSION 0
113 #endif
114 
115 #if MY_GCC_VERSION >= 20500
116 # define ATTR_PRINTF(f, a) __attribute__ ((format(printf,f,a)))
117 # define ATTR_NR_PRINTF(f, a) __attribute__ ((noreturn, format(printf,f,a)))
118 #else
119 # define ATTR_PRINTF(f, a)
120 # define ATTR_NR_PRINTF(f, a)
121 #endif
122 #if MY_GCC_VERSION >= 30200
123 # define ATTR_MALLOC  __attribute__ ((__malloc__))
124 #else
125 # define ATTR_MALLOC
126 #endif
127 
128 
129 
130 #define PGM "yat2m"
131 #ifdef PACKAGE_VERSION
132 # define VERSION PACKAGE_VERSION
133 #else
134 # define VERSION "1.0"
135 #endif
136 
137 /* The maximum length of a line including the linefeed and one extra
138    character. */
139 #define LINESIZE 1024
140 
141 /* Number of allowed condition nestings.  */
142 #define MAX_CONDITION_NESTING  10
143 
144 /* Option flags. */
145 static int verbose;
146 static int quiet;
147 static int debug;
148 static const char *opt_source;
149 static const char *opt_release;
150 static const char *opt_date;
151 static const char *opt_select;
152 static const char *opt_include;
153 static int opt_store;
154 
155 /* Flag to keep track whether any error occurred.  */
156 static int any_error;
157 
158 
159 /* Object to keep macro definitions.  */
160 struct macro_s
161 {
162   struct macro_s *next;
163   char *value;    /* Malloced value. */
164   char name[1];
165 };
166 typedef struct macro_s *macro_t;
167 
168 /* List of all defined macros. */
169 static macro_t macrolist;
170 
171 /* List of variables set by @set. */
172 static macro_t variablelist;
173 
174 /* List of global macro names.  The value part is not used.  */
175 static macro_t predefinedmacrolist;
176 
177 /* Object to keep track of @isset and @ifclear.  */
178 struct condition_s
179 {
180   int manverb;   /* "manverb" needs special treatment.  */
181   int isset;     /* This is an @isset condition.  */
182   char name[1];  /* Name of the condition macro.  */
183 };
184 typedef struct condition_s *condition_t;
185 
186 /* The stack used to evaluate conditions.  And the current states. */
187 static condition_t condition_stack[MAX_CONDITION_NESTING];
188 static int condition_stack_idx;
189 static int cond_is_active;     /* State of ifset/ifclear */
190 static int cond_in_verbatim;   /* State of "manverb".  */
191 
192 
193 /* Object to store one line of content.  */
194 struct line_buffer_s
195 {
196   struct line_buffer_s *next;
197   int verbatim;  /* True if LINE contains verbatim data.  The default
198                     is Texinfo source.  */
199   char *line;
200 };
201 typedef struct line_buffer_s *line_buffer_t;
202 
203 
204 /* Object to collect the data of a section.  */
205 struct section_buffer_s
206 {
207   char *name;           /* Malloced name of the section. This may be
208                            NULL to indicate this slot is not used.  */
209   line_buffer_t lines;  /* Linked list with the lines of the section.  */
210   line_buffer_t *lines_tail; /* Helper for faster appending to the
211                                 linked list.  */
212   line_buffer_t last_line;   /* Points to the last line appended.  */
213 };
214 typedef struct section_buffer_s *section_buffer_t;
215 
216 /* Variable to keep info about the current page together.  */
217 static struct
218 {
219   /* Filename of the current page or NULL if no page is active.  Malloced. */
220   char *name;
221 
222   /* Number of allocated elements in SECTIONS below.  */
223   size_t n_sections;
224   /* Array with the data of the sections.  */
225   section_buffer_t sections;
226 
227 } thepage;
228 
229 
230 /* The list of standard section names.  COMMANDS and ASSUAN are GnuPG
231    specific. */
232 static const char * const standard_sections[] =
233   { "NAME",  "SYNOPSIS",  "DESCRIPTION",
234     "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS",
235     "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES",
236     "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO",
237     "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL };
238 
239 
240 /*-- Local prototypes.  --*/
241 static void proc_texi_buffer (FILE *fp, const char *line, size_t len,
242                               int *table_level, int *eol_action);
243 
244 static void die (const char *format, ...) ATTR_NR_PRINTF(1,2);
245 static void err (const char *format, ...) ATTR_PRINTF(1,2);
246 static void inf (const char *format, ...) ATTR_PRINTF(1,2);
247 static void *xmalloc (size_t n) ATTR_MALLOC;
248 static void *xcalloc (size_t n, size_t m) ATTR_MALLOC;
249 
250 
251 
252 /*-- Functions --*/
253 
254 /* Print diagnostic message and exit with failure. */
255 static void
die(const char * format,...)256 die (const char *format, ...)
257 {
258   va_list arg_ptr;
259 
260   fflush (stdout);
261   fprintf (stderr, "%s: ", PGM);
262 
263   va_start (arg_ptr, format);
264   vfprintf (stderr, format, arg_ptr);
265   va_end (arg_ptr);
266   putc ('\n', stderr);
267 
268   exit (1);
269 }
270 
271 
272 /* Print diagnostic message. */
273 static void
err(const char * format,...)274 err (const char *format, ...)
275 {
276   va_list arg_ptr;
277 
278   fflush (stdout);
279   if (strncmp (format, "%s:%d:", 6))
280     fprintf (stderr, "%s: ", PGM);
281 
282   va_start (arg_ptr, format);
283   vfprintf (stderr, format, arg_ptr);
284   va_end (arg_ptr);
285   putc ('\n', stderr);
286   any_error = 1;
287 }
288 
289 /* Print diagnostic message. */
290 static void
inf(const char * format,...)291 inf (const char *format, ...)
292 {
293   va_list arg_ptr;
294 
295   fflush (stdout);
296   fprintf (stderr, "%s: ", PGM);
297 
298   va_start (arg_ptr, format);
299   vfprintf (stderr, format, arg_ptr);
300   va_end (arg_ptr);
301   putc ('\n', stderr);
302 }
303 
304 
305 static void *
xmalloc(size_t n)306 xmalloc (size_t n)
307 {
308   void *p = malloc (n);
309   if (!p)
310     die ("out of core: %s", strerror (errno));
311   return p;
312 }
313 
314 static void *
xcalloc(size_t n,size_t m)315 xcalloc (size_t n, size_t m)
316 {
317   void *p = calloc (n, m);
318   if (!p)
319     die ("out of core: %s", strerror (errno));
320   return p;
321 }
322 
323 static void *
xrealloc(void * old,size_t n)324 xrealloc (void *old, size_t n)
325 {
326   void *p = realloc (old, n);
327   if (!p)
328     die ("out of core: %s", strerror (errno));
329   return p;
330 }
331 
332 static char *
xstrdup(const char * string)333 xstrdup (const char *string)
334 {
335   void *p = malloc (strlen (string)+1);
336   if (!p)
337     die ("out of core: %s", strerror (errno));
338   strcpy (p, string);
339   return p;
340 }
341 
342 
343 /* Uppercase the ascii characters in STRING.  */
344 static char *
ascii_strupr(char * string)345 ascii_strupr (char *string)
346 {
347   char *p;
348 
349   for (p = string; *p; p++)
350     if (!(*p & 0x80))
351       *p = toupper (*p);
352   return string;
353 }
354 
355 
356 /* Return the current date as an ISO string.  */
357 const char *
isodatestring(void)358 isodatestring (void)
359 {
360   static char buffer[36];
361   struct tm *tp;
362   time_t atime;
363 
364   if (opt_date && *opt_date)
365     atime = strtoul (opt_date, NULL, 10);
366   else
367     atime = time (NULL);
368   if (atime < 0)
369     strcpy (buffer, "????" "-??" "-??");
370   else
371     {
372       tp = gmtime (&atime);
373       sprintf (buffer,"%04d-%02d-%02d",
374                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
375     }
376   return buffer;
377 }
378 
379 
380 /* Add NAME to the list of predefined macros which are global for all
381    files.  */
382 static void
add_predefined_macro(const char * name)383 add_predefined_macro (const char *name)
384 {
385   macro_t m;
386 
387   for (m=predefinedmacrolist; m; m = m->next)
388     if (!strcmp (m->name, name))
389       break;
390   if (!m)
391     {
392       m = xcalloc (1, sizeof *m + strlen (name));
393       strcpy (m->name, name);
394       m->next = predefinedmacrolist;
395       predefinedmacrolist = m;
396     }
397 }
398 
399 
400 /* Create or update a macro with name MACRONAME and set its values TO
401    MACROVALUE.  Note that ownership of the macro value is transferred
402    to this function.  */
403 static void
set_macro(const char * macroname,char * macrovalue)404 set_macro (const char *macroname, char *macrovalue)
405 {
406   macro_t m;
407 
408   for (m=macrolist; m; m = m->next)
409     if (!strcmp (m->name, macroname))
410       break;
411   if (m)
412     free (m->value);
413   else
414     {
415       m = xcalloc (1, sizeof *m + strlen (macroname));
416       strcpy (m->name, macroname);
417       m->next = macrolist;
418       macrolist = m;
419     }
420   m->value = macrovalue;
421   macrovalue = NULL;
422 }
423 
424 
425 /* Create or update a variable with name and value given in NAMEANDVALUE.  */
426 static void
set_variable(char * nameandvalue)427 set_variable (char *nameandvalue)
428 {
429   macro_t m;
430   const char *value;
431   char *p;
432 
433   for (p = nameandvalue; *p && *p != ' ' && *p != '\t'; p++)
434     ;
435   if (!*p)
436     value = "";
437   else
438     {
439       *p++ = 0;
440       while (*p == ' ' || *p == '\t')
441         p++;
442       value = p;
443     }
444 
445   for (m=variablelist; m; m = m->next)
446     if (!strcmp (m->name, nameandvalue))
447       break;
448   if (m)
449     free (m->value);
450   else
451     {
452       m = xcalloc (1, sizeof *m + strlen (nameandvalue));
453       strcpy (m->name, nameandvalue);
454       m->next = variablelist;
455       variablelist = m;
456     }
457   m->value = xstrdup (value);
458 }
459 
460 
461 /* Return true if the macro or variable NAME is set, i.e. not the
462    empty string and not evaluating to 0.  */
463 static int
macro_set_p(const char * name)464 macro_set_p (const char *name)
465 {
466   macro_t m;
467 
468   for (m = macrolist; m ; m = m->next)
469     if (!strcmp (m->name, name))
470       break;
471   if (!m)
472     for (m = variablelist; m ; m = m->next)
473       if (!strcmp (m->name, name))
474         break;
475   if (!m || !m->value || !*m->value)
476     return 0;
477   if ((*m->value & 0x80) || !isdigit (*m->value))
478     return 1; /* Not a digit but some other string.  */
479   return !!atoi (m->value);
480 }
481 
482 
483 /* Evaluate the current conditions.  */
484 static void
evaluate_conditions(const char * fname,int lnr)485 evaluate_conditions (const char *fname, int lnr)
486 {
487   int i;
488 
489   (void)fname;
490   (void)lnr;
491 
492   /* for (i=0; i < condition_stack_idx; i++) */
493   /*   inf ("%s:%d:   stack[%d] %s %s %c", */
494   /*        fname, lnr, i, condition_stack[i]->isset? "set":"clr", */
495   /*        condition_stack[i]->name, */
496   /*        (macro_set_p (condition_stack[i]->name) */
497   /*         ^ !condition_stack[i]->isset)? 't':'f'); */
498 
499   cond_is_active = 1;
500   cond_in_verbatim = 0;
501   if (condition_stack_idx)
502     {
503       for (i=0; i < condition_stack_idx; i++)
504         {
505           if (condition_stack[i]->manverb)
506             cond_in_verbatim = (macro_set_p (condition_stack[i]->name)
507                                 ^ !condition_stack[i]->isset);
508           else if (!(macro_set_p (condition_stack[i]->name)
509                      ^ !condition_stack[i]->isset))
510             {
511               cond_is_active = 0;
512               break;
513             }
514         }
515     }
516 
517   /* inf ("%s:%d:   active=%d verbatim=%d", */
518   /*      fname, lnr, cond_is_active, cond_in_verbatim); */
519 }
520 
521 
522 /* Push a condition with condition macro NAME onto the stack.  If
523    ISSET is true, a @isset condition is pushed.  */
524 static void
push_condition(const char * name,int isset,const char * fname,int lnr)525 push_condition (const char *name, int isset, const char *fname, int lnr)
526 {
527   condition_t cond;
528   int manverb = 0;
529 
530   if (condition_stack_idx >= MAX_CONDITION_NESTING)
531     {
532       err ("%s:%d: condition nested too deep", fname, lnr);
533       return;
534     }
535 
536   if (!strcmp (name, "manverb"))
537     {
538       if (!isset)
539         {
540           err ("%s:%d: using \"@ifclear manverb\" is not allowed", fname, lnr);
541           return;
542         }
543       manverb = 1;
544     }
545 
546   cond = xcalloc (1, sizeof *cond + strlen (name));
547   cond->manverb = manverb;
548   cond->isset = isset;
549   strcpy (cond->name, name);
550 
551   condition_stack[condition_stack_idx++] = cond;
552   evaluate_conditions (fname, lnr);
553 }
554 
555 
556 /* Remove the last condition from the stack.  ISSET is used for error
557    reporting.  */
558 static void
pop_condition(int isset,const char * fname,int lnr)559 pop_condition (int isset, const char *fname, int lnr)
560 {
561   if (!condition_stack_idx)
562     {
563       err ("%s:%d: unbalanced \"@end %s\"",
564            fname, lnr, isset?"isset":"isclear");
565       return;
566     }
567   condition_stack_idx--;
568   free (condition_stack[condition_stack_idx]);
569   condition_stack[condition_stack_idx] = NULL;
570   evaluate_conditions (fname, lnr);
571 }
572 
573 
574 
575 /* Return a section buffer for the section NAME.  Allocate a new buffer
576    if this is a new section.  Keep track of the sections in THEPAGE.
577    This function may reallocate the section array in THEPAGE.  */
578 static section_buffer_t
get_section_buffer(const char * name)579 get_section_buffer (const char *name)
580 {
581   int i;
582   section_buffer_t sect;
583 
584   /* If there is no section we put everything into the required NAME
585      section.  Given that this is the first one listed it is likely
586      that error are easily visible.  */
587   if (!name)
588     name = "NAME";
589 
590   for (i=0; i < thepage.n_sections; i++)
591     {
592       sect = thepage.sections + i;
593       if (sect->name && !strcmp (name, sect->name))
594         return sect;
595     }
596   for (i=0; i < thepage.n_sections; i++)
597     if (!thepage.sections[i].name)
598       break;
599   if (thepage.n_sections && i < thepage.n_sections)
600     sect = thepage.sections + i;
601   else
602     {
603       /* We need to allocate or reallocate the section array.  */
604       size_t old_n = thepage.n_sections;
605       size_t new_n = 20;
606 
607       if (!old_n)
608         thepage.sections = xcalloc (new_n, sizeof *thepage.sections);
609       else
610         {
611           thepage.sections = xrealloc (thepage.sections,
612                                        ((old_n + new_n)
613                                         * sizeof *thepage.sections));
614           memset (thepage.sections + old_n, 0,
615                   new_n * sizeof *thepage.sections);
616         }
617       thepage.n_sections += new_n;
618 
619       /* Setup the tail pointers.  */
620       for (i=old_n; i < thepage.n_sections; i++)
621         {
622           sect = thepage.sections + i;
623           sect->lines_tail = &sect->lines;
624         }
625       sect = thepage.sections + old_n;
626     }
627 
628   /* Store the name.  */
629   assert (!sect->name);
630   sect->name = xstrdup (name);
631   return sect;
632 }
633 
634 
635 
636 /* Add the content of LINE to the section named SECTNAME.  */
637 static void
add_content(const char * sectname,char * line,int verbatim)638 add_content (const char *sectname, char *line, int verbatim)
639 {
640   section_buffer_t sect;
641   line_buffer_t lb;
642 
643   sect = get_section_buffer (sectname);
644   if (sect->last_line && !sect->last_line->verbatim == !verbatim)
645     {
646       /* Lets append that line to the last one.  We do this to keep
647          all lines of the same kind (i.e.verbatim or not) together in
648          one large buffer.  */
649       size_t n1, n;
650 
651       lb = sect->last_line;
652       n1 = strlen (lb->line);
653       n = n1 + 1 + strlen (line) + 1;
654       lb->line = xrealloc (lb->line, n);
655       strcpy (lb->line+n1, "\n");
656       strcpy (lb->line+n1+1, line);
657     }
658   else
659     {
660       lb = xcalloc (1, sizeof *lb);
661       lb->verbatim = verbatim;
662       lb->line = xstrdup (line);
663       sect->last_line = lb;
664       *sect->lines_tail = lb;
665       sect->lines_tail = &lb->next;
666     }
667 }
668 
669 
670 /* Prepare for a new man page using the filename NAME. */
671 static void
start_page(char * name)672 start_page (char *name)
673 {
674   if (verbose)
675     inf ("starting page '%s'", name);
676   assert (!thepage.name);
677   thepage.name = xstrdup (name);
678   thepage.n_sections = 0;
679 }
680 
681 
682 /* Write the .TH entry of the current page.  Return -1 if there is a
683    problem with the page. */
684 static int
write_th(FILE * fp)685 write_th (FILE *fp)
686 {
687   char *name, *p;
688 
689   fputs (".\\\" Created from Texinfo source by yat2m " VERSION "\n", fp);
690 
691   name = ascii_strupr (xstrdup (thepage.name));
692   p = strrchr (name, '.');
693   if (!p || !p[1])
694     {
695       err ("no section name in man page '%s'", thepage.name);
696       free (name);
697       return -1;
698     }
699   *p++ = 0;
700   fprintf (fp, ".TH %s %s %s \"%s\" \"%s\"\n",
701            name, p, isodatestring (), opt_release, opt_source);
702   free (name);
703   return 0;
704 }
705 
706 
707 /* Process the texinfo command COMMAND (without the leading @) and
708    write output if needed to FP. REST is the remainer of the line
709    which should either point to an opening brace or to a white space.
710    The function returns the number of characters already processed
711    from REST.  LEN is the usable length of REST.  TABLE_LEVEL is used to
712    control the indentation of tables.  */
713 static size_t
proc_texi_cmd(FILE * fp,const char * command,const char * rest,size_t len,int * table_level,int * eol_action)714 proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len,
715                int *table_level, int *eol_action)
716 {
717   static struct {
718     const char *name;    /* Name of the command.  */
719     int what;            /* What to do with this command. */
720     const char *lead_in; /* String to print with a opening brace.  */
721     const char *lead_out;/* String to print with the closing brace. */
722   } cmdtbl[] = {
723     { "command", 0, "\\fB", "\\fR" },
724     { "code",    0, "\\fB", "\\fR" },
725     { "url",     0, "\\fB", "\\fR" },
726     { "sc",      0, "\\fB", "\\fR" },
727     { "var",     0, "\\fI", "\\fR" },
728     { "samp",    0, "\\(aq", "\\(aq"  },
729     { "file",    0, "\\(oq\\fI","\\fR\\(cq" },
730     { "env",     0, "\\(oq\\fI","\\fR\\(cq" },
731     { "acronym", 0 },
732     { "dfn",     0 },
733     { "option",  0, "\\fB", "\\fR"   },
734     { "example", 1, ".RS 2\n.nf\n" },
735     { "smallexample", 1, ".RS 2\n.nf\n" },
736     { "asis",    7 },
737     { "anchor",  7 },
738     { "cartouche", 1 },
739     { "ref",     0, "[", "]" },
740     { "xref",    0, "See: [", "]" },
741     { "pxref",   0, "see: [", "]" },
742     { "uref",    0, "(\\fB", "\\fR)" },
743     { "footnote",0, " ([", "])" },
744     { "emph",    0, "\\fI", "\\fR" },
745     { "w",       1 },
746     { "c",       5 },
747     { "efindex", 1 },
748     { "opindex", 1 },
749     { "cpindex", 1 },
750     { "cindex",  1 },
751     { "noindent", 0 },
752     { "section", 1 },
753     { "chapter", 1 },
754     { "subsection", 6, "\n.SS " },
755     { "chapheading", 0},
756     { "item",    2, ".TP\n.B " },
757     { "itemx",   2, ".TQ\n.B " },
758     { "table",   3 },
759     { "itemize",   3 },
760     { "bullet",  0, "* " },
761     { "*",       0, "\n.br"},
762     { "/",       0 },
763     { "end",     4 },
764     { "quotation",1, ".RS\n\\fB" },
765     { "value", 8 },
766     { NULL }
767   };
768   size_t n;
769   int i;
770   const char *s;
771   const char *lead_out = NULL;
772   int ignore_args = 0;
773 
774   for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
775     ;
776   if (cmdtbl[i].name)
777     {
778       s = cmdtbl[i].lead_in;
779       if (s)
780         fputs (s, fp);
781       lead_out = cmdtbl[i].lead_out;
782       switch (cmdtbl[i].what)
783         {
784         case 1: /* Throw away the entire line.  */
785           s = memchr (rest, '\n', len);
786           return s? (s-rest)+1 : len;
787         case 2: /* Handle @item.  */
788           break;
789         case 3: /* Handle table.  */
790           if (++(*table_level) > 1)
791             fputs (".RS\n", fp);
792           /* Now throw away the entire line. */
793           s = memchr (rest, '\n', len);
794           return s? (s-rest)+1 : len;
795           break;
796         case 4: /* Handle end.  */
797           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
798             ;
799           if (n >= 5 && !memcmp (s, "table", 5)
800               && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
801             {
802               if ((*table_level)-- > 1)
803                 fputs (".RE\n", fp);
804               else
805                 fputs (".P\n", fp);
806             }
807           else if (n >= 7 && !memcmp (s, "example", 7)
808               && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
809             {
810               fputs (".fi\n.RE\n", fp);
811             }
812           else if (n >= 12 && !memcmp (s, "smallexample", 12)
813               && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
814             {
815               fputs (".fi\n.RE\n", fp);
816             }
817           else if (n >= 9 && !memcmp (s, "quotation", 9)
818               && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
819             {
820               fputs ("\\fR\n.RE\n", fp);
821             }
822           /* Now throw away the entire line. */
823           s = memchr (rest, '\n', len);
824           return s? (s-rest)+1 : len;
825         case 5: /* Handle special comments. */
826           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
827             ;
828           if (n >= 4 && !memcmp (s, "man:", 4))
829             {
830               for (s+=4, n-=4; n && *s != '\n'; n--, s++)
831                 putc (*s, fp);
832               putc ('\n', fp);
833             }
834           /* Now throw away the entire line. */
835           s = memchr (rest, '\n', len);
836           return s? (s-rest)+1 : len;
837         case 6:
838           *eol_action = 1;
839           break;
840         case 7:
841           ignore_args = 1;
842           break;
843         case 8:
844           ignore_args = 1;
845           if (*rest != '{')
846             {
847               err ("opening brace for command '%s' missing", command);
848               return len;
849             }
850           else
851             {
852               /* Find closing brace.  */
853               for (s=rest+1, n=1; *s && n < len; s++, n++)
854                 if (*s == '}')
855                   break;
856               if (*s != '}')
857                 {
858                   err ("closing brace for command '%s' not found", command);
859                   return len;
860                 }
861               else
862                 {
863                   size_t rlen = s - (rest + 1);
864                   macro_t m;
865 
866                   for (m = variablelist; m; m = m->next)
867                     {
868                       if (strlen (m->name) == rlen
869                           && !strncmp (m->name, rest+1, rlen))
870                         break;
871                     }
872                   if (m)
873                     fputs (m->value, fp);
874                   else
875                     inf ("texinfo variable '%.*s' is not set",
876                          (int)rlen, rest+1);
877                 }
878             }
879           break;
880         default:
881           break;
882         }
883     }
884   else /* macro */
885     {
886       macro_t m;
887 
888       for (m = macrolist; m ; m = m->next)
889         if (!strcmp (m->name, command))
890             break;
891       if (m)
892         {
893           proc_texi_buffer (fp, m->value, strlen (m->value),
894                             table_level, eol_action);
895           ignore_args = 1; /* Parameterized macros are not yet supported. */
896         }
897       else
898         inf ("texinfo command '%s' not supported (%.*s)", command,
899              (int)((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
900     }
901 
902   if (*rest == '{')
903     {
904       /* Find matching closing brace.  */
905       for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
906         if (*s == '{')
907           i++;
908         else if (*s == '}')
909           i--;
910       if (i)
911         {
912           err ("closing brace for command '%s' not found", command);
913           return len;
914         }
915       if (n > 2 && !ignore_args)
916         proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
917     }
918   else
919     n = 0;
920 
921   if (lead_out)
922     fputs (lead_out, fp);
923 
924   return n;
925 }
926 
927 
928 
929 /* Process the string LINE with LEN bytes of Texinfo content. */
930 static void
proc_texi_buffer(FILE * fp,const char * line,size_t len,int * table_level,int * eol_action)931 proc_texi_buffer (FILE *fp, const char *line, size_t len,
932                   int *table_level, int *eol_action)
933 {
934   const char *s;
935   char cmdbuf[256];
936   int cmdidx = 0;
937   int in_cmd = 0;
938   size_t n;
939 
940   for (s=line; *s && len; s++, len--)
941     {
942       if (in_cmd)
943         {
944           if (in_cmd == 1)
945             {
946               switch (*s)
947                 {
948                 case '@': case '{': case '}':
949                   putc (*s, fp); in_cmd = 0;
950                   break;
951                 case ':': /* Not ending a sentence flag.  */
952                   in_cmd = 0;
953                   break;
954                 case '.': case '!': case '?': /* Ending a sentence. */
955                   putc (*s, fp); in_cmd = 0;
956                   break;
957                 case ' ': case '\t': case '\n': /* Non collapsing spaces.  */
958                   putc (*s, fp); in_cmd = 0;
959                   break;
960                 default:
961                   cmdidx = 0;
962                   cmdbuf[cmdidx++] = *s;
963                   in_cmd++;
964                   break;
965                 }
966             }
967           else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
968             {
969               cmdbuf[cmdidx] = 0;
970               n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
971               assert (n <= len);
972               s += n; len -= n;
973               s--; len++;
974               in_cmd = 0;
975             }
976           else if (cmdidx < sizeof cmdbuf -1)
977             cmdbuf[cmdidx++] = *s;
978           else
979             {
980               err ("texinfo command too long - ignored");
981               in_cmd = 0;
982             }
983         }
984       else if (*s == '@')
985         in_cmd = 1;
986       else if (*s == '\n')
987         {
988           switch (*eol_action)
989             {
990             case 1: /* Create a dummy paragraph. */
991               fputs ("\n\\ \n", fp);
992               break;
993             default:
994               putc (*s, fp);
995             }
996           *eol_action = 0;
997         }
998       else if (*s == '\\')
999         fputs ("\\\\", fp);
1000       else
1001         putc (*s, fp);
1002     }
1003 
1004   if (in_cmd > 1)
1005     {
1006       cmdbuf[cmdidx] = 0;
1007       n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
1008       assert (n <= len);
1009       s += n; len -= n;
1010       s--; len++;
1011       /* in_cmd = 0; -- doc only */
1012     }
1013 }
1014 
1015 
1016 /* Do something with the Texinfo line LINE.  */
1017 static void
parse_texi_line(FILE * fp,const char * line,int * table_level)1018 parse_texi_line (FILE *fp, const char *line, int *table_level)
1019 {
1020   int eol_action = 0;
1021 
1022   /* A quick test whether there are any texinfo commands.  */
1023   if (!strchr (line, '@'))
1024     {
1025       fputs (line, fp);
1026       putc ('\n', fp);
1027       return;
1028     }
1029   proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
1030   putc ('\n', fp);
1031 }
1032 
1033 
1034 /* Write all the lines LINES to FP.  */
1035 static void
write_content(FILE * fp,line_buffer_t lines)1036 write_content (FILE *fp, line_buffer_t lines)
1037 {
1038   line_buffer_t line;
1039   int table_level = 0;
1040 
1041   for (line = lines; line; line = line->next)
1042     {
1043       if (line->verbatim)
1044         {
1045           fputs (line->line, fp);
1046           putc ('\n', fp);
1047         }
1048       else
1049         {
1050 /*           fputs ("TEXI---", fp); */
1051 /*           fputs (line->line, fp); */
1052 /*           fputs ("---\n", fp); */
1053           parse_texi_line (fp, line->line, &table_level);
1054         }
1055     }
1056 }
1057 
1058 
1059 
1060 static int
is_standard_section(const char * name)1061 is_standard_section (const char *name)
1062 {
1063   int i;
1064   const char *s;
1065 
1066   for (i=0; (s=standard_sections[i]); i++)
1067     if (!strcmp (s, name))
1068       return 1;
1069   return 0;
1070 }
1071 
1072 
1073 /* Finish a page; that is sort the data and write it out to the file.  */
1074 static void
finish_page(void)1075 finish_page (void)
1076 {
1077   FILE *fp;
1078   section_buffer_t sect = NULL;
1079   int idx;
1080   const char *s;
1081   int i;
1082 
1083   if (!thepage.name)
1084     return; /* No page active.  */
1085 
1086   if (verbose)
1087     inf ("finishing page '%s'", thepage.name);
1088 
1089   if (opt_select)
1090     {
1091       if (!strcmp (opt_select, thepage.name))
1092         {
1093           inf ("selected '%s'", thepage.name );
1094           fp = stdout;
1095         }
1096       else
1097         {
1098           fp = fopen ( "/dev/null", "w" );
1099           if (!fp)
1100             die ("failed to open /dev/null: %s\n", strerror (errno));
1101         }
1102     }
1103   else if (opt_store)
1104     {
1105       inf ("writing '%s'", thepage.name );
1106       fp = fopen ( thepage.name, "w" );
1107       if (!fp)
1108         die ("failed to create '%s': %s\n", thepage.name, strerror (errno));
1109     }
1110   else
1111     fp = stdout;
1112 
1113   if (write_th (fp))
1114     goto leave;
1115 
1116   for (idx=0; (s=standard_sections[idx]); idx++)
1117     {
1118       for (i=0; i < thepage.n_sections; i++)
1119         {
1120           sect = thepage.sections + i;
1121           if (sect->name && !strcmp (s, sect->name))
1122             break;
1123         }
1124       if (i == thepage.n_sections)
1125         sect = NULL;
1126 
1127       if (sect)
1128         {
1129           fprintf (fp, ".SH %s\n", sect->name);
1130           write_content (fp, sect->lines);
1131           /* Now continue with all non standard sections directly
1132              following this one. */
1133           for (i++; i < thepage.n_sections; i++)
1134             {
1135               sect = thepage.sections + i;
1136               if (sect->name && is_standard_section (sect->name))
1137                 break;
1138               if (sect->name)
1139                 {
1140                   fprintf (fp, ".SH %s\n", sect->name);
1141                   write_content (fp, sect->lines);
1142                 }
1143             }
1144 
1145         }
1146     }
1147 
1148 
1149  leave:
1150   if (fp != stdout)
1151     fclose (fp);
1152   free (thepage.name);
1153   thepage.name = NULL;
1154   /* FIXME: Cleanup the content.  */
1155 }
1156 
1157 
1158 
1159 
1160 /* Parse one Texinfo file and create manpages according to the
1161    embedded instructions.  */
1162 static void
parse_file(const char * fname,FILE * fp,char ** section_name,int in_pause)1163 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
1164 {
1165   char *line;
1166   int lnr = 0;
1167   /* Fixme: The following state variables don't carry over to include
1168      files. */
1169   int skip_to_end = 0;        /* Used to skip over menu entries. */
1170   int skip_sect_line = 0;     /* Skip after @mansect.  */
1171   int item_indent = 0;        /* How far is the current @item indented.  */
1172 
1173   /* Helper to define a macro. */
1174   char *macroname = NULL;
1175   char *macrovalue = NULL;
1176   size_t macrovaluesize = 0;
1177   size_t macrovalueused = 0;
1178 
1179   line = xmalloc (LINESIZE);
1180   while (fgets (line, LINESIZE, fp))
1181     {
1182       size_t n = strlen (line);
1183       int got_line = 0;
1184       char *p, *pend;
1185 
1186       lnr++;
1187       if (!n || line[n-1] != '\n')
1188         {
1189           err ("%s:%d: trailing linefeed missing, line too long or "
1190                "embedded Nul character", fname, lnr);
1191           break;
1192         }
1193       line[--n] = 0;
1194 
1195       /* Kludge to allow indentation of tables.  */
1196       for (p=line; *p == ' ' || *p == '\t'; p++)
1197         ;
1198       if (*p)
1199         {
1200           if (*p == '@' && !strncmp (p+1, "item", 4))
1201             item_indent = p - line;  /* Set a new indent level.  */
1202           else if (p - line < item_indent)
1203             item_indent = 0;         /* Switch off indention.  */
1204 
1205           if (item_indent)
1206             {
1207               memmove (line, line+item_indent, n - item_indent + 1);
1208               n -= item_indent;
1209             }
1210         }
1211 
1212 
1213       if (*line == '@')
1214         {
1215           for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
1216             n++;
1217           while (*p == ' ' || *p == '\t')
1218             p++;
1219         }
1220       else
1221         p = line;
1222 
1223       /* Take action on macro.  */
1224       if (macroname)
1225         {
1226           if (n == 4 && !memcmp (line, "@end", 4)
1227               && (line[4]==' '||line[4]=='\t'||!line[4])
1228               && !strncmp (p, "macro", 5)
1229               && (p[5]==' '||p[5]=='\t'||!p[5]))
1230             {
1231               if (macrovalueused)
1232                 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
1233               macrovalue[macrovalueused] = 0;     /* Terminate macro. */
1234               macrovalue = xrealloc (macrovalue, macrovalueused+1);
1235 
1236               set_macro (macroname, macrovalue);
1237               macrovalue = NULL;
1238               free (macroname);
1239               macroname = NULL;
1240             }
1241           else
1242             {
1243               if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
1244                 {
1245                   macrovaluesize += strlen (line) + 256;
1246                   macrovalue = xrealloc (macrovalue,  macrovaluesize);
1247                 }
1248               strcpy (macrovalue+macrovalueused, line);
1249               macrovalueused += strlen (line);
1250               macrovalue[macrovalueused++] = '\n';
1251             }
1252           continue;
1253         }
1254 
1255 
1256       if (n >= 5 && !memcmp (line, "@node", 5)
1257           && (line[5]==' '||line[5]=='\t'||!line[5]))
1258         {
1259           /* Completey ignore @node lines.  */
1260           continue;
1261         }
1262 
1263 
1264       if (skip_sect_line)
1265         {
1266           skip_sect_line = 0;
1267           if (!strncmp (line, "@section", 8)
1268               || !strncmp (line, "@subsection", 11)
1269               || !strncmp (line, "@chapheading", 12))
1270             continue;
1271         }
1272 
1273       /* We only parse lines we need and ignore the rest.  There are a
1274          few macros used to control this as well as one @ifset
1275          command.  Parts we know about are saved away into containers
1276          separate for each section. */
1277 
1278       /* First process ifset/ifclear commands. */
1279       if (*line == '@')
1280         {
1281           if (n == 6 && !memcmp (line, "@ifset", 6)
1282                    && (line[6]==' '||line[6]=='\t'))
1283             {
1284               for (p=line+7; *p == ' ' || *p == '\t'; p++)
1285                 ;
1286               if (!*p)
1287                 {
1288                   err ("%s:%d: name missing after \"@ifset\"", fname, lnr);
1289                   continue;
1290                 }
1291               for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1292                 ;
1293               *pend = 0;  /* Ignore rest of the line.  */
1294               push_condition (p, 1, fname, lnr);
1295               continue;
1296             }
1297           else if (n == 8 && !memcmp (line, "@ifclear", 8)
1298                    && (line[8]==' '||line[8]=='\t'))
1299             {
1300               for (p=line+9; *p == ' ' || *p == '\t'; p++)
1301                 ;
1302               if (!*p)
1303                 {
1304                   err ("%s:%d: name missing after \"@ifsclear\"", fname, lnr);
1305                   continue;
1306                 }
1307               for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1308                 ;
1309               *pend = 0;  /* Ignore rest of the line.  */
1310               push_condition (p, 0, fname, lnr);
1311               continue;
1312             }
1313           else if (n == 4 && !memcmp (line, "@end", 4)
1314                    && (line[4]==' '||line[4]=='\t')
1315                    && !strncmp (p, "ifset", 5)
1316                    && (p[5]==' '||p[5]=='\t'||!p[5]))
1317             {
1318               pop_condition (1, fname, lnr);
1319               continue;
1320             }
1321           else if (n == 4 && !memcmp (line, "@end", 4)
1322                    && (line[4]==' '||line[4]=='\t')
1323                    && !strncmp (p, "ifclear", 7)
1324                    && (p[7]==' '||p[7]=='\t'||!p[7]))
1325             {
1326               pop_condition (0, fname, lnr);
1327               continue;
1328             }
1329         }
1330 
1331       /* Take action on ifset/ifclear.  */
1332       if (!cond_is_active)
1333         continue;
1334 
1335       /* Process commands. */
1336       if (*line == '@')
1337         {
1338           if (skip_to_end
1339               && n == 4 && !memcmp (line, "@end", 4)
1340               && (line[4]==' '||line[4]=='\t'||!line[4]))
1341             {
1342               skip_to_end = 0;
1343             }
1344           else if (cond_in_verbatim)
1345             {
1346                 got_line = 1;
1347             }
1348           else if (n == 6 && !memcmp (line, "@macro", 6))
1349             {
1350               macroname = xstrdup (p);
1351               macrovalue = xmalloc ((macrovaluesize = 1024));
1352               macrovalueused = 0;
1353             }
1354           else if (n == 4 && !memcmp (line, "@set", 4))
1355             {
1356               set_variable (p);
1357             }
1358           else if (n == 8 && !memcmp (line, "@manpage", 8))
1359             {
1360               free (*section_name);
1361               *section_name = NULL;
1362               finish_page ();
1363               start_page (p);
1364               in_pause = 0;
1365             }
1366           else if (n == 8 && !memcmp (line, "@mansect", 8))
1367             {
1368               if (!thepage.name)
1369                 err ("%s:%d: section outside of a man page", fname, lnr);
1370               else
1371                 {
1372                   free (*section_name);
1373                   *section_name = ascii_strupr (xstrdup (p));
1374                   in_pause = 0;
1375                   skip_sect_line = 1;
1376                 }
1377             }
1378           else if (n == 9 && !memcmp (line, "@manpause", 9))
1379             {
1380               if (!*section_name)
1381                 err ("%s:%d: pausing outside of a man section", fname, lnr);
1382               else if (in_pause)
1383                 err ("%s:%d: already pausing", fname, lnr);
1384               else
1385                 in_pause = 1;
1386             }
1387           else if (n == 8 && !memcmp (line, "@mancont", 8))
1388             {
1389               if (!*section_name)
1390                 err ("%s:%d: continue outside of a man section", fname, lnr);
1391               else if (!in_pause)
1392                 err ("%s:%d: continue while not pausing", fname, lnr);
1393               else
1394                 in_pause = 0;
1395             }
1396           else if (n == 5 && !memcmp (line, "@menu", 5)
1397                    && (line[5]==' '||line[5]=='\t'||!line[5]))
1398             {
1399               skip_to_end = 1;
1400             }
1401           else if (n == 8 && !memcmp (line, "@include", 8)
1402                    && (line[8]==' '||line[8]=='\t'||!line[8]))
1403             {
1404               char *incname = xstrdup (p);
1405               FILE *incfp = fopen (incname, "r");
1406 
1407               if (!incfp && opt_include && *opt_include && *p != '/')
1408                 {
1409                   free (incname);
1410                   incname = xmalloc (strlen (opt_include) + 1
1411                                      + strlen (p) + 1);
1412                   strcpy (incname, opt_include);
1413                   if ( incname[strlen (incname)-1] != '/' )
1414                     strcat (incname, "/");
1415                   strcat (incname, p);
1416                   incfp = fopen (incname, "r");
1417                 }
1418 
1419               if (!incfp)
1420                 err ("can't open include file '%s': %s",
1421                      incname, strerror (errno));
1422               else
1423                 {
1424                   parse_file (incname, incfp, section_name, in_pause);
1425                   fclose (incfp);
1426                 }
1427               free (incname);
1428             }
1429           else if (n == 4 && !memcmp (line, "@bye", 4)
1430                    && (line[4]==' '||line[4]=='\t'||!line[4]))
1431             {
1432               break;
1433             }
1434           else if (!skip_to_end)
1435             got_line = 1;
1436         }
1437       else if (!skip_to_end)
1438         got_line = 1;
1439 
1440       if (got_line && cond_in_verbatim)
1441         add_content (*section_name, line, 1);
1442       else if (got_line && thepage.name && *section_name && !in_pause)
1443         add_content (*section_name, line, 0);
1444 
1445     }
1446   if (ferror (fp))
1447     err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1448   free (macroname);
1449   free (macrovalue);
1450   free (line);
1451 }
1452 
1453 
1454 static void
top_parse_file(const char * fname,FILE * fp)1455 top_parse_file (const char *fname, FILE *fp)
1456 {
1457   char *section_name = NULL;  /* Name of the current section or NULL
1458                                  if not in a section.  */
1459   macro_t m;
1460 
1461   while (macrolist)
1462     {
1463       macro_t next = macrolist->next;
1464       free (macrolist->value);
1465       free (macrolist);
1466       macrolist = next;
1467     }
1468   while (variablelist)
1469     {
1470       macro_t next = variablelist->next;
1471       free (variablelist->value);
1472       free (variablelist);
1473       variablelist = next;
1474     }
1475   for (m=predefinedmacrolist; m; m = m->next)
1476     set_macro (m->name, xstrdup ("1"));
1477   cond_is_active = 1;
1478   cond_in_verbatim = 0;
1479 
1480   parse_file (fname, fp, &section_name, 0);
1481   free (section_name);
1482   finish_page ();
1483 }
1484 
1485 
1486 int
main(int argc,char ** argv)1487 main (int argc, char **argv)
1488 {
1489   int last_argc = -1;
1490   const char *s;
1491 
1492   opt_source = "GNU";
1493   opt_release = "";
1494 
1495   /* Define default macros.  The trick is that these macros are not
1496      defined when using the actual texinfo renderer. */
1497   add_predefined_macro ("isman");
1498   add_predefined_macro ("manverb");
1499 
1500   /* Option parsing.  */
1501   if (argc)
1502     {
1503       argc--; argv++;
1504     }
1505   while (argc && last_argc != argc )
1506     {
1507       last_argc = argc;
1508       if (!strcmp (*argv, "--"))
1509         {
1510           argc--; argv++;
1511           break;
1512         }
1513       else if (!strcmp (*argv, "--help"))
1514         {
1515           puts (
1516                 "Usage: " PGM " [OPTION] [FILE]\n"
1517                 "Extract man pages from a Texinfo source.\n\n"
1518                 "  --source NAME    use NAME as source field\n"
1519                 "  --release STRING use STRING as the release field\n"
1520                 "  --date EPOCH     use EPOCH as publication date\n"
1521                 "  --store          write output using @manpage name\n"
1522                 "  --select NAME    only output pages with @manpage NAME\n"
1523                 "  --verbose        enable extra informational output\n"
1524                 "  --debug          enable additional debug output\n"
1525                 "  --help           display this help and exit\n"
1526                 "  -I DIR           also search in include DIR\n"
1527                 "  -D gpgone        the only usable define\n\n"
1528                 "With no FILE, or when FILE is -, read standard input.\n\n"
1529                 "Report bugs to <https://bugs.gnupg.org>.");
1530           exit (0);
1531         }
1532       else if (!strcmp (*argv, "--version"))
1533         {
1534           puts (PGM " " VERSION "\n"
1535                "Copyright (C) 2005, 2017 g10 Code GmbH\n"
1536                "This program comes with ABSOLUTELY NO WARRANTY.\n"
1537                "This is free software, and you are welcome to redistribute it\n"
1538                 "under certain conditions. See the file COPYING for details.");
1539           exit (0);
1540         }
1541       else if (!strcmp (*argv, "--verbose"))
1542         {
1543           verbose = 1;
1544           argc--; argv++;
1545         }
1546       else if (!strcmp (*argv, "--quiet"))
1547         {
1548           quiet = 1;
1549           argc--; argv++;
1550         }
1551       else if (!strcmp (*argv, "--debug"))
1552         {
1553           verbose = debug = 1;
1554           argc--; argv++;
1555         }
1556       else if (!strcmp (*argv, "--source"))
1557         {
1558           argc--; argv++;
1559           if (argc)
1560             {
1561               opt_source = *argv;
1562               argc--; argv++;
1563             }
1564         }
1565       else if (!strcmp (*argv, "--release"))
1566         {
1567           argc--; argv++;
1568           if (argc)
1569             {
1570               opt_release = *argv;
1571               argc--; argv++;
1572             }
1573         }
1574       else if (!strcmp (*argv, "--date"))
1575         {
1576           argc--; argv++;
1577           if (argc)
1578             {
1579               opt_date = *argv;
1580               argc--; argv++;
1581             }
1582         }
1583       else if (!strcmp (*argv, "--store"))
1584         {
1585           opt_store = 1;
1586           argc--; argv++;
1587         }
1588       else if (!strcmp (*argv, "--select"))
1589         {
1590           argc--; argv++;
1591           if (argc)
1592             {
1593               opt_select = strrchr (*argv, '/');
1594               if (opt_select)
1595                 opt_select++;
1596               else
1597                 opt_select = *argv;
1598               argc--; argv++;
1599             }
1600         }
1601       else if (!strcmp (*argv, "-I"))
1602         {
1603           argc--; argv++;
1604           if (argc)
1605             {
1606               opt_include = *argv;
1607               argc--; argv++;
1608             }
1609         }
1610       else if (!strcmp (*argv, "-D"))
1611         {
1612           argc--; argv++;
1613           if (argc)
1614             {
1615               add_predefined_macro (*argv);
1616               argc--; argv++;
1617             }
1618         }
1619     }
1620 
1621   if (argc > 1)
1622     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1623 
1624   /* Take care of supplied timestamp for reproducible builds.  See
1625    * https://reproducible-builds.org/specs/source-date-epoch/  */
1626   if (!opt_date && (s = getenv ("SOURCE_DATE_EPOCH")) && *s)
1627     opt_date = s;
1628 
1629   /* Start processing. */
1630   if (argc && strcmp (*argv, "-"))
1631     {
1632       FILE *fp = fopen (*argv, "rb");
1633       if (!fp)
1634         die ("%s:0: can't open file: %s", *argv, strerror (errno));
1635       top_parse_file (*argv, fp);
1636       fclose (fp);
1637     }
1638   else
1639     top_parse_file ("-", stdin);
1640 
1641   return !!any_error;
1642 }
1643 
1644 
1645 /*
1646 Local Variables:
1647 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1648 End:
1649 */
1650