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 = §->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, §ion_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