doc: Add files and envvars to a new index.
[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     { "url",     0, "\\fB", "\\fR" },
688     { "sc",      0, "\\fB", "\\fR" },
689     { "var",     0, "\\fI", "\\fR" },
690     { "samp",    0, "\\(aq", "\\(aq"  },
691     { "file",    0, "\\(oq\\fI","\\fR\\(cq" },
692     { "env",     0, "\\(oq\\fI","\\fR\\(cq" },
693     { "acronym", 0 },
694     { "dfn",     0 },
695     { "option",  0, "\\fB", "\\fR"   },
696     { "example", 1, ".RS 2\n.nf\n" },
697     { "smallexample", 1, ".RS 2\n.nf\n" },
698     { "asis",    7 },
699     { "anchor",  7 },
700     { "cartouche", 1 },
701     { "xref",    0, "see: [", "]" },
702     { "pxref",   0, "see: [", "]" },
703     { "uref",    0, "(\\fB", "\\fR)" },
704     { "footnote",0, " ([", "])" },
705     { "emph",    0, "\\fI", "\\fR" },
706     { "w",       1 },
707     { "c",       5 },
708     { "efindex", 1 },
709     { "opindex", 1 },
710     { "cpindex", 1 },
711     { "cindex",  1 },
712     { "noindent", 0 },
713     { "section", 1 },
714     { "chapter", 1 },
715     { "subsection", 6, "\n.SS " },
716     { "chapheading", 0},
717     { "item",    2, ".TP\n.B " },
718     { "itemx",   2, ".TP\n.B " },
719     { "table",   3 },
720     { "itemize",   3 },
721     { "bullet",  0, "* " },
722     { "*",       0, "\n.br"},
723     { "/",       0 },
724     { "end",     4 },
725     { "quotation",1, ".RS\n\\fB" },
726     { "value", 8 },
727     { NULL }
728   };
729   size_t n;
730   int i;
731   const char *s;
732   const char *lead_out = NULL;
733   int ignore_args = 0;
734
735   for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
736     ;
737   if (cmdtbl[i].name)
738     {
739       s = cmdtbl[i].lead_in;
740       if (s)
741         fputs (s, fp);
742       lead_out = cmdtbl[i].lead_out;
743       switch (cmdtbl[i].what)
744         {
745         case 1: /* Throw away the entire line.  */
746           s = memchr (rest, '\n', len);
747           return s? (s-rest)+1 : len;
748         case 2: /* Handle @item.  */
749           break;
750         case 3: /* Handle table.  */
751           if (++(*table_level) > 1)
752             fputs (".RS\n", fp);
753           /* Now throw away the entire line. */
754           s = memchr (rest, '\n', len);
755           return s? (s-rest)+1 : len;
756           break;
757         case 4: /* Handle end.  */
758           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
759             ;
760           if (n >= 5 && !memcmp (s, "table", 5)
761               && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
762             {
763               if ((*table_level)-- > 1)
764                 fputs (".RE\n", fp);
765             }
766           else if (n >= 7 && !memcmp (s, "example", 7)
767               && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
768             {
769               fputs (".fi\n.RE\n", fp);
770             }
771           else if (n >= 12 && !memcmp (s, "smallexample", 12)
772               && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
773             {
774               fputs (".fi\n.RE\n", fp);
775             }
776           else if (n >= 9 && !memcmp (s, "quotation", 9)
777               && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
778             {
779               fputs ("\\fR\n.RE\n", fp);
780             }
781           /* Now throw away the entire line. */
782           s = memchr (rest, '\n', len);
783           return s? (s-rest)+1 : len;
784         case 5: /* Handle special comments. */
785           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
786             ;
787           if (n >= 4 && !memcmp (s, "man:", 4))
788             {
789               for (s+=4, n-=4; n && *s != '\n'; n--, s++)
790                 putc (*s, fp);
791               putc ('\n', fp);
792             }
793           /* Now throw away the entire line. */
794           s = memchr (rest, '\n', len);
795           return s? (s-rest)+1 : len;
796         case 6:
797           *eol_action = 1;
798           break;
799         case 7:
800           ignore_args = 1;
801           break;
802         case 8:
803           ignore_args = 1;
804           if (*rest != '{')
805             {
806               err ("opening brace for command '%s' missing", command);
807               return len;
808             }
809           else
810             {
811               /* Find closing brace.  */
812               for (s=rest+1, n=1; *s && n < len; s++, n++)
813                 if (*s == '}')
814                   break;
815               if (*s != '}')
816                 {
817                   err ("closing brace for command '%s' not found", command);
818                   return len;
819                 }
820               else
821                 {
822                   size_t len = s - (rest + 1);
823                   macro_t m;
824
825                   for (m = variablelist; m; m = m->next)
826                     if (strlen (m->name) == len
827                         &&!strncmp (m->name, rest+1, len))
828                       break;
829                   if (m)
830                     fputs (m->value, fp);
831                   else
832                     inf ("texinfo variable '%.*s' is not set",
833                          (int)len, rest+1);
834                 }
835             }
836           break;
837         default:
838           break;
839         }
840     }
841   else /* macro */
842     {
843       macro_t m;
844
845       for (m = macrolist; m ; m = m->next)
846         if (!strcmp (m->name, command))
847             break;
848       if (m)
849         {
850           proc_texi_buffer (fp, m->value, strlen (m->value),
851                             table_level, eol_action);
852           ignore_args = 1; /* Parameterized macros are not yet supported. */
853         }
854       else
855         inf ("texinfo command '%s' not supported (%.*s)", command,
856              ((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
857     }
858
859   if (*rest == '{')
860     {
861       /* Find matching closing brace.  */
862       for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
863         if (*s == '{')
864           i++;
865         else if (*s == '}')
866           i--;
867       if (i)
868         {
869           err ("closing brace for command '%s' not found", command);
870           return len;
871         }
872       if (n > 2 && !ignore_args)
873         proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
874     }
875   else
876     n = 0;
877
878   if (lead_out)
879     fputs (lead_out, fp);
880
881   return n;
882 }
883
884
885
886 /* Process the string LINE with LEN bytes of Texinfo content. */
887 static void
888 proc_texi_buffer (FILE *fp, const char *line, size_t len,
889                   int *table_level, int *eol_action)
890 {
891   const char *s;
892   char cmdbuf[256];
893   int cmdidx = 0;
894   int in_cmd = 0;
895   size_t n;
896
897   for (s=line; *s && len; s++, len--)
898     {
899       if (in_cmd)
900         {
901           if (in_cmd == 1)
902             {
903               switch (*s)
904                 {
905                 case '@': case '{': case '}':
906                   putc (*s, fp); in_cmd = 0;
907                   break;
908                 case ':': /* Not ending a sentence flag.  */
909                   in_cmd = 0;
910                   break;
911                 case '.': case '!': case '?': /* Ending a sentence. */
912                   putc (*s, fp); in_cmd = 0;
913                   break;
914                 case ' ': case '\t': case '\n': /* Non collapsing spaces.  */
915                   putc (*s, fp); in_cmd = 0;
916                   break;
917                 default:
918                   cmdidx = 0;
919                   cmdbuf[cmdidx++] = *s;
920                   in_cmd++;
921                   break;
922                 }
923             }
924           else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
925             {
926               cmdbuf[cmdidx] = 0;
927               n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
928               assert (n <= len);
929               s += n; len -= n;
930               s--; len++;
931               in_cmd = 0;
932             }
933           else if (cmdidx < sizeof cmdbuf -1)
934             cmdbuf[cmdidx++] = *s;
935           else
936             {
937               err ("texinfo command too long - ignored");
938               in_cmd = 0;
939             }
940         }
941       else if (*s == '@')
942         in_cmd = 1;
943       else if (*s == '\n')
944         {
945           switch (*eol_action)
946             {
947             case 1: /* Create a dummy paragraph. */
948               fputs ("\n\\ \n", fp);
949               break;
950             default:
951               putc (*s, fp);
952             }
953           *eol_action = 0;
954         }
955       else if (*s == '\\')
956         fputs ("\\\\", fp);
957       else
958         putc (*s, fp);
959     }
960
961   if (in_cmd > 1)
962     {
963       cmdbuf[cmdidx] = 0;
964       n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
965       assert (n <= len);
966       s += n; len -= n;
967       s--; len++;
968       in_cmd = 0;
969     }
970 }
971
972
973 /* Do something with the Texinfo line LINE.  */
974 static void
975 parse_texi_line (FILE *fp, const char *line, int *table_level)
976 {
977   int eol_action = 0;
978
979   /* A quick test whether there are any texinfo commands.  */
980   if (!strchr (line, '@'))
981     {
982       fputs (line, fp);
983       putc ('\n', fp);
984       return;
985     }
986   proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
987   putc ('\n', fp);
988 }
989
990
991 /* Write all the lines LINES to FP.  */
992 static void
993 write_content (FILE *fp, line_buffer_t lines)
994 {
995   line_buffer_t line;
996   int table_level = 0;
997
998   for (line = lines; line; line = line->next)
999     {
1000       if (line->verbatim)
1001         {
1002           fputs (line->line, fp);
1003           putc ('\n', fp);
1004         }
1005       else
1006         {
1007 /*           fputs ("TEXI---", fp); */
1008 /*           fputs (line->line, fp); */
1009 /*           fputs ("---\n", fp); */
1010           parse_texi_line (fp, line->line, &table_level);
1011         }
1012     }
1013 }
1014
1015
1016
1017 static int
1018 is_standard_section (const char *name)
1019 {
1020   int i;
1021   const char *s;
1022
1023   for (i=0; (s=standard_sections[i]); i++)
1024     if (!strcmp (s, name))
1025       return 1;
1026   return 0;
1027 }
1028
1029
1030 /* Finish a page; that is sort the data and write it out to the file.  */
1031 static void
1032 finish_page (void)
1033 {
1034   FILE *fp;
1035   section_buffer_t sect = NULL;
1036   int idx;
1037   const char *s;
1038   int i;
1039
1040   if (!thepage.name)
1041     return; /* No page active.  */
1042
1043   if (verbose)
1044     inf ("finishing page '%s'", thepage.name);
1045
1046   if (opt_select)
1047     {
1048       if (!strcmp (opt_select, thepage.name))
1049         {
1050           inf ("selected '%s'", thepage.name );
1051           fp = stdout;
1052         }
1053       else
1054         {
1055           fp = fopen ( "/dev/null", "w" );
1056           if (!fp)
1057             die ("failed to open /dev/null: %s\n", strerror (errno));
1058         }
1059     }
1060   else if (opt_store)
1061     {
1062       inf ("writing '%s'", thepage.name );
1063       fp = fopen ( thepage.name, "w" );
1064       if (!fp)
1065         die ("failed to create '%s': %s\n", thepage.name, strerror (errno));
1066     }
1067   else
1068     fp = stdout;
1069
1070   if (write_th (fp))
1071     goto leave;
1072
1073   for (idx=0; (s=standard_sections[idx]); idx++)
1074     {
1075       for (i=0; i < thepage.n_sections; i++)
1076         {
1077           sect = thepage.sections + i;
1078           if (sect->name && !strcmp (s, sect->name))
1079             break;
1080         }
1081       if (i == thepage.n_sections)
1082         sect = NULL;
1083
1084       if (sect)
1085         {
1086           fprintf (fp, ".SH %s\n", sect->name);
1087           write_content (fp, sect->lines);
1088           /* Now continue with all non standard sections directly
1089              following this one. */
1090           for (i++; i < thepage.n_sections; i++)
1091             {
1092               sect = thepage.sections + i;
1093               if (sect->name && is_standard_section (sect->name))
1094                 break;
1095               if (sect->name)
1096                 {
1097                   fprintf (fp, ".SH %s\n", sect->name);
1098                   write_content (fp, sect->lines);
1099                 }
1100             }
1101
1102         }
1103     }
1104
1105
1106  leave:
1107   if (fp != stdout)
1108     fclose (fp);
1109   free (thepage.name);
1110   thepage.name = NULL;
1111   /* FIXME: Cleanup the content.  */
1112 }
1113
1114
1115
1116
1117 /* Parse one Texinfo file and create manpages according to the
1118    embedded instructions.  */
1119 static void
1120 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
1121 {
1122   char *line;
1123   int lnr = 0;
1124   /* Fixme: The following state variables don't carry over to include
1125      files. */
1126   int skip_to_end = 0;        /* Used to skip over menu entries. */
1127   int skip_sect_line = 0;     /* Skip after @mansect.  */
1128   int item_indent = 0;        /* How far is the current @item indented.  */
1129
1130   /* Helper to define a macro. */
1131   char *macroname = NULL;
1132   char *macrovalue = NULL;
1133   size_t macrovaluesize = 0;
1134   size_t macrovalueused = 0;
1135
1136   line = xmalloc (LINESIZE);
1137   while (fgets (line, LINESIZE, fp))
1138     {
1139       size_t n = strlen (line);
1140       int got_line = 0;
1141       char *p, *pend;
1142
1143       lnr++;
1144       if (!n || line[n-1] != '\n')
1145         {
1146           err ("%s:%d: trailing linefeed missing, line too long or "
1147                "embedded Nul character", fname, lnr);
1148           break;
1149         }
1150       line[--n] = 0;
1151
1152       /* Kludge to allow indentation of tables.  */
1153       for (p=line; *p == ' ' || *p == '\t'; p++)
1154         ;
1155       if (*p)
1156         {
1157           if (*p == '@' && !strncmp (p+1, "item", 4))
1158             item_indent = p - line;  /* Set a new indent level.  */
1159           else if (p - line < item_indent)
1160             item_indent = 0;         /* Switch off indention.  */
1161
1162           if (item_indent)
1163             {
1164               memmove (line, line+item_indent, n - item_indent + 1);
1165               n -= item_indent;
1166             }
1167         }
1168
1169
1170       if (*line == '@')
1171         {
1172           for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
1173             n++;
1174           while (*p == ' ' || *p == '\t')
1175             p++;
1176         }
1177       else
1178         p = line;
1179
1180       /* Take action on macro.  */
1181       if (macroname)
1182         {
1183           if (n == 4 && !memcmp (line, "@end", 4)
1184               && (line[4]==' '||line[4]=='\t'||!line[4])
1185               && !strncmp (p, "macro", 5)
1186               && (p[5]==' '||p[5]=='\t'||!p[5]))
1187             {
1188               if (macrovalueused)
1189                 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
1190               macrovalue[macrovalueused] = 0;     /* Terminate macro. */
1191               macrovalue = xrealloc (macrovalue, macrovalueused+1);
1192
1193               set_macro (macroname, macrovalue);
1194               macrovalue = NULL;
1195               free (macroname);
1196               macroname = NULL;
1197             }
1198           else
1199             {
1200               if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
1201                 {
1202                   macrovaluesize += strlen (line) + 256;
1203                   macrovalue = xrealloc (macrovalue,  macrovaluesize);
1204                 }
1205               strcpy (macrovalue+macrovalueused, line);
1206               macrovalueused += strlen (line);
1207               macrovalue[macrovalueused++] = '\n';
1208             }
1209           continue;
1210         }
1211
1212
1213       if (n >= 5 && !memcmp (line, "@node", 5)
1214           && (line[5]==' '||line[5]=='\t'||!line[5]))
1215         {
1216           /* Completey ignore @node lines.  */
1217           continue;
1218         }
1219
1220
1221       if (skip_sect_line)
1222         {
1223           skip_sect_line = 0;
1224           if (!strncmp (line, "@section", 8)
1225               || !strncmp (line, "@subsection", 11)
1226               || !strncmp (line, "@chapheading", 12))
1227             continue;
1228         }
1229
1230       /* We only parse lines we need and ignore the rest.  There are a
1231          few macros used to control this as well as one @ifset
1232          command.  Parts we know about are saved away into containers
1233          separate for each section. */
1234
1235       /* First process ifset/ifclear commands. */
1236       if (*line == '@')
1237         {
1238           if (n == 6 && !memcmp (line, "@ifset", 6)
1239                    && (line[6]==' '||line[6]=='\t'))
1240             {
1241               for (p=line+7; *p == ' ' || *p == '\t'; p++)
1242                 ;
1243               if (!*p)
1244                 {
1245                   err ("%s:%d: name missing after \"@ifset\"", fname, lnr);
1246                   continue;
1247                 }
1248               for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1249                 ;
1250               *pend = 0;  /* Ignore rest of the line.  */
1251               push_condition (p, 1, fname, lnr);
1252               continue;
1253             }
1254           else if (n == 8 && !memcmp (line, "@ifclear", 8)
1255                    && (line[8]==' '||line[8]=='\t'))
1256             {
1257               for (p=line+9; *p == ' ' || *p == '\t'; p++)
1258                 ;
1259               if (!*p)
1260                 {
1261                   err ("%s:%d: name missing after \"@ifsclear\"", fname, lnr);
1262                   continue;
1263                 }
1264               for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1265                 ;
1266               *pend = 0;  /* Ignore rest of the line.  */
1267               push_condition (p, 0, fname, lnr);
1268               continue;
1269             }
1270           else if (n == 4 && !memcmp (line, "@end", 4)
1271                    && (line[4]==' '||line[4]=='\t')
1272                    && !strncmp (p, "ifset", 5)
1273                    && (p[5]==' '||p[5]=='\t'||!p[5]))
1274             {
1275               pop_condition (1, fname, lnr);
1276               continue;
1277             }
1278           else if (n == 4 && !memcmp (line, "@end", 4)
1279                    && (line[4]==' '||line[4]=='\t')
1280                    && !strncmp (p, "ifclear", 7)
1281                    && (p[7]==' '||p[7]=='\t'||!p[7]))
1282             {
1283               pop_condition (0, fname, lnr);
1284               continue;
1285             }
1286         }
1287
1288       /* Take action on ifset/ifclear.  */
1289       if (!cond_is_active)
1290         continue;
1291
1292       /* Process commands. */
1293       if (*line == '@')
1294         {
1295           if (skip_to_end
1296               && n == 4 && !memcmp (line, "@end", 4)
1297               && (line[4]==' '||line[4]=='\t'||!line[4]))
1298             {
1299               skip_to_end = 0;
1300             }
1301           else if (cond_in_verbatim)
1302             {
1303                 got_line = 1;
1304             }
1305           else if (n == 6 && !memcmp (line, "@macro", 6))
1306             {
1307               macroname = xstrdup (p);
1308               macrovalue = xmalloc ((macrovaluesize = 1024));
1309               macrovalueused = 0;
1310             }
1311           else if (n == 4 && !memcmp (line, "@set", 4))
1312             {
1313               set_variable (p);
1314             }
1315           else if (n == 8 && !memcmp (line, "@manpage", 8))
1316             {
1317               free (*section_name);
1318               *section_name = NULL;
1319               finish_page ();
1320               start_page (p);
1321               in_pause = 0;
1322             }
1323           else if (n == 8 && !memcmp (line, "@mansect", 8))
1324             {
1325               if (!thepage.name)
1326                 err ("%s:%d: section outside of a man page", fname, lnr);
1327               else
1328                 {
1329                   free (*section_name);
1330                   *section_name = ascii_strupr (xstrdup (p));
1331                   in_pause = 0;
1332                   skip_sect_line = 1;
1333                 }
1334             }
1335           else if (n == 9 && !memcmp (line, "@manpause", 9))
1336             {
1337               if (!*section_name)
1338                 err ("%s:%d: pausing outside of a man section", fname, lnr);
1339               else if (in_pause)
1340                 err ("%s:%d: already pausing", fname, lnr);
1341               else
1342                 in_pause = 1;
1343             }
1344           else if (n == 8 && !memcmp (line, "@mancont", 8))
1345             {
1346               if (!*section_name)
1347                 err ("%s:%d: continue outside of a man section", fname, lnr);
1348               else if (!in_pause)
1349                 err ("%s:%d: continue while not pausing", fname, lnr);
1350               else
1351                 in_pause = 0;
1352             }
1353           else if (n == 5 && !memcmp (line, "@menu", 5)
1354                    && (line[5]==' '||line[5]=='\t'||!line[5]))
1355             {
1356               skip_to_end = 1;
1357             }
1358           else if (n == 8 && !memcmp (line, "@include", 8)
1359                    && (line[8]==' '||line[8]=='\t'||!line[8]))
1360             {
1361               char *incname = xstrdup (p);
1362               FILE *incfp = fopen (incname, "r");
1363
1364               if (!incfp && opt_include && *opt_include && *p != '/')
1365                 {
1366                   free (incname);
1367                   incname = xmalloc (strlen (opt_include) + 1
1368                                      + strlen (p) + 1);
1369                   strcpy (incname, opt_include);
1370                   if ( incname[strlen (incname)-1] != '/' )
1371                     strcat (incname, "/");
1372                   strcat (incname, p);
1373                   incfp = fopen (incname, "r");
1374                 }
1375
1376               if (!incfp)
1377                 err ("can't open include file '%s': %s",
1378                      incname, strerror (errno));
1379               else
1380                 {
1381                   parse_file (incname, incfp, section_name, in_pause);
1382                   fclose (incfp);
1383                 }
1384               free (incname);
1385             }
1386           else if (n == 4 && !memcmp (line, "@bye", 4)
1387                    && (line[4]==' '||line[4]=='\t'||!line[4]))
1388             {
1389               break;
1390             }
1391           else if (!skip_to_end)
1392             got_line = 1;
1393         }
1394       else if (!skip_to_end)
1395         got_line = 1;
1396
1397       if (got_line && cond_in_verbatim)
1398         add_content (*section_name, line, 1);
1399       else if (got_line && thepage.name && *section_name && !in_pause)
1400         add_content (*section_name, line, 0);
1401
1402     }
1403   if (ferror (fp))
1404     err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1405   free (macroname);
1406   free (macrovalue);
1407   free (line);
1408 }
1409
1410
1411 static void
1412 top_parse_file (const char *fname, FILE *fp)
1413 {
1414   char *section_name = NULL;  /* Name of the current section or NULL
1415                                  if not in a section.  */
1416   macro_t m;
1417
1418   while (macrolist)
1419     {
1420       macro_t next = macrolist->next;
1421       free (macrolist->value);
1422       free (macrolist);
1423       macrolist = next;
1424     }
1425   while (variablelist)
1426     {
1427       macro_t next = variablelist->next;
1428       free (variablelist->value);
1429       free (variablelist);
1430       variablelist = next;
1431     }
1432   for (m=predefinedmacrolist; m; m = m->next)
1433     set_macro (m->name, xstrdup ("1"));
1434   cond_is_active = 1;
1435   cond_in_verbatim = 0;
1436
1437   parse_file (fname, fp, &section_name, 0);
1438   free (section_name);
1439   finish_page ();
1440 }
1441
1442
1443 int
1444 main (int argc, char **argv)
1445 {
1446   int last_argc = -1;
1447
1448   opt_source = "GNU";
1449   opt_release = "";
1450
1451   /* Define default macros.  The trick is that these macros are not
1452      defined when using the actual texinfo renderer. */
1453   add_predefined_macro ("isman");
1454   add_predefined_macro ("manverb");
1455
1456   /* Option parsing.  */
1457   if (argc)
1458     {
1459       argc--; argv++;
1460     }
1461   while (argc && last_argc != argc )
1462     {
1463       last_argc = argc;
1464       if (!strcmp (*argv, "--"))
1465         {
1466           argc--; argv++;
1467           break;
1468         }
1469       else if (!strcmp (*argv, "--help"))
1470         {
1471           puts (
1472                 "Usage: " PGM " [OPTION] [FILE]\n"
1473                 "Extract man pages from a Texinfo source.\n\n"
1474                 "  --source NAME    use NAME as source field\n"
1475                 "  --release STRING use STRING as the release field\n"
1476                 "  --date EPOCH     use EPOCH as publication date\n"
1477                 "  --store          write output using @manpage name\n"
1478                 "  --select NAME    only output pages with @manpage NAME\n"
1479                 "  --verbose        enable extra informational output\n"
1480                 "  --debug          enable additional debug output\n"
1481                 "  --help           display this help and exit\n"
1482                 "  -I DIR           also search in include DIR\n"
1483                 "  -D gpgone        the only usable define\n\n"
1484                 "With no FILE, or when FILE is -, read standard input.\n\n"
1485                 "Report bugs to <bugs@g10code.com>.");
1486           exit (0);
1487         }
1488       else if (!strcmp (*argv, "--version"))
1489         {
1490           puts (PGM " " VERSION "\n"
1491                "Copyright (C) 2005 g10 Code GmbH\n"
1492                "This program comes with ABSOLUTELY NO WARRANTY.\n"
1493                "This is free software, and you are welcome to redistribute it\n"
1494                 "under certain conditions. See the file COPYING for details.");
1495           exit (0);
1496         }
1497       else if (!strcmp (*argv, "--verbose"))
1498         {
1499           verbose = 1;
1500           argc--; argv++;
1501         }
1502       else if (!strcmp (*argv, "--quiet"))
1503         {
1504           quiet = 1;
1505           argc--; argv++;
1506         }
1507       else if (!strcmp (*argv, "--debug"))
1508         {
1509           verbose = debug = 1;
1510           argc--; argv++;
1511         }
1512       else if (!strcmp (*argv, "--source"))
1513         {
1514           argc--; argv++;
1515           if (argc)
1516             {
1517               opt_source = *argv;
1518               argc--; argv++;
1519             }
1520         }
1521       else if (!strcmp (*argv, "--release"))
1522         {
1523           argc--; argv++;
1524           if (argc)
1525             {
1526               opt_release = *argv;
1527               argc--; argv++;
1528             }
1529         }
1530       else if (!strcmp (*argv, "--date"))
1531         {
1532           argc--; argv++;
1533           if (argc)
1534             {
1535               opt_date = *argv;
1536               argc--; argv++;
1537             }
1538         }
1539       else if (!strcmp (*argv, "--store"))
1540         {
1541           opt_store = 1;
1542           argc--; argv++;
1543         }
1544       else if (!strcmp (*argv, "--select"))
1545         {
1546           argc--; argv++;
1547           if (argc)
1548             {
1549               opt_select = strrchr (*argv, '/');
1550               if (opt_select)
1551                 opt_select++;
1552               else
1553                 opt_select = *argv;
1554               argc--; argv++;
1555             }
1556         }
1557       else if (!strcmp (*argv, "-I"))
1558         {
1559           argc--; argv++;
1560           if (argc)
1561             {
1562               opt_include = *argv;
1563               argc--; argv++;
1564             }
1565         }
1566       else if (!strcmp (*argv, "-D"))
1567         {
1568           argc--; argv++;
1569           if (argc)
1570             {
1571               add_predefined_macro (*argv);
1572               argc--; argv++;
1573             }
1574         }
1575     }
1576
1577   if (argc > 1)
1578     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1579
1580   /* Start processing. */
1581   if (argc && strcmp (*argv, "-"))
1582     {
1583       FILE *fp = fopen (*argv, "rb");
1584       if (!fp)
1585         die ("%s:0: can't open file: %s", *argv, strerror (errno));
1586       top_parse_file (*argv, fp);
1587       fclose (fp);
1588     }
1589   else
1590     top_parse_file ("-", stdin);
1591
1592   return !!any_error;
1593 }
1594
1595
1596 /*
1597 Local Variables:
1598 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1599 End:
1600 */