More changes on the way to remove secring.gpg.
[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, 2008 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 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
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 "1.0"
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   fputs (".\\\" Created from Texinfo source by yat2m " VERSION "\n", fp);
418
419   name = ascii_strupr (xstrdup (thepage.name));
420   p = strrchr (name, '.');
421   if (!p || !p[1])
422     {
423       err ("no section name in man page `%s'", thepage.name);
424       free (name);
425       return -1;
426     }
427   *p++ = 0;
428   fprintf (fp, ".TH %s %s %s \"%s\" \"%s\"\n",
429            name, p, isodatestring (), opt_release, opt_source);
430   return 0;
431 }
432
433
434 /* Process the texinfo command COMMAND (without the leading @) and
435    write output if needed to FP. REST is the remainer of the line
436    which should either point to an opening brace or to a white space.
437    The function returns the number of characters already processed
438    from REST.  LEN is the usable length of REST.  TABLE_LEVEL is used to
439    control the indentation of tables.  */
440 static size_t
441 proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len,
442                int *table_level, int *eol_action)
443 {
444   static struct {
445     const char *name;    /* Name of the command.  */
446     int what;            /* What to do with this command. */
447     const char *lead_in; /* String to print with a opening brace.  */
448     const char *lead_out;/* String to print with the closing brace. */
449   } cmdtbl[] = {
450     { "command", 0, "\\fB", "\\fR" },
451     { "code",    0, "\\fB", "\\fR" },
452     { "sc",      0, "\\fB", "\\fR" },
453     { "var",     0, "\\fI", "\\fR" },
454     { "samp",    0, "\\(aq", "\\(aq"  },
455     { "file",    0, "\\(oq\\fI","\\fR\\(cq" }, 
456     { "env",     0, "\\(oq\\fI","\\fR\\(cq" }, 
457     { "acronym", 0 },
458     { "dfn",     0 },
459     { "option",  0, "\\fB", "\\fR"   },
460     { "example", 1, ".RS 2\n.nf\n" },
461     { "smallexample", 1, ".RS 2\n.nf\n" },
462     { "asis",    7 },
463     { "anchor",  7 },
464     { "cartouche", 1 },
465     { "xref",    0, "see: [", "]" },
466     { "pxref",   0, "see: [", "]" },
467     { "uref",    0, "(\\fB", "\\fR)" },
468     { "footnote",0, " ([", "])" },
469     { "emph",    0, "\\fI", "\\fR" },
470     { "w",       1 },                                 
471     { "c",       5 },
472     { "opindex", 1 },
473     { "cpindex", 1 },
474     { "cindex",  1 },
475     { "noindent", 0 },
476     { "section", 1 },
477     { "chapter", 1 },
478     { "subsection", 6, "\n.SS " },
479     { "chapheading", 0},
480     { "item",    2, ".TP\n.B " },
481     { "itemx",   2, ".TP\n.B " },
482     { "table",   3 }, 
483     { "itemize",   3 }, 
484     { "bullet",  0, "* " },
485     { "end",     4 },
486     { "quotation",1, ".RS\n\\fB" },
487     { NULL }
488   };
489   size_t n;
490   int i;
491   const char *s;
492   const char *lead_out = NULL;
493   int ignore_args = 0;
494
495   for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
496     ;
497   if (cmdtbl[i].name)
498     {
499       s = cmdtbl[i].lead_in;
500       if (s)
501         fputs (s, fp);
502       lead_out = cmdtbl[i].lead_out;
503       switch (cmdtbl[i].what)
504         {
505         case 1: /* Throw away the entire line.  */
506           s = memchr (rest, '\n', len);
507           return s? (s-rest)+1 : len;  
508         case 2: /* Handle @item.  */
509           break;
510         case 3: /* Handle table.  */
511           if (++(*table_level) > 1)
512             fputs (".RS\n", fp);
513           /* Now throw away the entire line. */
514           s = memchr (rest, '\n', len);
515           return s? (s-rest)+1 : len;  
516           break;
517         case 4: /* Handle end.  */
518           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
519             ;
520           if (n >= 5 && !memcmp (s, "table", 5)
521               && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
522             {
523               if ((*table_level)-- > 1)
524                 fputs (".RE\n", fp);
525             }
526           else if (n >= 7 && !memcmp (s, "example", 7)
527               && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
528             {
529               fputs (".fi\n.RE\n", fp);
530             }
531           else if (n >= 12 && !memcmp (s, "smallexample", 12)
532               && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
533             {
534               fputs (".fi\n.RE\n", fp);
535             }
536           else if (n >= 9 && !memcmp (s, "quotation", 9)
537               && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
538             {
539               fputs ("\\fR\n.RE\n", fp);
540             }
541           /* Now throw away the entire line. */
542           s = memchr (rest, '\n', len);
543           return s? (s-rest)+1 : len;  
544         case 5: /* Handle special comments. */
545           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
546             ;
547           if (n >= 4 && !memcmp (s, "man:", 4))
548             {
549               for (s+=4, n-=4; n && *s != '\n'; n--, s++)
550                 putc (*s, fp);
551               putc ('\n', fp);
552             }
553           /* Now throw away the entire line. */
554           s = memchr (rest, '\n', len);
555           return s? (s-rest)+1 : len;  
556         case 6:
557           *eol_action = 1;
558           break;
559         case 7:
560           ignore_args = 1;
561           break;
562         default:
563           break;
564         }
565     }
566   else
567     {
568       macro_t m;
569
570       for (m = macrolist; m ; m = m->next)
571         if (!strcmp (m->name, command))
572             break;
573       if (m)
574         {
575           proc_texi_buffer (fp, m->value, strlen (m->value),
576                             table_level, eol_action);
577           ignore_args = 1; /* Parameterized macros are not yet supported. */
578         }
579       else
580         inf ("texinfo command `%s' not supported (%.*s)", command,
581              ((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
582     }
583
584   if (*rest == '{')
585     {
586       /* Find matching closing brace.  */
587       for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
588         if (*s == '{')
589           i++;
590         else if (*s == '}')
591           i--;
592       if (i)
593         {
594           err ("closing brace for command `%s' not found", command);
595           return len;
596         }
597       if (n > 2 && !ignore_args)
598         proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
599     }
600   else
601     n = 0;
602
603   if (lead_out)
604     fputs (lead_out, fp);
605
606   return n;
607 }
608
609
610
611 /* Process the string LINE with LEN bytes of Texinfo content. */
612 static void
613 proc_texi_buffer (FILE *fp, const char *line, size_t len,
614                   int *table_level, int *eol_action)
615 {
616   const char *s;
617   char cmdbuf[256];
618   int cmdidx = 0;
619   int in_cmd = 0;
620   size_t n;
621
622   for (s=line; *s && len; s++, len--)
623     {
624       if (in_cmd)
625         {
626           if (in_cmd == 1)
627             {
628               switch (*s)
629                 {
630                 case '@': case '{': case '}': 
631                   putc (*s, fp); in_cmd = 0; 
632                   break;
633                 case ':': /* Not ending a sentence flag.  */
634                   in_cmd = 0;
635                   break;
636                 case '.': case '!': case '?': /* Ending a sentence. */
637                   putc (*s, fp); in_cmd = 0; 
638                   break;
639                 case ' ': case '\t': case '\n': /* Non collapsing spaces.  */
640                   putc (*s, fp); in_cmd = 0; 
641                   break;
642                 default:
643                   cmdidx = 0;
644                   cmdbuf[cmdidx++] = *s;
645                   in_cmd++;
646                   break;
647                 }
648             }
649           else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
650             {
651               cmdbuf[cmdidx] = 0;
652               n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
653               assert (n <= len);
654               s += n; len -= n;
655               s--; len++;
656               in_cmd = 0;
657             }
658           else if (cmdidx < sizeof cmdbuf -1)  
659             cmdbuf[cmdidx++] = *s;
660           else
661             {
662               err ("texinfo command too long - ignored");
663               in_cmd = 0;
664             }
665         }
666       else if (*s == '@')
667         in_cmd = 1;
668       else if (*s == '\n')
669         {
670           switch (*eol_action)
671             {
672             case 1: /* Create a dummy paragraph. */
673               fputs ("\n\\ \n", fp);
674               break;
675             default:
676               putc (*s, fp);
677             }
678           *eol_action = 0;
679         }
680       else if (*s == '\\')
681         fputs ("\\\\", fp);
682       else
683         putc (*s, fp);
684     }
685
686   if (in_cmd > 1)
687     {
688       cmdbuf[cmdidx] = 0;
689       n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
690       assert (n <= len);
691       s += n; len -= n;
692       s--; len++;
693       in_cmd = 0;
694     }
695 }
696
697
698 /* Do something with the Texinfo line LINE.  */
699 static void
700 parse_texi_line (FILE *fp, const char *line, int *table_level)
701 {
702   int eol_action = 0;
703
704   /* A quick test whether there are any texinfo commands.  */
705   if (!strchr (line, '@'))
706     {
707       fputs (line, fp);
708       putc ('\n', fp);
709       return;
710     }
711   proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
712   putc ('\n', fp);
713 }
714
715
716 /* Write all the lines LINES to FP.  */
717 static void
718 write_content (FILE *fp, line_buffer_t lines)
719 {
720   line_buffer_t line;
721   int table_level = 0;
722
723   for (line = lines; line; line = line->next)
724     {
725       if (line->verbatim)
726         {
727           fputs (line->line, fp);
728           putc ('\n', fp);
729         }
730       else
731         {
732 /*           fputs ("TEXI---", fp); */
733 /*           fputs (line->line, fp); */
734 /*           fputs ("---\n", fp); */
735           parse_texi_line (fp, line->line, &table_level);
736         }
737     }  
738 }
739
740
741
742 static int
743 is_standard_section (const char *name)
744 {
745   int i;
746   const char *s;
747
748   for (i=0; (s=standard_sections[i]); i++)
749     if (!strcmp (s, name))
750       return 1;
751   return 0;
752 }
753
754
755 /* Finish a page; that is sort the data and write it out to the file.  */
756 static void
757 finish_page (void)
758 {
759   FILE *fp;
760   section_buffer_t sect = NULL;
761   int idx;
762   const char *s;
763   int i;
764
765   if (!thepage.name)
766     return; /* No page active.  */
767
768   if (verbose)
769     inf ("finishing page `%s'", thepage.name);
770
771   if (opt_select)
772     {
773       if (!strcmp (opt_select, thepage.name))
774         {
775           inf ("selected `%s'", thepage.name );
776           fp = stdout;
777         }
778       else
779         {
780           fp = fopen ( "/dev/null", "w" );
781           if (!fp)
782             die ("failed to open /dev/null: %s\n", strerror (errno));
783         }
784     }
785   else if (opt_store)
786     {
787       inf ("writing `%s'", thepage.name );
788       fp = fopen ( thepage.name, "w" );
789       if (!fp)
790         die ("failed to create `%s': %s\n", thepage.name, strerror (errno));
791     }
792   else
793     fp = stdout;
794
795   if (write_th (fp))
796     goto leave;
797
798   for (idx=0; (s=standard_sections[idx]); idx++)
799     {
800       for (i=0; i < thepage.n_sections; i++)
801         {
802           sect = thepage.sections + i;
803           if (sect->name && !strcmp (s, sect->name))
804             break;
805         }
806       if (i == thepage.n_sections)
807         sect = NULL;
808
809       if (sect)
810         {
811           fprintf (fp, ".SH %s\n", sect->name);
812           write_content (fp, sect->lines);
813           /* Now continue with all non standard sections directly
814              following this one. */
815           for (i++; i < thepage.n_sections; i++)
816             {
817               sect = thepage.sections + i;
818               if (sect->name && is_standard_section (sect->name))
819                 break;
820               if (sect->name)
821                 {
822                   fprintf (fp, ".SH %s\n", sect->name);
823                   write_content (fp, sect->lines);
824                 }
825             }
826           
827         }
828     }
829
830
831  leave:
832   if (fp != stdout)
833     fclose (fp);
834   free (thepage.name);
835   thepage.name = NULL;
836   /* FIXME: Cleanup the content.  */
837 }
838
839
840
841
842 /* Parse one Texinfo file and create manpages according to the
843    embedded instructions.  */
844 static void
845 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
846 {
847   char *line;
848   int lnr = 0;
849   /* Fixme: The following state variables don't carry over to include
850      files. */
851   int in_verbatim = 0;
852   int skip_to_end = 0;        /* Used to skip over menu entries. */
853   int skip_sect_line = 0;     /* Skip after @mansect.  */
854   int ifset_nesting = 0;      /* How often a ifset has been seen. */
855   int ifclear_nesting = 0;    /* How often a ifclear has been seen. */
856   int in_gpgone = 0;          /* Keep track of "@ifset gpgone" parts.  */
857   int not_in_gpgone = 0;      /* Keep track of "@ifclear gpgone" parts.  */
858   int not_in_man = 0;         /* Keep track of "@ifclear isman" parts.  */
859
860   /* Helper to define a macro. */
861   char *macroname = NULL;     
862   char *macrovalue = NULL; 
863   size_t macrovaluesize = 0;
864   size_t macrovalueused = 0;
865
866   line = xmalloc (LINESIZE);
867   while (fgets (line, LINESIZE, fp))
868     {
869       size_t n = strlen (line);
870       int got_line = 0;
871       char *p;
872
873       lnr++;
874       if (!n || line[n-1] != '\n')
875         {
876           err ("%s:%d: trailing linefeed missing, line too long or "
877                "embedded Nul character", fname, lnr);
878           break;
879         }
880       line[--n] = 0;
881
882       if (*line == '@')
883         {
884           for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
885             n++;
886           while (*p == ' ' || *p == '\t')
887             p++;
888         }
889       else
890         p = line;
891
892       /* Take action on macro.  */
893       if (macroname)
894         {
895           if (n == 4 && !memcmp (line, "@end", 4)
896               && (line[4]==' '||line[4]=='\t'||!line[4])
897               && !strncmp (p, "macro", 5)
898               && (p[5]==' '||p[5]=='\t'||!p[5]))
899             {
900               macro_t m;
901
902               if (macrovalueused)
903                 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
904               macrovalue[macrovalueused] = 0;     /* Terminate macro. */
905               macrovalue = xrealloc (macrovalue, macrovalueused+1);
906               
907               for (m= macrolist; m; m = m->next)
908                 if (!strcmp (m->name, macroname))
909                   break;
910               if (m)
911                 free (m->value);
912               else
913                 {
914                   m = xcalloc (1, sizeof *m + strlen (macroname));
915                   strcpy (m->name, macroname);
916                   m->next = macrolist;
917                   macrolist = m;
918                 }
919               m->value = macrovalue;
920               macrovalue = NULL;
921               free (macroname);
922               macroname = NULL;
923             }
924           else
925             {
926               if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
927                 {
928                   macrovaluesize += strlen (line) + 256;
929                   macrovalue = xrealloc (macrovalue,  macrovaluesize);
930                 }
931               strcpy (macrovalue+macrovalueused, line);
932               macrovalueused += strlen (line);
933               macrovalue[macrovalueused++] = '\n';
934             }
935           continue;
936         }
937
938
939       if (n >= 5 && !memcmp (line, "@node", 5)
940           && (line[5]==' '||line[5]=='\t'||!line[5]))
941         {
942           /* Completey ignore @node lines.  */
943           continue;
944         }
945
946
947       if (skip_sect_line)
948         {
949           skip_sect_line = 0;
950           if (!strncmp (line, "@section", 8)
951               || !strncmp (line, "@subsection", 11)
952               || !strncmp (line, "@chapheading", 12))
953             continue;
954         }
955
956       /* We only parse lines we need and ignore the rest.  There are a
957          few macros used to control this as well as one @ifset
958          command.  Parts we know about are saved away into containers
959          separate for each section. */
960
961       /* First process ifset/ifclear commands. */
962       if (*line == '@')
963         {
964           if (n == 6 && !memcmp (line, "@ifset", 6)
965                    && (line[6]==' '||line[6]=='\t'))
966             {
967               ifset_nesting++;
968
969               if (!strncmp (p, "manverb", 7) && (p[7]==' '||p[7]=='\t'||!p[7]))
970                 {
971                   if (in_verbatim)
972                     err ("%s:%d: nested \"@ifset manverb\"", fname, lnr);
973                   else
974                     in_verbatim = ifset_nesting;
975                 }
976               else if (!strncmp (p, "gpgone", 6)
977                        && (p[6]==' '||p[6]=='\t'||!p[6]))
978                 {
979                   if (in_gpgone)
980                     err ("%s:%d: nested \"@ifset gpgone\"", fname, lnr);
981                   else
982                     in_gpgone = ifset_nesting;
983                 }
984               continue;
985             }
986           else if (n == 4 && !memcmp (line, "@end", 4)
987                    && (line[4]==' '||line[4]=='\t')
988                    && !strncmp (p, "ifset", 5)
989                    && (p[5]==' '||p[5]=='\t'||!p[5]))
990             {
991               if (in_verbatim && ifset_nesting == in_verbatim)
992                 in_verbatim = 0;
993               if (in_gpgone && ifset_nesting == in_gpgone)
994                 in_gpgone = 0;
995
996               if (ifset_nesting)
997                 ifset_nesting--;
998               else
999                 err ("%s:%d: unbalanced \"@end ifset\"", fname, lnr);
1000               continue;
1001             }
1002           else if (n == 8 && !memcmp (line, "@ifclear", 8)
1003                    && (line[8]==' '||line[8]=='\t'))
1004             {
1005               ifclear_nesting++;
1006
1007               if (!strncmp (p, "gpgone", 6)
1008                   && (p[6]==' '||p[6]=='\t'||!p[6]))
1009                 {
1010                   if (not_in_gpgone)
1011                     err ("%s:%d: nested \"@ifclear gpgone\"", fname, lnr);
1012                   else
1013                     not_in_gpgone = ifclear_nesting;
1014                 }
1015
1016               else if (!strncmp (p, "isman", 5)
1017                        && (p[5]==' '||p[5]=='\t'||!p[5]))
1018                 {
1019                   if (not_in_man)
1020                     err ("%s:%d: nested \"@ifclear isman\"", fname, lnr);
1021                   else
1022                     not_in_man = ifclear_nesting;
1023                 }
1024
1025               continue;
1026             }
1027           else if (n == 4 && !memcmp (line, "@end", 4)
1028                    && (line[4]==' '||line[4]=='\t')
1029                    && !strncmp (p, "ifclear", 7)
1030                    && (p[7]==' '||p[7]=='\t'||!p[7]))
1031             {
1032               if (not_in_gpgone && ifclear_nesting == not_in_gpgone)
1033                 not_in_gpgone = 0;
1034               if (not_in_man && ifclear_nesting == not_in_man)
1035                 not_in_man = 0;
1036
1037               if (ifclear_nesting)
1038                 ifclear_nesting--;
1039               else
1040                 err ("%s:%d: unbalanced \"@end ifclear\"", fname, lnr);
1041               continue;
1042             }
1043         }
1044
1045       /* Take action on ifset/ifclear.  */
1046       if ( (in_gpgone && !gpgone_defined)
1047            || (not_in_gpgone && gpgone_defined)
1048            || not_in_man)
1049         continue;
1050
1051       /* Process commands. */
1052       if (*line == '@')
1053         {
1054           if (skip_to_end
1055               && n == 4 && !memcmp (line, "@end", 4)
1056               && (line[4]==' '||line[4]=='\t'||!line[4]))
1057             {
1058               skip_to_end = 0;
1059             }
1060           else if (in_verbatim)
1061             {
1062                 got_line = 1;
1063             }
1064           else if (n == 6 && !memcmp (line, "@macro", 6))
1065             {
1066               macroname = xstrdup (p);
1067               macrovalue = xmalloc ((macrovaluesize = 1024));
1068               macrovalueused = 0;
1069             }
1070           else if (n == 8 && !memcmp (line, "@manpage", 8))
1071             {
1072               free (*section_name);
1073               *section_name = NULL;
1074               finish_page ();
1075               start_page (p);
1076               in_pause = 0;
1077             }
1078           else if (n == 8 && !memcmp (line, "@mansect", 8))
1079             {
1080               if (!thepage.name)
1081                 err ("%s:%d: section outside of a man page", fname, lnr);
1082               else
1083                 {
1084                   free (*section_name);
1085                   *section_name = ascii_strupr (xstrdup (p));
1086                   in_pause = 0;
1087                   skip_sect_line = 1;
1088                 }
1089             }
1090           else if (n == 9 && !memcmp (line, "@manpause", 9))
1091             {
1092               if (!*section_name)
1093                 err ("%s:%d: pausing outside of a man section", fname, lnr);
1094               else if (in_pause)
1095                 err ("%s:%d: already pausing", fname, lnr);
1096               else
1097                 in_pause = 1;
1098             }
1099           else if (n == 8 && !memcmp (line, "@mancont", 8))
1100             {
1101               if (!*section_name)
1102                 err ("%s:%d: continue outside of a man section", fname, lnr);
1103               else if (!in_pause)
1104                 err ("%s:%d: continue while not pausing", fname, lnr);
1105               else
1106                 in_pause = 0;
1107             }
1108           else if (n == 5 && !memcmp (line, "@menu", 5)
1109                    && (line[5]==' '||line[5]=='\t'||!line[5]))
1110             {
1111               skip_to_end = 1;
1112             }
1113           else if (n == 8 && !memcmp (line, "@include", 8)
1114                    && (line[8]==' '||line[8]=='\t'||!line[8]))
1115             {
1116               char *incname = xstrdup (p);
1117               FILE *incfp = fopen (incname, "r");
1118
1119               if (!incfp && opt_include && *opt_include && *p != '/')
1120                 {
1121                   free (incname);
1122                   incname = xmalloc (strlen (opt_include) + 1
1123                                      + strlen (p) + 1);
1124                   strcpy (incname, opt_include);
1125                   if ( incname[strlen (incname)-1] != '/' )
1126                     strcat (incname, "/");
1127                   strcat (incname, p);
1128                   incfp = fopen (incname, "r");
1129                 }
1130
1131               if (!incfp)
1132                 err ("can't open include file `%s':%s",
1133                      incname, strerror (errno));
1134               else
1135                 {
1136                   parse_file (incname, incfp, section_name, in_pause);
1137                   fclose (incfp);
1138                 }
1139               free (incname);
1140             }
1141           else if (n == 4 && !memcmp (line, "@bye", 4)
1142                    && (line[4]==' '||line[4]=='\t'||!line[4]))
1143             {
1144               break;
1145             }
1146           else if (!skip_to_end)
1147             got_line = 1;
1148         }
1149       else if (!skip_to_end)
1150         got_line = 1;
1151
1152       if (got_line && in_verbatim)
1153         add_content (*section_name, line, 1);
1154       else if (got_line && thepage.name && *section_name && !in_pause)
1155         add_content (*section_name, line, 0);
1156
1157     }
1158   if (ferror (fp))
1159     err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1160   free (macroname);
1161   free (macrovalue);
1162   free (line);
1163 }
1164
1165
1166 static void
1167 top_parse_file (const char *fname, FILE *fp)
1168 {
1169   char *section_name = NULL;  /* Name of the current section or NULL
1170                                  if not in a section.  */
1171   while (macrolist)
1172     {
1173       macro_t m = macrolist->next;
1174       free (m->value);
1175       free (m);
1176       macrolist = m;
1177     }
1178
1179   parse_file (fname, fp, &section_name, 0);
1180   free (section_name);
1181   finish_page ();
1182 }
1183
1184
1185 int 
1186 main (int argc, char **argv)
1187 {
1188   int last_argc = -1;
1189
1190   opt_source = "GNU";
1191   opt_release = "";
1192
1193   if (argc)
1194     {
1195       argc--; argv++;
1196     }
1197   while (argc && last_argc != argc )
1198     {
1199       last_argc = argc;
1200       if (!strcmp (*argv, "--"))
1201         {
1202           argc--; argv++;
1203           break;
1204         }
1205       else if (!strcmp (*argv, "--help"))
1206         {
1207           puts (
1208                 "Usage: " PGM " [OPTION] [FILE]\n"
1209                 "Extract man pages from a Texinfo source.\n\n"
1210                 "  --source NAME    use NAME as source field\n"
1211                 "  --release STRING use STRING as the release field\n"
1212                 "  --store          write output using @manpage name\n"
1213                 "  --select NAME    only output pages with @manpage NAME\n"
1214                 "  --verbose        enable extra informational output\n"
1215                 "  --debug          enable additional debug output\n"
1216                 "  --help           display this help and exit\n"
1217                 "  -I DIR           also search in include DIR\n"
1218                 "  -D gpgone        the only useable define\n\n"
1219                 "With no FILE, or when FILE is -, read standard input.\n\n"
1220                 "Report bugs to <bugs@g10code.com>.");
1221           exit (0);
1222         }
1223       else if (!strcmp (*argv, "--version"))
1224         {
1225           puts (PGM " " VERSION "\n"
1226                "Copyright (C) 2005 g10 Code GmbH\n"
1227                "This program comes with ABSOLUTELY NO WARRANTY.\n"
1228                "This is free software, and you are welcome to redistribute it\n"
1229                 "under certain conditions. See the file COPYING for details.");
1230           exit (0);
1231         }
1232       else if (!strcmp (*argv, "--verbose"))
1233         {
1234           verbose = 1;
1235           argc--; argv++;
1236         }
1237       else if (!strcmp (*argv, "--quiet"))
1238         {
1239           quiet = 1;
1240           argc--; argv++;
1241         }
1242       else if (!strcmp (*argv, "--debug"))
1243         {
1244           verbose = debug = 1;
1245           argc--; argv++;
1246         }
1247       else if (!strcmp (*argv, "--source"))
1248         {
1249           argc--; argv++;
1250           if (argc)
1251             {
1252               opt_source = *argv;
1253               argc--; argv++;
1254             }
1255         }
1256       else if (!strcmp (*argv, "--release"))
1257         {
1258           argc--; argv++;
1259           if (argc)
1260             {
1261               opt_release = *argv;
1262               argc--; argv++;
1263             }
1264         }
1265       else if (!strcmp (*argv, "--store"))
1266         {
1267           opt_store = 1;
1268           argc--; argv++;
1269         }
1270       else if (!strcmp (*argv, "--select"))
1271         {
1272           argc--; argv++;
1273           if (argc)
1274             {
1275               opt_select = strrchr (*argv, '/');
1276               if (opt_select)
1277                 opt_select++;
1278               else 
1279                 opt_select = *argv;
1280               argc--; argv++;
1281             }
1282         }
1283       else if (!strcmp (*argv, "-I"))
1284         {
1285           argc--; argv++;
1286           if (argc)
1287             {
1288               opt_include = *argv;
1289               argc--; argv++;
1290             }
1291         }
1292       else if (!strcmp (*argv, "-D"))
1293         {
1294           argc--; argv++;
1295           if (argc)
1296             {
1297               if (!strcmp (*argv, "gpgone"))
1298                 gpgone_defined = 1;
1299               argc--; argv++;
1300             }
1301         }
1302     }          
1303  
1304   if (argc > 1)
1305     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1306
1307   /* Start processing. */
1308   if (argc && strcmp (*argv, "-"))
1309     {
1310       FILE *fp = fopen (*argv, "rb");
1311       if (!fp)
1312         die ("%s:0: can't open file: %s", *argv, strerror (errno));
1313       top_parse_file (*argv, fp);
1314       fclose (fp);
1315     }
1316   else
1317     top_parse_file ("-", stdin);
1318
1319   return !!any_error;
1320 }
1321
1322
1323 /*
1324 Local Variables:
1325 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1326 End:
1327 */