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