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