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