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