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     alternatively 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 static char const default_css[] =
145   "<style type=\"text/css\">\n"
146   "  .y2m {\n"
147   "    font-family: monospace;\n"
148   "  }\n"
149   "  .y2m u {\n"
150   "    text-decoration: underline;\n"
151   "  }\n"
152   "  .y2m-sc {\n"
153   "    font-variant: small-caps;\n"
154   "  }\n"
155   "  .y2m li {\n"
156   "    margin-top: 1em;\n"
157   "  }\n"
158   "  .y2m-item {\n"
159   "     display: block;\n"
160   "     font-weight: bold;\n"
161   "  }\n"
162   "  .y2m-args {\n"
163   "     font-weight: normal;\n"
164   "  }\n"
165   "</style>\n";
166 
167 
168 
169 /* Option flags. */
170 static int verbose;
171 static int quiet;
172 static int debug;
173 static int htmlmode;
174 static const char *opt_source;
175 static const char *opt_release;
176 static const char *opt_date;
177 static const char *opt_select;
178 static const char *opt_include;
179 static int opt_store;
180 
181 /* Flag to keep track whether any error occurred.  */
182 static int any_error;
183 
184 
185 /* Object to keep macro definitions.  */
186 struct macro_s
187 {
188   struct macro_s *next;
189   char *value;    /* Malloced value. */
190   char name[1];
191 };
192 typedef struct macro_s *macro_t;
193 
194 /* List of all defined macros. */
195 static macro_t macrolist;
196 
197 /* List of variables set by @set. */
198 static macro_t variablelist;
199 
200 /* List of global macro names.  The value part is not used.  */
201 static macro_t predefinedmacrolist;
202 
203 /* Object to keep track of @isset and @ifclear.  */
204 struct condition_s
205 {
206   int manverb;   /* "manverb" needs special treatment.  */
207   int isset;     /* This is an @isset condition.  */
208   char name[1];  /* Name of the condition macro.  */
209 };
210 typedef struct condition_s *condition_t;
211 
212 /* The stack used to evaluate conditions.  And the current states. */
213 static condition_t condition_stack[MAX_CONDITION_NESTING];
214 static int condition_stack_idx;
215 static int cond_is_active;     /* State of ifset/ifclear */
216 static int cond_in_verbatim;   /* State of "manverb".  */
217 
218 
219 /* Object to store one line of content.  */
220 struct line_buffer_s
221 {
222   struct line_buffer_s *next;
223   int verbatim;  /* True if LINE contains verbatim data.  The default
224                     is Texinfo source.  */
225   char *line;
226 };
227 typedef struct line_buffer_s *line_buffer_t;
228 
229 
230 /* Object to collect the data of a section.  */
231 struct section_buffer_s
232 {
233   char *name;           /* Malloced name of the section. This may be
234                            NULL to indicate this slot is not used.  */
235   line_buffer_t lines;  /* Linked list with the lines of the section.  */
236   line_buffer_t *lines_tail; /* Helper for faster appending to the
237                                 linked list.  */
238   line_buffer_t last_line;   /* Points to the last line appended.  */
239 };
240 typedef struct section_buffer_s *section_buffer_t;
241 
242 /* Variable to keep info about the current page together.  */
243 static struct
244 {
245   /* Filename of the current page or NULL if no page is active.  Malloced. */
246   char *name;
247 
248   /* Number of allocated elements in SECTIONS below.  */
249   size_t n_sections;
250   /* Array with the data of the sections.  */
251   section_buffer_t sections;
252 
253 } thepage;
254 
255 
256 /* The list of standard section names.  COMMANDS and ASSUAN are GnuPG
257    specific. */
258 static const char * const standard_sections[] =
259   { "NAME",  "SYNOPSIS",  "DESCRIPTION",
260     "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS",
261     "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES",
262     "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO",
263     "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL };
264 
265 
266 /*-- Local prototypes.  --*/
267 static void proc_texi_buffer (FILE *fp, const char *line, size_t len,
268                               int *table_level, int *eol_action);
269 
270 static void die (const char *format, ...) ATTR_NR_PRINTF(1,2);
271 static void err (const char *format, ...) ATTR_PRINTF(1,2);
272 static void inf (const char *format, ...) ATTR_PRINTF(1,2);
273 static void *xmalloc (size_t n) ATTR_MALLOC;
274 static void *xcalloc (size_t n, size_t m) ATTR_MALLOC;
275 
276 
277 
278 /*-- Functions --*/
279 
280 /* Print diagnostic message and exit with failure. */
281 static void
die(const char * format,...)282 die (const char *format, ...)
283 {
284   va_list arg_ptr;
285 
286   fflush (stdout);
287   fprintf (stderr, "%s: ", PGM);
288 
289   va_start (arg_ptr, format);
290   vfprintf (stderr, format, arg_ptr);
291   va_end (arg_ptr);
292   putc ('\n', stderr);
293 
294   exit (1);
295 }
296 
297 
298 /* Print diagnostic message. */
299 static void
err(const char * format,...)300 err (const char *format, ...)
301 {
302   va_list arg_ptr;
303 
304   fflush (stdout);
305   if (strncmp (format, "%s:%d:", 6))
306     fprintf (stderr, "%s: ", PGM);
307 
308   va_start (arg_ptr, format);
309   vfprintf (stderr, format, arg_ptr);
310   va_end (arg_ptr);
311   putc ('\n', stderr);
312   any_error = 1;
313 }
314 
315 /* Print diagnostic message. */
316 static void
inf(const char * format,...)317 inf (const char *format, ...)
318 {
319   va_list arg_ptr;
320 
321   fflush (stdout);
322   fprintf (stderr, "%s: ", PGM);
323 
324   va_start (arg_ptr, format);
325   vfprintf (stderr, format, arg_ptr);
326   va_end (arg_ptr);
327   putc ('\n', stderr);
328 }
329 
330 
331 static void *
xmalloc(size_t n)332 xmalloc (size_t n)
333 {
334   void *p = malloc (n);
335   if (!p)
336     die ("out of core: %s", strerror (errno));
337   return p;
338 }
339 
340 static void *
xcalloc(size_t n,size_t m)341 xcalloc (size_t n, size_t m)
342 {
343   void *p = calloc (n, m);
344   if (!p)
345     die ("out of core: %s", strerror (errno));
346   return p;
347 }
348 
349 static void *
xrealloc(void * old,size_t n)350 xrealloc (void *old, size_t n)
351 {
352   void *p = realloc (old, n);
353   if (!p)
354     die ("out of core: %s", strerror (errno));
355   return p;
356 }
357 
358 static char *
xstrdup(const char * string)359 xstrdup (const char *string)
360 {
361   void *p = malloc (strlen (string)+1);
362   if (!p)
363     die ("out of core: %s", strerror (errno));
364   strcpy (p, string);
365   return p;
366 }
367 
368 
369 /* Uppercase the ascii characters in STRING.  */
370 static char *
ascii_strupr(char * string)371 ascii_strupr (char *string)
372 {
373   char *p;
374 
375   for (p = string; *p; p++)
376     if (!(*p & 0x80))
377       *p = toupper (*p);
378   return string;
379 }
380 
381 
382 /* Return the current date as an ISO string.  */
383 const char *
isodatestring(void)384 isodatestring (void)
385 {
386   static char buffer[36];
387   struct tm *tp;
388   time_t atime;
389 
390   if (opt_date && *opt_date)
391     atime = strtoul (opt_date, NULL, 10);
392   else
393     atime = time (NULL);
394   if (atime < 0)
395     strcpy (buffer, "????" "-??" "-??");
396   else
397     {
398       tp = gmtime (&atime);
399       sprintf (buffer,"%04d-%02d-%02d",
400                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
401     }
402   return buffer;
403 }
404 
405 
406 /* Add NAME to the list of predefined macros which are global for all
407    files.  */
408 static void
add_predefined_macro(const char * name)409 add_predefined_macro (const char *name)
410 {
411   macro_t m;
412 
413   for (m=predefinedmacrolist; m; m = m->next)
414     if (!strcmp (m->name, name))
415       break;
416   if (!m)
417     {
418       m = xcalloc (1, sizeof *m + strlen (name));
419       strcpy (m->name, name);
420       m->next = predefinedmacrolist;
421       predefinedmacrolist = m;
422     }
423 }
424 
425 
426 /* Create or update a macro with name MACRONAME and set its values TO
427    MACROVALUE.  Note that ownership of the macro value is transferred
428    to this function.  */
429 static void
set_macro(const char * macroname,char * macrovalue)430 set_macro (const char *macroname, char *macrovalue)
431 {
432   macro_t m;
433 
434   for (m=macrolist; m; m = m->next)
435     if (!strcmp (m->name, macroname))
436       break;
437   if (m)
438     free (m->value);
439   else
440     {
441       m = xcalloc (1, sizeof *m + strlen (macroname));
442       strcpy (m->name, macroname);
443       m->next = macrolist;
444       macrolist = m;
445     }
446   m->value = macrovalue;
447   macrovalue = NULL;
448 }
449 
450 
451 /* Create or update a variable with name and value given in NAMEANDVALUE.  */
452 static void
set_variable(char * nameandvalue)453 set_variable (char *nameandvalue)
454 {
455   macro_t m;
456   const char *value;
457   char *p;
458 
459   for (p = nameandvalue; *p && *p != ' ' && *p != '\t'; p++)
460     ;
461   if (!*p)
462     value = "";
463   else
464     {
465       *p++ = 0;
466       while (*p == ' ' || *p == '\t')
467         p++;
468       value = p;
469     }
470 
471   for (m=variablelist; m; m = m->next)
472     if (!strcmp (m->name, nameandvalue))
473       break;
474   if (m)
475     free (m->value);
476   else
477     {
478       m = xcalloc (1, sizeof *m + strlen (nameandvalue));
479       strcpy (m->name, nameandvalue);
480       m->next = variablelist;
481       variablelist = m;
482     }
483   m->value = xstrdup (value);
484 }
485 
486 
487 /* Return true if the macro or variable NAME is set, i.e. not the
488    empty string and not evaluating to 0.  */
489 static int
macro_set_p(const char * name)490 macro_set_p (const char *name)
491 {
492   macro_t m;
493 
494   for (m = macrolist; m ; m = m->next)
495     if (!strcmp (m->name, name))
496       break;
497   if (!m)
498     for (m = variablelist; m ; m = m->next)
499       if (!strcmp (m->name, name))
500         break;
501   if (!m || !m->value || !*m->value)
502     return 0;
503   if ((*m->value & 0x80) || !isdigit (*m->value))
504     return 1; /* Not a digit but some other string.  */
505   return !!atoi (m->value);
506 }
507 
508 
509 /* Evaluate the current conditions.  */
510 static void
evaluate_conditions(const char * fname,int lnr)511 evaluate_conditions (const char *fname, int lnr)
512 {
513   int i;
514 
515   (void)fname;
516   (void)lnr;
517 
518   /* for (i=0; i < condition_stack_idx; i++) */
519   /*   inf ("%s:%d:   stack[%d] %s %s %c", */
520   /*        fname, lnr, i, condition_stack[i]->isset? "set":"clr", */
521   /*        condition_stack[i]->name, */
522   /*        (macro_set_p (condition_stack[i]->name) */
523   /*         ^ !condition_stack[i]->isset)? 't':'f'); */
524 
525   cond_is_active = 1;
526   cond_in_verbatim = 0;
527   if (condition_stack_idx)
528     {
529       for (i=0; i < condition_stack_idx; i++)
530         {
531           if (condition_stack[i]->manverb)
532             cond_in_verbatim = (macro_set_p (condition_stack[i]->name)
533                                 ^ !condition_stack[i]->isset);
534           else if (!(macro_set_p (condition_stack[i]->name)
535                      ^ !condition_stack[i]->isset))
536             {
537               cond_is_active = 0;
538               break;
539             }
540         }
541     }
542 
543   /* inf ("%s:%d:   active=%d verbatim=%d", */
544   /*      fname, lnr, cond_is_active, cond_in_verbatim); */
545 }
546 
547 
548 /* Push a condition with condition macro NAME onto the stack.  If
549    ISSET is true, a @isset condition is pushed.  */
550 static void
push_condition(const char * name,int isset,const char * fname,int lnr)551 push_condition (const char *name, int isset, const char *fname, int lnr)
552 {
553   condition_t cond;
554   int manverb = 0;
555 
556   if (condition_stack_idx >= MAX_CONDITION_NESTING)
557     {
558       err ("%s:%d: condition nested too deep", fname, lnr);
559       return;
560     }
561 
562   if (!strcmp (name, "manverb"))
563     {
564       if (!isset)
565         {
566           err ("%s:%d: using \"@ifclear manverb\" is not allowed", fname, lnr);
567           return;
568         }
569       manverb = 1;
570     }
571 
572   cond = xcalloc (1, sizeof *cond + strlen (name));
573   cond->manverb = manverb;
574   cond->isset = isset;
575   strcpy (cond->name, name);
576 
577   condition_stack[condition_stack_idx++] = cond;
578   evaluate_conditions (fname, lnr);
579 }
580 
581 
582 /* Remove the last condition from the stack.  ISSET is used for error
583    reporting.  */
584 static void
pop_condition(int isset,const char * fname,int lnr)585 pop_condition (int isset, const char *fname, int lnr)
586 {
587   if (!condition_stack_idx)
588     {
589       err ("%s:%d: unbalanced \"@end %s\"",
590            fname, lnr, isset?"isset":"isclear");
591       return;
592     }
593   condition_stack_idx--;
594   free (condition_stack[condition_stack_idx]);
595   condition_stack[condition_stack_idx] = NULL;
596   evaluate_conditions (fname, lnr);
597 }
598 
599 
600 
601 /* Return a section buffer for the section NAME.  Allocate a new buffer
602    if this is a new section.  Keep track of the sections in THEPAGE.
603    This function may reallocate the section array in THEPAGE.  */
604 static section_buffer_t
get_section_buffer(const char * name)605 get_section_buffer (const char *name)
606 {
607   int i;
608   section_buffer_t sect;
609 
610   /* If there is no section we put everything into the required NAME
611      section.  Given that this is the first one listed it is likely
612      that error are easily visible.  */
613   if (!name)
614     name = "NAME";
615 
616   for (i=0; i < thepage.n_sections; i++)
617     {
618       sect = thepage.sections + i;
619       if (sect->name && !strcmp (name, sect->name))
620         return sect;
621     }
622   for (i=0; i < thepage.n_sections; i++)
623     if (!thepage.sections[i].name)
624       break;
625   if (thepage.n_sections && i < thepage.n_sections)
626     sect = thepage.sections + i;
627   else
628     {
629       /* We need to allocate or reallocate the section array.  */
630       size_t old_n = thepage.n_sections;
631       size_t new_n = 20;
632 
633       if (!old_n)
634         thepage.sections = xcalloc (new_n, sizeof *thepage.sections);
635       else
636         {
637           thepage.sections = xrealloc (thepage.sections,
638                                        ((old_n + new_n)
639                                         * sizeof *thepage.sections));
640           memset (thepage.sections + old_n, 0,
641                   new_n * sizeof *thepage.sections);
642         }
643       thepage.n_sections += new_n;
644 
645       /* Setup the tail pointers.  */
646       for (i=old_n; i < thepage.n_sections; i++)
647         {
648           sect = thepage.sections + i;
649           sect->lines_tail = &sect->lines;
650         }
651       sect = thepage.sections + old_n;
652     }
653 
654   /* Store the name.  */
655   assert (!sect->name);
656   sect->name = xstrdup (name);
657   return sect;
658 }
659 
660 
661 
662 /* Add the content of LINE to the section named SECTNAME.  */
663 static void
add_content(const char * sectname,char * line,int verbatim)664 add_content (const char *sectname, char *line, int verbatim)
665 {
666   section_buffer_t sect;
667   line_buffer_t lb;
668 
669   sect = get_section_buffer (sectname);
670   if (sect->last_line && !sect->last_line->verbatim == !verbatim)
671     {
672       /* Lets append that line to the last one.  We do this to keep
673          all lines of the same kind (i.e.verbatim or not) together in
674          one large buffer.  */
675       size_t n1, n;
676 
677       lb = sect->last_line;
678       n1 = strlen (lb->line);
679       n = n1 + 1 + strlen (line) + 1;
680       lb->line = xrealloc (lb->line, n);
681       strcpy (lb->line+n1, "\n");
682       strcpy (lb->line+n1+1, line);
683     }
684   else
685     {
686       lb = xcalloc (1, sizeof *lb);
687       lb->verbatim = verbatim;
688       lb->line = xstrdup (line);
689       sect->last_line = lb;
690       *sect->lines_tail = lb;
691       sect->lines_tail = &lb->next;
692     }
693 }
694 
695 
696 /* Prepare for a new man page using the filename NAME. */
697 static void
start_page(char * name)698 start_page (char *name)
699 {
700   if (verbose)
701     inf ("starting page '%s'", name);
702   assert (!thepage.name);
703   thepage.name = xstrdup (name);
704   thepage.n_sections = 0;
705 }
706 
707 
708 /* Write a character to FP.  */
709 static void
writechr(int c,FILE * fp)710 writechr (int c, FILE *fp)
711 {
712   putc (c, fp);
713 }
714 
715 
716 /* Write depending on HTMLMODE either ROFF or HTML to FP.  */
717 static void
writestr(const char * roff,const char * html,FILE * fp)718 writestr (const char *roff, const char *html, FILE *fp)
719 {
720   const char *s = htmlmode? html : roff;
721 
722   if (s)
723     fputs (s, fp);
724 }
725 
726 
727 /* Write the .TH entry of the current page.  Return -1 if there is a
728    problem with the page. */
729 static int
write_th(FILE * fp)730 write_th (FILE *fp)
731 {
732   char *name, *p;
733 
734   writestr (".\\\" Created from Texinfo source by yat2m " VERSION "\n",
735             "<!-- Created from Texinfo source by yat2m " VERSION " -->\n",
736             fp);
737 
738   name = ascii_strupr (xstrdup (thepage.name));
739   p = strrchr (name, '.');
740   if (!p || !p[1])
741     {
742       err ("no section name in man page '%s'", thepage.name);
743       free (name);
744       return -1;
745     }
746   *p++ = 0;
747 
748   if (htmlmode)
749     {
750       fputs ("<html>\n"
751              "<head>\n", fp);
752       fprintf (fp, " <title>%s(%s)</title>\n", name, p);
753       fputs (default_css, fp);
754       fputs ("</head>\n"
755              "<body>\n", fp);
756       fputs ("<div class=\"y2m\">\n", fp);
757     }
758 
759   /* This roff source
760    *   .TH GPG 1 2016-12-20 "GnuPG 2.1.17" "GNU Privacy Guard 2.1"
761    * is rendered by man like this:
762    *   GPG(1)         GNU Privacy Guard 2.1      GPG(1)
763    *   [...]
764    *   GnuPG 2.1.17        2016-12-20            GPG(1)
765    */
766   if (htmlmode)
767     {
768       fprintf (fp, "<p class=\"y2m y2m-top\">"
769                "<span class=\"y2m-left\">%s(%s)</span> "
770                "<span class=\"y2m-center\">%s</span> "
771                "<span class=\"y2m-right\">%s(%s)</span>"
772                "</p>\n",
773                name, p, opt_source, name, p);
774       /* Note that the HTML footer is written by write_bottom().  */
775 
776     }
777   else
778     fprintf (fp, ".TH %s %s %s \"%s\" \"%s\"\n",
779              name, p, isodatestring (), opt_release, opt_source);
780 
781   free (name);
782   return 0;
783 }
784 
785 
786 /* In HTML mode we need to render a footer.  */
787 static int
write_bottom(FILE * fp)788 write_bottom (FILE *fp)
789 {
790   char *name, *p;
791 
792   if (!htmlmode)
793     return 0;
794 
795   name = ascii_strupr (xstrdup (thepage.name));
796   p = strrchr (name, '.');
797   if (!p || !p[1])
798     {
799       err ("no section name in man page '%s'", thepage.name);
800       free (name);
801       return -1;
802     }
803   *p++ = 0;
804 
805   /* This roff source
806    *   .TH GPG 1 2016-12-20 "GnuPG 2.1.17" "GNU Privacy Guard 2.1"
807    * is rendered by man to this footer:
808    *   GnuPG 2.1.17        2016-12-20            GPG(1)
809    */
810   fprintf (fp, "<p class=\"y2m y2m-footer\">"
811            "<span class=\"y2m-left\">%s</span> "
812            "<span class=\"y2m-center\">%s</span> "
813            "<span class=\"y2m-right\">%s(%s)</span>"
814            "</p>\n",
815            opt_release, isodatestring (), name, p);
816   fputs ("</div><!-- class y2m -->\n", fp);
817   fputs ("</body>\n"
818          "</html>\n", fp);
819 
820   free (name);
821   return 0;
822 }
823 
824 
825 /* Write the .SH header.  With NULL passed for NAME just close a
826  * section in html mode if there is an open section. */
827 static void
write_sh(FILE * fp,const char * name)828 write_sh (FILE *fp, const char *name)
829 {
830   static int in_section;
831 
832   if (htmlmode && in_section)
833     fprintf (fp, "</div>\n");
834   in_section = 0;
835 
836   if (name)
837     {
838       if (htmlmode)
839         fprintf (fp,
840                  "<div class=\"y2m-section\">\n"
841                  "<p class=\"y2m-sh\">%s</p>\n", name);
842       else
843         fprintf (fp, ".SH %s\n", name);
844       in_section = 1;
845     }
846 }
847 
848 /* Render a @item line to HTML.  (LINE,LEN) gives the arguments of
849  * @item.  Use NULL for LINE to close a possible open <li>.  ITEMX
850  * flags a @itemx line.  */
851 static void
write_html_item(FILE * fp,const char * line,size_t len,int itemx)852 write_html_item (FILE *fp, const char *line, size_t len, int itemx)
853 {
854   static int in_li;
855   const char *rest;
856   size_t n, n0;
857   int eol_action = 0;
858   int table_level = 0;
859 
860   if (!itemx && in_li)
861     {
862       fprintf (fp, "</li>\n");
863       in_li = 0;
864     }
865 
866   if (line)
867     {
868       /* Trim the LF and skip leading spaces. */
869       if (len && line[len-1] == '\n')
870         len--;
871       for (; len && (*line == ' ' || *line == '\t'); len--, line++)
872         ;
873       if (len)
874         {
875           rest = line;
876           for (n=0; n < len && !(*rest == ' ' || *rest == '\t'); n++, rest++)
877             ;
878           n0 = n;
879           for (; n < len && (*rest == ' ' || *rest == '\t'); n++, rest++)
880             ;
881           len -= n;
882           /* Now the first word is (LINE,N0) and the args are (REST,LEN) */
883           fprintf (fp, "%s<span class=\"y2m-item\">%.*s",
884                    itemx? "    ":"<li>", (int)n0, line);
885           if (len)
886             {
887               fputs (" <span class=\"y2m-args\">", fp);
888               proc_texi_buffer (fp, rest, len, &table_level, &eol_action);
889               fputs ("</span>", fp);
890             }
891           fputs ("</span>\n", fp);
892           in_li = 1;
893         }
894     }
895 }
896 
897 
898 /* Process the texinfo command COMMAND (without the leading @) and
899    write output if needed to FP. REST is the remainder of the line
900    which should either point to an opening brace or to a white space.
901    The function returns the number of characters already processed
902    from REST.  LEN is the usable length of REST.  TABLE_LEVEL is used to
903    control the indentation of tables.  */
904 static size_t
proc_texi_cmd(FILE * fp,const char * command,const char * rest,size_t len,int * table_level,int * eol_action)905 proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len,
906                int *table_level, int *eol_action)
907 {
908   static struct {
909     const char *name;    /* Name of the command.  */
910     int what;            /* What to do with this command. */
911     const char *lead_in; /* String to print with a opening brace.  */
912     const char *lead_out;/* String to print with the closing brace. */
913     const char *html_in; /* Same as LEAD_IN but for HTML.  */
914     const char *html_out;/* Same as LEAD_OUT but for HTML.  */
915   } cmdtbl[] = {
916     { "command", 0, "\\fB", "\\fR", "<i>", "</i>" },
917     { "code",    0, "\\fB", "\\fR", "<samp>", "</samp>" },
918     { "url",     0, "\\fB", "\\fR", "<strong>", "</strong>" },
919     { "sc",      0, "\\fB", "\\fR", "<span class=\"y2m-sc\">", "</span>" },
920     { "var",     0, "\\fI", "\\fR", "<u>", "</u>" },
921     { "samp",    0, "\\(oq", "\\(cq"  },
922     { "kbd",     0, "\\(oq", "\\(cq"  },
923     { "file",    0, "\\(oq\\fI","\\fR\\(cq" },
924     { "env",     0, "\\(oq\\fI","\\fR\\(cq" },
925     { "acronym", 0 },
926     { "dfn",     0 },
927     { "option",  0, "\\fB", "\\fR", "<samp>", "</samp>" },
928     { "example", 1, ".RS 2\n.nf\n",      NULL, "\n<pre>\n", "\n</pre>\n" },
929     { "smallexample", 1, ".RS 2\n.nf\n", NULL, "\n<pre>\n", "\n</pre>\n" },
930     { "asis",    7 },
931     { "anchor",  7 },
932     { "cartouche", 1 },
933     { "ref",     0, "[", "]" },
934     { "xref",    0, "See: [", "]" },
935     { "pxref",   0, "see: [", "]" },
936     { "uref",    0, "(\\fB", "\\fR)" },
937     { "footnote",0, " ([", "])" },
938     { "emph",    0, "\\fI", "\\fR", "<em>", "</em>" },
939     { "w",       1 },
940     { "c",       5 },
941     { "efindex", 1 },
942     { "opindex", 1 },
943     { "cpindex", 1 },
944     { "cindex",  1 },
945     { "noindent", 0 },
946     { "section", 1 },
947     { "chapter", 1 },
948     { "subsection", 6, "\n.SS " },
949     { "chapheading", 0},
950     { "item",    2, ".TP\n.B " },
951     { "itemx",   2, ".TQ\n.B " },
952     { "table",   3, NULL, NULL, "<ul>\n", "</ul>\n" },
953     { "itemize",   3 },
954     { "bullet",  0, "* " },
955     { "*",       0, "\n.br"},
956     { "/",       0 },
957     { "end",     4 },
958     { "quotation",1, ".RS\n\\fB" },
959     { "value", 8 },
960     { NULL }
961   };
962   size_t n;
963   int i;
964   const char *s;
965   const char *lead_out = NULL;
966   const char *html_out = NULL;
967   int ignore_args = 0;
968 
969   for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
970     ;
971   if (cmdtbl[i].name)
972     {
973       writestr (cmdtbl[i].lead_in, cmdtbl[i].html_in, fp);
974       lead_out = cmdtbl[i].lead_out;
975       html_out = cmdtbl[i].html_out;
976       switch (cmdtbl[i].what)
977         {
978         case 1: /* Throw away the entire line.  */
979           s = memchr (rest, '\n', len);
980           return s? (s-rest)+1 : len;
981         case 2: /* Handle @item.  */
982           if (htmlmode)
983             {
984               s = memchr (rest, '\n', len);
985               n = s? (s-rest)+1 : len;
986               write_html_item (fp, rest, n, !strcmp(cmdtbl[i].name, "itemx"));
987               return n;
988             }
989           break;
990         case 3: /* Handle table.  */
991           if (++(*table_level) > 1)
992             {
993               write_html_item (fp, NULL, 0, 0);
994               writestr (".RS\n", "<ul>\n", fp);
995             }
996           /* Now throw away the entire line. */
997           s = memchr (rest, '\n', len);
998           return s? (s-rest)+1 : len;
999           break;
1000         case 4: /* Handle end.  */
1001           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
1002             ;
1003           if (n >= 5 && !memcmp (s, "table", 5)
1004               && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
1005             {
1006               if (htmlmode)
1007                 write_html_item (fp, NULL, 0, 0);
1008               if ((*table_level)-- > 1)
1009                 writestr (".RE\n", "</ul>\n", fp);
1010               else
1011                 writestr (".P\n", "</ul>\n", fp);
1012             }
1013           else if (n >= 7 && !memcmp (s, "example", 7)
1014               && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
1015             {
1016               writestr (".fi\n.RE\n", "</pre>\n", fp);
1017             }
1018           else if (n >= 12 && !memcmp (s, "smallexample", 12)
1019               && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
1020             {
1021               writestr (".fi\n.RE\n", "</pre>\n", fp);
1022             }
1023           else if (n >= 9 && !memcmp (s, "quotation", 9)
1024               && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
1025             {
1026               writestr ("\\fR\n.RE\n", "xx", fp);
1027             }
1028           /* Now throw away the entire line. */
1029           s = memchr (rest, '\n', len);
1030           return s? (s-rest)+1 : len;
1031         case 5: /* Handle special comments. */
1032           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
1033             ;
1034           if (n >= 4 && !memcmp (s, "man:", 4))
1035             {
1036               s += 4;
1037               n -= 4;
1038               if (htmlmode)
1039                 {
1040                   if (!strncmp (s, ".RE\n", 4)
1041                       || !strncmp (s, ".RS\n", 4))
1042                     ;
1043                   else
1044                     inf ("unknown special comment \"man:\"");
1045                 }
1046               else
1047                 {
1048                   for (; n && *s != '\n'; n--, s++)
1049                     writechr (*s, fp);
1050                   writechr ('\n', fp);
1051                 }
1052             }
1053           /* Now throw away the entire line. */
1054           s = memchr (rest, '\n', len);
1055           return s? (s-rest)+1 : len;
1056         case 6:
1057           *eol_action = 1;
1058           break;
1059         case 7:
1060           ignore_args = 1;
1061           break;
1062         case 8:
1063           ignore_args = 1;
1064           if (*rest != '{')
1065             {
1066               err ("opening brace for command '%s' missing", command);
1067               return len;
1068             }
1069           else
1070             {
1071               /* Find closing brace.  */
1072               for (s=rest+1, n=1; *s && n < len; s++, n++)
1073                 if (*s == '}')
1074                   break;
1075               if (*s != '}')
1076                 {
1077                   err ("closing brace for command '%s' not found", command);
1078                   return len;
1079                 }
1080               else
1081                 {
1082                   size_t rlen = s - (rest + 1);
1083                   macro_t m;
1084 
1085                   for (m = variablelist; m; m = m->next)
1086                     {
1087                       if (strlen (m->name) == rlen
1088                           && !strncmp (m->name, rest+1, rlen))
1089                         break;
1090                     }
1091                   if (m)
1092                     writestr (m->value, m->value, fp);
1093                   else
1094                     inf ("texinfo variable '%.*s' is not set",
1095                          (int)rlen, rest+1);
1096                 }
1097             }
1098           break;
1099         default:
1100           break;
1101         }
1102     }
1103   else /* macro */
1104     {
1105       macro_t m;
1106 
1107       for (m = macrolist; m ; m = m->next)
1108         if (!strcmp (m->name, command))
1109             break;
1110       if (m)
1111         {
1112           proc_texi_buffer (fp, m->value, strlen (m->value),
1113                             table_level, eol_action);
1114           ignore_args = 1; /* Parameterized macros are not yet supported. */
1115         }
1116       else
1117         inf ("texinfo command '%s' not supported (%.*s)", command,
1118              (int)((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
1119     }
1120 
1121   if (*rest == '{')
1122     {
1123       /* Find matching closing brace.  */
1124       for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
1125         if (*s == '{')
1126           i++;
1127         else if (*s == '}')
1128           i--;
1129       if (i)
1130         {
1131           err ("closing brace for command '%s' not found", command);
1132           return len;
1133         }
1134       if (n > 2 && !ignore_args)
1135         proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
1136     }
1137   else
1138     n = 0;
1139 
1140   writestr (lead_out, html_out, fp);
1141 
1142   return n;
1143 }
1144 
1145 
1146 
1147 /* Process the string LINE with LEN bytes of Texinfo content. */
1148 static void
proc_texi_buffer(FILE * fp,const char * line,size_t len,int * table_level,int * eol_action)1149 proc_texi_buffer (FILE *fp, const char *line, size_t len,
1150                   int *table_level, int *eol_action)
1151 {
1152   const char *s;
1153   char cmdbuf[256];
1154   int cmdidx = 0;
1155   int in_cmd = 0;
1156   size_t n;
1157 
1158   for (s=line; *s && len; s++, len--)
1159     {
1160       if (in_cmd)
1161         {
1162           if (in_cmd == 1)
1163             {
1164               switch (*s)
1165                 {
1166                 case '@': case '{': case '}':
1167                   writechr (*s, fp); in_cmd = 0;
1168                   break;
1169                 case ':': /* Not ending a sentence flag.  */
1170                   in_cmd = 0;
1171                   break;
1172                 case '.': case '!': case '?': /* Ending a sentence. */
1173                   writechr (*s, fp); in_cmd = 0;
1174                   break;
1175                 case ' ': case '\t': case '\n': /* Non collapsing spaces.  */
1176                   writechr (*s, fp); in_cmd = 0;
1177                   break;
1178                 default:
1179                   cmdidx = 0;
1180                   cmdbuf[cmdidx++] = *s;
1181                   in_cmd++;
1182                   break;
1183                 }
1184             }
1185           else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
1186             {
1187               cmdbuf[cmdidx] = 0;
1188               n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
1189               assert (n <= len);
1190               s += n; len -= n;
1191               s--; len++;
1192               in_cmd = 0;
1193             }
1194           else if (cmdidx < sizeof cmdbuf -1)
1195             cmdbuf[cmdidx++] = *s;
1196           else
1197             {
1198               err ("texinfo command too long - ignored");
1199               in_cmd = 0;
1200             }
1201         }
1202       else if (*s == '@')
1203         in_cmd = 1;
1204       else if (*s == '\n')
1205         {
1206           switch (*eol_action)
1207             {
1208             case 1: /* Create a dummy paragraph. */
1209               writestr ("\n\\ \n", "\n<-- dummy par -->\n", fp);
1210               break;
1211             default:
1212               writechr (*s, fp);
1213             }
1214           *eol_action = 0;
1215         }
1216       else if (*s == '\\')
1217         writestr ("\\\\", "\\\\", fp);
1218       else
1219         writechr (*s, fp);
1220     }
1221 
1222   if (in_cmd > 1)
1223     {
1224       cmdbuf[cmdidx] = 0;
1225       n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
1226       assert (n <= len);
1227       s += n; len -= n;
1228       s--; len++;
1229       /* in_cmd = 0; -- doc only */
1230     }
1231 }
1232 
1233 
1234 /* Do something with the Texinfo line LINE.  */
1235 static void
parse_texi_line(FILE * fp,const char * line,int * table_level)1236 parse_texi_line (FILE *fp, const char *line, int *table_level)
1237 {
1238   int eol_action = 0;
1239 
1240   /* A quick test whether there are any texinfo commands.  */
1241   if (!strchr (line, '@'))
1242     {
1243       /* FIXME: In html mode escape HTML stuff. */
1244       writestr (line, line, fp);
1245       writechr ('\n', fp);
1246       return;
1247     }
1248   proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
1249   writechr ('\n', fp);
1250 }
1251 
1252 
1253 /* Write all the lines LINES to FP.  */
1254 static void
write_content(FILE * fp,line_buffer_t lines)1255 write_content (FILE *fp, line_buffer_t lines)
1256 {
1257   line_buffer_t line;
1258   int table_level = 0;
1259 
1260   for (line = lines; line; line = line->next)
1261     {
1262       if (line->verbatim)
1263         {
1264           /* FIXME: IN HTML mode we need to employ a parser for roff
1265            * markup.  */
1266           writestr (line->line, line->line, fp);
1267           writechr ('\n', fp);
1268         }
1269       else
1270         {
1271 /*           fputs ("TEXI---", fp); */
1272 /*           fputs (line->line, fp); */
1273 /*           fputs ("---\n", fp); */
1274           parse_texi_line (fp, line->line, &table_level);
1275         }
1276     }
1277 }
1278 
1279 
1280 
1281 static int
is_standard_section(const char * name)1282 is_standard_section (const char *name)
1283 {
1284   int i;
1285   const char *s;
1286 
1287   for (i=0; (s=standard_sections[i]); i++)
1288     if (!strcmp (s, name))
1289       return 1;
1290   return 0;
1291 }
1292 
1293 
1294 /* Finish a page; that is sort the data and write it out to the file.  */
1295 static void
finish_page(void)1296 finish_page (void)
1297 {
1298   FILE *fp;
1299   section_buffer_t sect = NULL;
1300   int idx;
1301   const char *s;
1302   int i;
1303 
1304   if (!thepage.name)
1305     return; /* No page active.  */
1306 
1307   if (verbose)
1308     inf ("finishing page '%s'", thepage.name);
1309 
1310   if (opt_select)
1311     {
1312       if (!strcmp (opt_select, thepage.name))
1313         {
1314           inf ("selected '%s'", thepage.name );
1315           fp = stdout;
1316         }
1317       else
1318         {
1319           fp = fopen ( "/dev/null", "w" );
1320           if (!fp)
1321             die ("failed to open /dev/null: %s\n", strerror (errno));
1322         }
1323     }
1324   else if (opt_store)
1325     {
1326       inf ("writing '%s'", thepage.name );
1327       fp = fopen ( thepage.name, "w" );
1328       if (!fp)
1329         die ("failed to create '%s': %s\n", thepage.name, strerror (errno));
1330     }
1331   else
1332     fp = stdout;
1333 
1334   if (write_th (fp))
1335     goto leave;
1336 
1337   for (idx=0; (s=standard_sections[idx]); idx++)
1338     {
1339       for (i=0; i < thepage.n_sections; i++)
1340         {
1341           sect = thepage.sections + i;
1342           if (sect->name && !strcmp (s, sect->name))
1343             break;
1344         }
1345       if (i == thepage.n_sections)
1346         sect = NULL;
1347 
1348       if (sect)
1349         {
1350           write_sh (fp, sect->name);
1351           write_content (fp, sect->lines);
1352           /* Now continue with all non standard sections directly
1353              following this one. */
1354           for (i++; i < thepage.n_sections; i++)
1355             {
1356               sect = thepage.sections + i;
1357               if (sect->name && is_standard_section (sect->name))
1358                 break;
1359               if (sect->name)
1360                 {
1361                   write_sh (fp, sect->name);
1362                   write_content (fp, sect->lines);
1363                 }
1364             }
1365 
1366         }
1367     }
1368 
1369   write_sh (fp, NULL);
1370   if (write_bottom (fp))
1371     goto leave;
1372 
1373  leave:
1374   if (fp != stdout)
1375     fclose (fp);
1376   free (thepage.name);
1377   thepage.name = NULL;
1378   /* FIXME: Cleanup the content.  */
1379 }
1380 
1381 
1382 
1383 
1384 /* Parse one Texinfo file and create manpages according to the
1385    embedded instructions.  */
1386 static void
parse_file(const char * fname,FILE * fp,char ** section_name,int in_pause)1387 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
1388 {
1389   char *line;
1390   int lnr = 0;
1391   /* Fixme: The following state variables don't carry over to include
1392      files. */
1393   int skip_to_end = 0;        /* Used to skip over menu entries. */
1394   int skip_sect_line = 0;     /* Skip after @mansect.  */
1395   int item_indent = 0;        /* How far is the current @item indented.  */
1396 
1397   /* Helper to define a macro. */
1398   char *macroname = NULL;
1399   char *macrovalue = NULL;
1400   size_t macrovaluesize = 0;
1401   size_t macrovalueused = 0;
1402 
1403   line = xmalloc (LINESIZE);
1404   while (fgets (line, LINESIZE, fp))
1405     {
1406       size_t n = strlen (line);
1407       int got_line = 0;
1408       char *p, *pend;
1409 
1410       lnr++;
1411       if (!n || line[n-1] != '\n')
1412         {
1413           err ("%s:%d: trailing linefeed missing, line too long or "
1414                "embedded Nul character", fname, lnr);
1415           break;
1416         }
1417       line[--n] = 0;
1418 
1419       /* Kludge to allow indentation of tables.  */
1420       for (p=line; *p == ' ' || *p == '\t'; p++)
1421         ;
1422       if (*p)
1423         {
1424           if (*p == '@' && !strncmp (p+1, "item", 4))
1425             item_indent = p - line;  /* Set a new indent level.  */
1426           else if (p - line < item_indent)
1427             item_indent = 0;         /* Switch off indention.  */
1428 
1429           if (item_indent)
1430             {
1431               memmove (line, line+item_indent, n - item_indent + 1);
1432               n -= item_indent;
1433             }
1434         }
1435 
1436 
1437       if (*line == '@')
1438         {
1439           for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
1440             n++;
1441           while (*p == ' ' || *p == '\t')
1442             p++;
1443         }
1444       else
1445         p = line;
1446 
1447       /* Take action on macro.  */
1448       if (macroname)
1449         {
1450           if (n == 4 && !memcmp (line, "@end", 4)
1451               && (line[4]==' '||line[4]=='\t'||!line[4])
1452               && !strncmp (p, "macro", 5)
1453               && (p[5]==' '||p[5]=='\t'||!p[5]))
1454             {
1455               if (macrovalueused)
1456                 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
1457               macrovalue[macrovalueused] = 0;     /* Terminate macro. */
1458               macrovalue = xrealloc (macrovalue, macrovalueused+1);
1459 
1460               set_macro (macroname, macrovalue);
1461               macrovalue = NULL;
1462               free (macroname);
1463               macroname = NULL;
1464             }
1465           else
1466             {
1467               if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
1468                 {
1469                   macrovaluesize += strlen (line) + 256;
1470                   macrovalue = xrealloc (macrovalue,  macrovaluesize);
1471                 }
1472               strcpy (macrovalue+macrovalueused, line);
1473               macrovalueused += strlen (line);
1474               macrovalue[macrovalueused++] = '\n';
1475             }
1476           continue;
1477         }
1478 
1479 
1480       if (n >= 5 && !memcmp (line, "@node", 5)
1481           && (line[5]==' '||line[5]=='\t'||!line[5]))
1482         {
1483           /* Completey ignore @node lines.  */
1484           continue;
1485         }
1486 
1487 
1488       if (skip_sect_line)
1489         {
1490           skip_sect_line = 0;
1491           if (!strncmp (line, "@section", 8)
1492               || !strncmp (line, "@subsection", 11)
1493               || !strncmp (line, "@chapheading", 12))
1494             continue;
1495         }
1496 
1497       /* We only parse lines we need and ignore the rest.  There are a
1498          few macros used to control this as well as one @ifset
1499          command.  Parts we know about are saved away into containers
1500          separate for each section. */
1501 
1502       /* First process ifset/ifclear commands. */
1503       if (*line == '@')
1504         {
1505           if (n == 6 && !memcmp (line, "@ifset", 6)
1506                    && (line[6]==' '||line[6]=='\t'))
1507             {
1508               for (p=line+7; *p == ' ' || *p == '\t'; p++)
1509                 ;
1510               if (!*p)
1511                 {
1512                   err ("%s:%d: name missing after \"@ifset\"", fname, lnr);
1513                   continue;
1514                 }
1515               for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1516                 ;
1517               *pend = 0;  /* Ignore rest of the line.  */
1518               push_condition (p, 1, fname, lnr);
1519               continue;
1520             }
1521           else if (n == 8 && !memcmp (line, "@ifclear", 8)
1522                    && (line[8]==' '||line[8]=='\t'))
1523             {
1524               for (p=line+9; *p == ' ' || *p == '\t'; p++)
1525                 ;
1526               if (!*p)
1527                 {
1528                   err ("%s:%d: name missing after \"@ifsclear\"", fname, lnr);
1529                   continue;
1530                 }
1531               for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1532                 ;
1533               *pend = 0;  /* Ignore rest of the line.  */
1534               push_condition (p, 0, fname, lnr);
1535               continue;
1536             }
1537           else if (n == 4 && !memcmp (line, "@end", 4)
1538                    && (line[4]==' '||line[4]=='\t')
1539                    && !strncmp (p, "ifset", 5)
1540                    && (p[5]==' '||p[5]=='\t'||!p[5]))
1541             {
1542               pop_condition (1, fname, lnr);
1543               continue;
1544             }
1545           else if (n == 4 && !memcmp (line, "@end", 4)
1546                    && (line[4]==' '||line[4]=='\t')
1547                    && !strncmp (p, "ifclear", 7)
1548                    && (p[7]==' '||p[7]=='\t'||!p[7]))
1549             {
1550               pop_condition (0, fname, lnr);
1551               continue;
1552             }
1553         }
1554 
1555       /* Take action on ifset/ifclear.  */
1556       if (!cond_is_active)
1557         continue;
1558 
1559       /* Process commands. */
1560       if (*line == '@')
1561         {
1562           if (skip_to_end
1563               && n == 4 && !memcmp (line, "@end", 4)
1564               && (line[4]==' '||line[4]=='\t'||!line[4]))
1565             {
1566               skip_to_end = 0;
1567             }
1568           else if (cond_in_verbatim)
1569             {
1570                 got_line = 1;
1571             }
1572           else if (n == 6 && !memcmp (line, "@macro", 6))
1573             {
1574               macroname = xstrdup (p);
1575               macrovalue = xmalloc ((macrovaluesize = 1024));
1576               macrovalueused = 0;
1577             }
1578           else if (n == 4 && !memcmp (line, "@set", 4))
1579             {
1580               set_variable (p);
1581             }
1582           else if (n == 8 && !memcmp (line, "@manpage", 8))
1583             {
1584               free (*section_name);
1585               *section_name = NULL;
1586               finish_page ();
1587               start_page (p);
1588               in_pause = 0;
1589             }
1590           else if (n == 8 && !memcmp (line, "@mansect", 8))
1591             {
1592               if (!thepage.name)
1593                 err ("%s:%d: section outside of a man page", fname, lnr);
1594               else
1595                 {
1596                   free (*section_name);
1597                   *section_name = ascii_strupr (xstrdup (p));
1598                   in_pause = 0;
1599                   skip_sect_line = 1;
1600                 }
1601             }
1602           else if (n == 9 && !memcmp (line, "@manpause", 9))
1603             {
1604               if (!*section_name)
1605                 err ("%s:%d: pausing outside of a man section", fname, lnr);
1606               else if (in_pause)
1607                 err ("%s:%d: already pausing", fname, lnr);
1608               else
1609                 in_pause = 1;
1610             }
1611           else if (n == 8 && !memcmp (line, "@mancont", 8))
1612             {
1613               if (!*section_name)
1614                 err ("%s:%d: continue outside of a man section", fname, lnr);
1615               else if (!in_pause)
1616                 err ("%s:%d: continue while not pausing", fname, lnr);
1617               else
1618                 in_pause = 0;
1619             }
1620           else if (n == 5 && !memcmp (line, "@menu", 5)
1621                    && (line[5]==' '||line[5]=='\t'||!line[5]))
1622             {
1623               skip_to_end = 1;
1624             }
1625           else if (n == 8 && !memcmp (line, "@include", 8)
1626                    && (line[8]==' '||line[8]=='\t'||!line[8]))
1627             {
1628               char *incname = xstrdup (p);
1629               FILE *incfp = fopen (incname, "r");
1630 
1631               if (!incfp && opt_include && *opt_include && *p != '/')
1632                 {
1633                   free (incname);
1634                   incname = xmalloc (strlen (opt_include) + 1
1635                                      + strlen (p) + 1);
1636                   strcpy (incname, opt_include);
1637                   if ( incname[strlen (incname)-1] != '/' )
1638                     strcat (incname, "/");
1639                   strcat (incname, p);
1640                   incfp = fopen (incname, "r");
1641                 }
1642 
1643               if (!incfp)
1644                 err ("can't open include file '%s': %s",
1645                      incname, strerror (errno));
1646               else
1647                 {
1648                   parse_file (incname, incfp, section_name, in_pause);
1649                   fclose (incfp);
1650                 }
1651               free (incname);
1652             }
1653           else if (n == 4 && !memcmp (line, "@bye", 4)
1654                    && (line[4]==' '||line[4]=='\t'||!line[4]))
1655             {
1656               break;
1657             }
1658           else if (!skip_to_end)
1659             got_line = 1;
1660         }
1661       else if (!skip_to_end)
1662         got_line = 1;
1663 
1664       if (got_line && cond_in_verbatim)
1665         add_content (*section_name, line, 1);
1666       else if (got_line && thepage.name && *section_name && !in_pause)
1667         add_content (*section_name, line, 0);
1668 
1669     }
1670   if (ferror (fp))
1671     err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1672   free (macroname);
1673   free (macrovalue);
1674   free (line);
1675 }
1676 
1677 
1678 static void
top_parse_file(const char * fname,FILE * fp)1679 top_parse_file (const char *fname, FILE *fp)
1680 {
1681   char *section_name = NULL;  /* Name of the current section or NULL
1682                                  if not in a section.  */
1683   macro_t m;
1684 
1685   while (macrolist)
1686     {
1687       macro_t next = macrolist->next;
1688       free (macrolist->value);
1689       free (macrolist);
1690       macrolist = next;
1691     }
1692   while (variablelist)
1693     {
1694       macro_t next = variablelist->next;
1695       free (variablelist->value);
1696       free (variablelist);
1697       variablelist = next;
1698     }
1699   for (m=predefinedmacrolist; m; m = m->next)
1700     set_macro (m->name, xstrdup ("1"));
1701   cond_is_active = 1;
1702   cond_in_verbatim = 0;
1703 
1704   parse_file (fname, fp, &section_name, 0);
1705   free (section_name);
1706   finish_page ();
1707 }
1708 
1709 
1710 int
main(int argc,char ** argv)1711 main (int argc, char **argv)
1712 {
1713   int last_argc = -1;
1714   const char *s;
1715 
1716   opt_source = "GNU";
1717   opt_release = "";
1718 
1719   /* Define default macros.  The trick is that these macros are not
1720      defined when using the actual texinfo renderer. */
1721   add_predefined_macro ("isman");
1722   add_predefined_macro ("manverb");
1723 
1724   /* Option parsing.  */
1725   if (argc)
1726     {
1727       argc--; argv++;
1728     }
1729   while (argc && last_argc != argc )
1730     {
1731       last_argc = argc;
1732       if (!strcmp (*argv, "--"))
1733         {
1734           argc--; argv++;
1735           break;
1736         }
1737       else if (!strcmp (*argv, "--help"))
1738         {
1739           puts (
1740                 "Usage: " PGM " [OPTION] [FILE]\n"
1741                 "Extract man pages from a Texinfo source.\n\n"
1742                 "  --html           render output as HTML\n"
1743                 "  --source NAME    use NAME as source field\n"
1744                 "  --release STRING use STRING as the release field\n"
1745                 "  --date EPOCH     use EPOCH as publication date\n"
1746                 "  --store          write output using @manpage name\n"
1747                 "  --select NAME    only output pages with @manpage NAME\n"
1748                 "  --verbose        enable extra informational output\n"
1749                 "  --debug          enable additional debug output\n"
1750                 "  --help           display this help and exit\n"
1751                 "  -I DIR           also search in include DIR\n"
1752                 "  -D MACRO         define MACRO to 1\n\n"
1753                 "With no FILE, or when FILE is -, read standard input.\n\n"
1754                 "Report bugs to <https://bugs.gnupg.org>.");
1755           exit (0);
1756         }
1757       else if (!strcmp (*argv, "--version"))
1758         {
1759           puts (PGM " " VERSION "\n"
1760                "Copyright (C) 2005, 2017 g10 Code GmbH\n"
1761                "This program comes with ABSOLUTELY NO WARRANTY.\n"
1762                "This is free software, and you are welcome to redistribute it\n"
1763                 "under certain conditions. See the file COPYING for details.");
1764           exit (0);
1765         }
1766       else if (!strcmp (*argv, "--html"))
1767         {
1768           htmlmode = 1;
1769           argc--; argv++;
1770         }
1771       else if (!strcmp (*argv, "--verbose"))
1772         {
1773           verbose = 1;
1774           argc--; argv++;
1775         }
1776       else if (!strcmp (*argv, "--quiet"))
1777         {
1778           quiet = 1;
1779           argc--; argv++;
1780         }
1781       else if (!strcmp (*argv, "--debug"))
1782         {
1783           verbose = debug = 1;
1784           argc--; argv++;
1785         }
1786       else if (!strcmp (*argv, "--source"))
1787         {
1788           argc--; argv++;
1789           if (argc)
1790             {
1791               opt_source = *argv;
1792               argc--; argv++;
1793             }
1794         }
1795       else if (!strcmp (*argv, "--release"))
1796         {
1797           argc--; argv++;
1798           if (argc)
1799             {
1800               opt_release = *argv;
1801               argc--; argv++;
1802             }
1803         }
1804       else if (!strcmp (*argv, "--date"))
1805         {
1806           argc--; argv++;
1807           if (argc)
1808             {
1809               opt_date = *argv;
1810               argc--; argv++;
1811             }
1812         }
1813       else if (!strcmp (*argv, "--store"))
1814         {
1815           opt_store = 1;
1816           argc--; argv++;
1817         }
1818       else if (!strcmp (*argv, "--select"))
1819         {
1820           argc--; argv++;
1821           if (argc)
1822             {
1823               opt_select = strrchr (*argv, '/');
1824               if (opt_select)
1825                 opt_select++;
1826               else
1827                 opt_select = *argv;
1828               argc--; argv++;
1829             }
1830         }
1831       else if (!strcmp (*argv, "-I"))
1832         {
1833           argc--; argv++;
1834           if (argc)
1835             {
1836               opt_include = *argv;
1837               argc--; argv++;
1838             }
1839         }
1840       else if (!strcmp (*argv, "-D"))
1841         {
1842           argc--; argv++;
1843           if (argc)
1844             {
1845               add_predefined_macro (*argv);
1846               argc--; argv++;
1847             }
1848         }
1849     }
1850 
1851   if (argc > 1)
1852     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1853 
1854   /* Take care of supplied timestamp for reproducible builds.  See
1855    * https://reproducible-builds.org/specs/source-date-epoch/  */
1856   if (!opt_date && (s = getenv ("SOURCE_DATE_EPOCH")) && *s)
1857     opt_date = s;
1858 
1859   /* Start processing. */
1860   if (argc && strcmp (*argv, "-"))
1861     {
1862       FILE *fp = fopen (*argv, "rb");
1863       if (!fp)
1864         die ("%s:0: can't open file: %s", *argv, strerror (errno));
1865       top_parse_file (*argv, fp);
1866       fclose (fp);
1867     }
1868   else
1869     top_parse_file ("-", stdin);
1870 
1871   return !!any_error;
1872 }
1873 
1874 
1875 /*
1876 Local Variables:
1877 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1878 End:
1879 */
1880