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