First steps towards supporting W32.
[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 2 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  * 02110-1301, USA.
19  */
20
21 /*
22     This is a simple textinfo to man page converter.  It needs some
23     special markup in th e texinfo and tries best to get a create man
24     page.  It has been designed for the GnuPG man pages and thus only
25     a few texinfo commands are supported.
26
27     To use this you need to add the following macros into your texinfo
28     source:
29
30       @macro manpage {a}
31       @end macro
32       @macro mansect {a}
33       @end macro
34       @macro manpause 
35       @end macro
36       @macro mancont
37       @end macro
38       
39     They are used by yat2m to select parts of the Texinfo which should
40     go into the man page. These macros need to be used without leading
41     left space. Processing starts after a "manpage" macro has been
42     seen.  "mansect" identifies the section and yat2m make sure to
43     emit the sections in the proper order.  Note that @mansect skips
44     the next input line if that line begins with @section, @subsection or
45     @chapheading.
46
47     To insert verbatim troff markup, the follwing texinfo code may be
48     used:
49
50       @ifset manverb
51       .B whateever you want
52       @end ifset
53
54     alternativly a special comment may be used:
55
56       @c man:.B whatever you want
57
58     This is useful in case you need just one line. If you want to
59     include parts only in the man page but keep the texinfo
60     translation you may use:
61
62       @ifset isman
63       stuff to be rendered only on man pages
64       @end ifset
65
66     or to exclude stuff from man pages:
67
68       @ifclear isman
69       stuff not to be rendered on man pages
70       @end ifclear
71
72     the keyword @section is ignored, however @subsection gets rendered
73     as ".SS".  @menu is completely skipped. Several man pages may be
74     extracted from one file, either using the --store or the --select
75     option.
76
77
78 */
79
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <stddef.h>
83 #include <string.h>
84 #include <errno.h>
85 #include <stdarg.h>
86 #include <assert.h>
87 #include <ctype.h>
88 #include <time.h>
89
90
91 #define PGM "yat2m"
92 #define VERSION "0.5"
93
94 /* The maximum length of a line including the linefeed and one extra
95    character. */
96 #define LINESIZE 1024
97
98 /* Option flags. */
99 static int verbose;
100 static int quiet;
101 static int debug;
102 static const char *opt_source; 
103 static const char *opt_release; 
104 static const char *opt_select;
105 static const char *opt_include;
106 static int opt_store;
107
108 /* The only define we understand is -D gpgone.  Thus we need a simple
109    boolean tro track it. */
110 static int gpgone_defined;
111
112 /* Flag to keep track whether any error occurred.  */
113 static int any_error;
114
115
116 /* Object to keep macro definitions.  */
117 struct macro_s
118 {
119   struct macro_s *next;
120   char *value;  /* Malloced value. */
121   char name[1];
122 };
123 typedef struct macro_s *macro_t;
124
125 /* List of all defined macros. */
126 static macro_t macrolist;
127
128
129 /* Object to store one line of content.  */
130 struct line_buffer_s
131 {
132   struct line_buffer_s *next;
133   int verbatim;  /* True if LINE contains verbatim data.  The default
134                     is Texinfo source.  */
135   char *line;
136 };
137 typedef struct line_buffer_s *line_buffer_t;
138
139
140 /* Object to collect the data of a section.  */
141 struct section_buffer_s
142 {
143   char *name;           /* Malloced name of the section. This may be
144                            NULL to indicate this slot is not used.  */
145   line_buffer_t lines;  /* Linked list with the lines of the section.  */
146   line_buffer_t *lines_tail; /* Helper for faster appending to the
147                                 linked list.  */
148   line_buffer_t last_line;   /* Points to the last line appended.  */
149 };
150 typedef struct section_buffer_s *section_buffer_t;
151
152 /* Variable to keep info about the current page together.  */
153 static struct 
154 {
155   /* Filename of the current page or NULL if no page is active.  Malloced. */
156   char *name;
157
158   /* Number of allocated elements in SECTIONS below.  */
159   size_t n_sections;       
160   /* Array with the data of the sections.  */
161   section_buffer_t sections; 
162
163 } thepage;
164
165
166 /* The list of standard section names.  COMMANDS and ASSUAN are GnuPG
167    specific. */
168 static const char * const standard_sections[] = 
169   { "NAME",  "SYNOPSIS",  "DESCRIPTION",
170     "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS",
171     "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES",
172     "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO",
173     "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL };
174
175
176 /*-- Local prototypes.  --*/
177 static void proc_texi_buffer (FILE *fp, const char *line, size_t len,
178                               int *table_level, int *eol_action);
179
180
181
182 /* Print diagnostic message and exit with failure. */
183 static void
184 die (const char *format, ...)
185 {
186   va_list arg_ptr;
187
188   fflush (stdout);
189   fprintf (stderr, "%s: ", PGM);
190
191   va_start (arg_ptr, format);
192   vfprintf (stderr, format, arg_ptr);
193   va_end (arg_ptr);
194   putc ('\n', stderr);
195
196   exit (1);
197 }
198
199
200 /* Print diagnostic message. */
201 static void
202 err (const char *format, ...)
203 {
204   va_list arg_ptr;
205
206   fflush (stdout);
207   if (strncmp (format, "%s:%d:", 6))
208     fprintf (stderr, "%s: ", PGM);
209
210   va_start (arg_ptr, format);
211   vfprintf (stderr, format, arg_ptr);
212   va_end (arg_ptr);
213   putc ('\n', stderr);
214   any_error = 1;
215 }
216
217 /* Print diagnostic message. */
218 static void
219 inf (const char *format, ...)
220 {
221   va_list arg_ptr;
222
223   fflush (stdout);
224   fprintf (stderr, "%s: ", PGM);
225
226   va_start (arg_ptr, format);
227   vfprintf (stderr, format, arg_ptr);
228   va_end (arg_ptr);
229   putc ('\n', stderr);
230 }
231
232
233 static void *
234 xmalloc (size_t n)
235 {
236   void *p = malloc (n);
237   if (!p)
238     die ("out of core: %s", strerror (errno));
239   return p;
240 }
241
242 static void *
243 xcalloc (size_t n, size_t m)
244 {
245   void *p = calloc (n, m);
246   if (!p)
247     die ("out of core: %s", strerror (errno));
248   return p;
249 }
250
251 static void *
252 xrealloc (void *old, size_t n)
253 {
254   void *p = realloc (old, n);
255   if (!p)
256     die ("out of core: %s", strerror (errno));
257   return p;
258 }
259
260 static char *
261 xstrdup (const char *string)
262 {
263   void *p = malloc (strlen (string)+1);
264   if (!p)
265     die ("out of core: %s", strerror (errno));
266   strcpy (p, string);
267   return p;
268 }
269
270
271 /* Uppercase the ascii characters in STRING.  */
272 static char *
273 ascii_strupr (char *string)
274 {
275   char *p;
276
277   for (p = string; *p; p++)
278     if (!(*p & 0x80))
279       *p = toupper (*p);
280   return string;
281 }
282
283
284 /* Return the current date as an ISO string.  */
285 const char *
286 isodatestring (void)
287 {
288   static char buffer[11+5];
289   struct tm *tp;
290   time_t atime = time (NULL);
291   
292   if (atime < 0)
293     strcpy (buffer, "????" "-??" "-??");
294   else
295     {
296       tp = gmtime (&atime);
297       sprintf (buffer,"%04d-%02d-%02d",
298                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
299     }
300   return buffer;
301 }
302
303
304
305 /* Return a section buffer for the section NAME.  Allocate a new buffer
306    if this is a new section.  Keep track of the sections in THEPAGE.
307    This function may reallocate the section array in THEPAGE.  */
308 static section_buffer_t
309 get_section_buffer (const char *name)
310 {
311   int i;
312   section_buffer_t sect; 
313
314   /* If there is no section we put everything into the required NAME
315      section.  Given that this is the first one listed it is likely
316      that error are easily visible.  */
317   if (!name)
318     name = "NAME";
319
320   for (i=0; i < thepage.n_sections; i++)
321     {
322       sect = thepage.sections + i;
323       if (sect->name && !strcmp (name, sect->name))
324         return sect;
325     }
326   for (i=0; i < thepage.n_sections; i++)
327     if (!thepage.sections[i].name)
328       break;
329   if (i < thepage.n_sections)
330     sect = thepage.sections + i;
331   else
332     {
333       /* We need to allocate or reallocate the section array.  */
334       size_t old_n = thepage.n_sections;
335       size_t new_n = 20;
336
337       if (!old_n)
338         thepage.sections = xcalloc (new_n, sizeof *thepage.sections);
339       else
340         {
341           thepage.sections = xrealloc (thepage.sections,
342                                        ((old_n + new_n)
343                                         * sizeof *thepage.sections));
344           memset (thepage.sections + old_n, 0,
345                   new_n * sizeof *thepage.sections);
346         }
347       thepage.n_sections += new_n;
348
349       /* Setup the tail pointers.  */
350       for (i=old_n; i < thepage.n_sections; i++)
351         {
352           sect = thepage.sections + i;
353           sect->lines_tail = &sect->lines;
354         }
355       sect = thepage.sections + old_n;
356     }
357
358   /* Store the name.  */
359   assert (!sect->name);
360   sect->name = xstrdup (name);
361   return sect;
362 }
363
364
365
366 /* Add the content of LINE to the section named SECTNAME.  */
367 static void
368 add_content (const char *sectname, char *line, int verbatim)
369 {
370   section_buffer_t sect;
371   line_buffer_t lb;
372
373   sect = get_section_buffer (sectname);
374   if (sect->last_line && !sect->last_line->verbatim == !verbatim)
375     {
376       /* Lets append that line to the last one.  We do this to keep
377          all lines of the same kind (i.e.verbatim or not) together in
378          one large buffer.  */
379       size_t n1, n;
380
381       lb = sect->last_line;
382       n1 = strlen (lb->line);
383       n = n1 + 1 + strlen (line) + 1;
384       lb->line = xrealloc (lb->line, n);
385       strcpy (lb->line+n1, "\n");
386       strcpy (lb->line+n1+1, line);
387     }
388   else
389     {
390       lb = xcalloc (1, sizeof *lb);
391       lb->verbatim = verbatim;
392       lb->line = xstrdup (line);
393       sect->last_line = lb;
394       *sect->lines_tail = lb;
395       sect->lines_tail = &lb->next;
396     }
397 }
398
399
400 /* Prepare for a new man page using the filename NAME. */
401 static void
402 start_page (char *name)
403 {
404   if (verbose)
405     inf ("starting page `%s'", name);
406   assert (!thepage.name);
407   thepage.name = xstrdup (name);
408   thepage.n_sections = 0;
409 }
410
411
412 /* Write the .TH entry of the current page.  Return -1 if there is a
413    problem with the page. */
414 static int
415 write_th (FILE *fp)
416 {
417   char *name, *p;
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, "\n'",  "'\n"  },
455     { "file",    0, "`\\fI","\\fR'" }, 
456     { "env",     0, "`\\fI","\\fR'" }, 
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
681         putc (*s, fp);
682     }
683
684   if (in_cmd > 1)
685     {
686       cmdbuf[cmdidx] = 0;
687       n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
688       assert (n <= len);
689       s += n; len -= n;
690       s--; len++;
691       in_cmd = 0;
692     }
693 }
694
695
696 /* Do something with the Texinfo line LINE.  */
697 static void
698 parse_texi_line (FILE *fp, const char *line, int *table_level)
699 {
700   int eol_action = 0;
701
702   /* A quick test whether there are any texinfo commands.  */
703   if (!strchr (line, '@'))
704     {
705       fputs (line, fp);
706       putc ('\n', fp);
707       return;
708     }
709   proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
710   putc ('\n', fp);
711 }
712
713
714 /* Write all the lines LINES to FP.  */
715 static void
716 write_content (FILE *fp, line_buffer_t lines)
717 {
718   line_buffer_t line;
719   int table_level = 0;
720
721   for (line = lines; line; line = line->next)
722     {
723       if (line->verbatim)
724         {
725           fputs (line->line, fp);
726           putc ('\n', fp);
727         }
728       else
729         {
730 /*           fputs ("TEXI---", fp); */
731 /*           fputs (line->line, fp); */
732 /*           fputs ("---\n", fp); */
733           parse_texi_line (fp, line->line, &table_level);
734         }
735     }  
736 }
737
738
739
740 static int
741 is_standard_section (const char *name)
742 {
743   int i;
744   const char *s;
745
746   for (i=0; (s=standard_sections[i]); i++)
747     if (!strcmp (s, name))
748       return 1;
749   return 0;
750 }
751
752
753 /* Finish a page; that is sort the data and write it out to the file.  */
754 static void
755 finish_page (void)
756 {
757   FILE *fp;
758   section_buffer_t sect = NULL;
759   int idx;
760   const char *s;
761   int i;
762
763   if (!thepage.name)
764     return; /* No page active.  */
765
766   if (verbose)
767     inf ("finishing page `%s'", thepage.name);
768
769   if (opt_select)
770     {
771       if (!strcmp (opt_select, thepage.name))
772         {
773           inf ("selected `%s'", thepage.name );
774           fp = stdout;
775         }
776       else
777         {
778           fp = fopen ( "/dev/null", "w" );
779           if (!fp)
780             die ("failed to open /dev/null: %s\n", strerror (errno));
781         }
782     }
783   else if (opt_store)
784     {
785       inf ("writing `%s'", thepage.name );
786       fp = fopen ( thepage.name, "w" );
787       if (!fp)
788         die ("failed to create `%s': %s\n", thepage.name, strerror (errno));
789     }
790   else
791     fp = stdout;
792
793   if (write_th (fp))
794     goto leave;
795
796   for (idx=0; (s=standard_sections[idx]); idx++)
797     {
798       for (i=0; i < thepage.n_sections; i++)
799         {
800           sect = thepage.sections + i;
801           if (sect->name && !strcmp (s, sect->name))
802             break;
803         }
804       if (i == thepage.n_sections)
805         sect = NULL;
806
807       if (sect)
808         {
809           fprintf (fp, ".SH %s\n", sect->name);
810           write_content (fp, sect->lines);
811           /* Now continue with all non standard sections directly
812              following this one. */
813           for (i++; i < thepage.n_sections; i++)
814             {
815               sect = thepage.sections + i;
816               if (sect->name && is_standard_section (sect->name))
817                 break;
818               if (sect->name)
819                 {
820                   fprintf (fp, ".SH %s\n", sect->name);
821                   write_content (fp, sect->lines);
822                 }
823             }
824           
825         }
826     }
827
828
829  leave:
830   if (fp != stdout)
831     fclose (fp);
832   free (thepage.name);
833   thepage.name = NULL;
834   /* FIXME: Cleanup the content.  */
835 }
836
837
838
839
840 /* Parse one Texinfo file and create manpages according to the
841    embedded instructions.  */
842 static void
843 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
844 {
845   char *line;
846   int lnr = 0;
847   /* Fixme: The follwing state variables don't carry over to include
848      files. */
849   int in_verbatim = 0;
850   int skip_to_end = 0;        /* Used to skip over menu entries. */
851   int skip_sect_line = 0;     /* Skip after @mansect.  */
852   int ifset_nesting = 0;      /* How often a ifset has been seen. */
853   int ifclear_nesting = 0;    /* How often a ifclear has been seen. */
854   int in_gpgone = 0;          /* Keep track of "@ifset gpgone" parts.  */
855   int not_in_gpgone = 0;      /* Keep track of "@ifclear gpgone" parts.  */
856   int not_in_man = 0;         /* Keep track of "@ifclear isman" parts.  */
857
858   /* Helper to define a macro. */
859   char *macroname = NULL;     
860   char *macrovalue = NULL; 
861   size_t macrovaluesize = 0;
862   size_t macrovalueused = 0;
863
864   line = xmalloc (LINESIZE);
865   while (fgets (line, LINESIZE, fp))
866     {
867       size_t n = strlen (line);
868       int got_line = 0;
869       char *p;
870
871       lnr++;
872       if (!n || line[n-1] != '\n')
873         {
874           err ("%s:%d: trailing linefeed missing, line too long or "
875                "embedded Nul character", fname, lnr);
876           break;
877         }
878       line[--n] = 0;
879
880       if (*line == '@')
881         {
882           for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
883             n++;
884           while (*p == ' ' || *p == '\t')
885             p++;
886         }
887       else
888         p = line;
889
890       /* Take action on macro.  */
891       if (macroname)
892         {
893           if (n == 4 && !memcmp (line, "@end", 4)
894               && (line[4]==' '||line[4]=='\t'||!line[4])
895               && !strncmp (p, "macro", 5)
896               && (p[5]==' '||p[5]=='\t'||!p[5]))
897             {
898               macro_t m;
899
900               if (macrovalueused)
901                 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
902               macrovalue[macrovalueused] = 0;     /* Terminate macro. */
903               macrovalue = xrealloc (macrovalue, macrovalueused+1);
904               
905               for (m= macrolist; m; m = m->next)
906                 if (!strcmp (m->name, macroname))
907                   break;
908               if (m)
909                 free (m->value);
910               else
911                 {
912                   m = xcalloc (1, sizeof *m + strlen (macroname));
913                   strcpy (m->name, macroname);
914                   m->next = macrolist;
915                   macrolist = m;
916                 }
917               m->value = macrovalue;
918               macrovalue = NULL;
919               free (macroname);
920               macroname = NULL;
921             }
922           else
923             {
924               if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
925                 {
926                   macrovaluesize += strlen (line) + 256;
927                   macrovalue = xrealloc (macrovalue,  macrovaluesize);
928                 }
929               strcpy (macrovalue+macrovalueused, line);
930               macrovalueused += strlen (line);
931               macrovalue[macrovalueused++] = '\n';
932             }
933           continue;
934         }
935
936
937       if (n >= 5 && !memcmp (line, "@node", 5)
938           && (line[5]==' '||line[5]=='\t'||!line[5]))
939         {
940           /* Completey ignore @node lines.  */
941           continue;
942         }
943
944
945       if (skip_sect_line)
946         {
947           skip_sect_line = 0;
948           if (!strncmp (line, "@section", 8)
949               || !strncmp (line, "@subsection", 11)
950               || !strncmp (line, "@chapheading", 12))
951             continue;
952         }
953
954       /* We only parse lines we need and ignore the rest.  There are a
955          few macros used to control this as well as one @ifset
956          command.  Parts we know about are saved away into containers
957          separate for each section. */
958
959       /* First process ifset/ifclear commands. */
960       if (*line == '@')
961         {
962           if (n == 6 && !memcmp (line, "@ifset", 6)
963                    && (line[6]==' '||line[6]=='\t'))
964             {
965               ifset_nesting++;
966
967               if (!strncmp (p, "manverb", 7) && (p[7]==' '||p[7]=='\t'||!p[7]))
968                 {
969                   if (in_verbatim)
970                     err ("%s:%d: nested \"@ifset manverb\"", fname, lnr);
971                   else
972                     in_verbatim = ifset_nesting;
973                 }
974               else if (!strncmp (p, "gpgone", 6)
975                        && (p[6]==' '||p[6]=='\t'||!p[6]))
976                 {
977                   if (in_gpgone)
978                     err ("%s:%d: nested \"@ifset gpgone\"", fname, lnr);
979                   else
980                     in_gpgone = ifset_nesting;
981                 }
982               continue;
983             }
984           else if (n == 4 && !memcmp (line, "@end", 4)
985                    && (line[4]==' '||line[4]=='\t')
986                    && !strncmp (p, "ifset", 5)
987                    && (p[5]==' '||p[5]=='\t'||!p[5]))
988             {
989               if (in_verbatim && ifset_nesting == in_verbatim)
990                 in_verbatim = 0;
991               if (in_gpgone && ifset_nesting == in_gpgone)
992                 in_gpgone = 0;
993
994               if (ifset_nesting)
995                 ifset_nesting--;
996               else
997                 err ("%s:%d: unbalanced \"@end ifset\"", fname, lnr);
998               continue;
999             }
1000           else if (n == 8 && !memcmp (line, "@ifclear", 8)
1001                    && (line[8]==' '||line[8]=='\t'))
1002             {
1003               ifclear_nesting++;
1004
1005               if (!strncmp (p, "gpgone", 6)
1006                   && (p[6]==' '||p[6]=='\t'||!p[6]))
1007                 {
1008                   if (not_in_gpgone)
1009                     err ("%s:%d: nested \"@ifclear gpgone\"", fname, lnr);
1010                   else
1011                     not_in_gpgone = ifclear_nesting;
1012                 }
1013
1014               else if (!strncmp (p, "isman", 5)
1015                        && (p[5]==' '||p[5]=='\t'||!p[5]))
1016                 {
1017                   if (not_in_man)
1018                     err ("%s:%d: nested \"@ifclear isman\"", fname, lnr);
1019                   else
1020                     not_in_man = ifclear_nesting;
1021                 }
1022
1023               continue;
1024             }
1025           else if (n == 4 && !memcmp (line, "@end", 4)
1026                    && (line[4]==' '||line[4]=='\t')
1027                    && !strncmp (p, "ifclear", 7)
1028                    && (p[7]==' '||p[7]=='\t'||!p[7]))
1029             {
1030               if (not_in_gpgone && ifclear_nesting == not_in_gpgone)
1031                 not_in_gpgone = 0;
1032               if (not_in_man && ifclear_nesting == not_in_man)
1033                 not_in_man = 0;
1034
1035               if (ifclear_nesting)
1036                 ifclear_nesting--;
1037               else
1038                 err ("%s:%d: unbalanced \"@end ifclear\"", fname, lnr);
1039               continue;
1040             }
1041         }
1042
1043       /* Take action on ifset/ifclear.  */
1044       if ( (in_gpgone && !gpgone_defined)
1045            || (not_in_gpgone && gpgone_defined)
1046            || not_in_man)
1047         continue;
1048
1049       /* Process commands. */
1050       if (*line == '@')
1051         {
1052           if (skip_to_end
1053               && n == 4 && !memcmp (line, "@end", 4)
1054               && (line[4]==' '||line[4]=='\t'||!line[4]))
1055             {
1056               skip_to_end = 0;
1057             }
1058           else if (in_verbatim)
1059             {
1060                 got_line = 1;
1061             }
1062           else if (n == 6 && !memcmp (line, "@macro", 6))
1063             {
1064               macroname = xstrdup (p);
1065               macrovalue = xmalloc ((macrovaluesize = 1024));
1066               macrovalueused = 0;
1067             }
1068           else if (n == 8 && !memcmp (line, "@manpage", 8))
1069             {
1070               free (*section_name);
1071               *section_name = NULL;
1072               finish_page ();
1073               start_page (p);
1074               in_pause = 0;
1075             }
1076           else if (n == 8 && !memcmp (line, "@mansect", 8))
1077             {
1078               if (!thepage.name)
1079                 err ("%s:%d: section outside of a man page", fname, lnr);
1080               else
1081                 {
1082                   free (*section_name);
1083                   *section_name = ascii_strupr (xstrdup (p));
1084                   in_pause = 0;
1085                   skip_sect_line = 1;
1086                 }
1087             }
1088           else if (n == 9 && !memcmp (line, "@manpause", 9))
1089             {
1090               if (!*section_name)
1091                 err ("%s:%d: pausing outside of a man section", fname, lnr);
1092               else if (in_pause)
1093                 err ("%s:%d: already pausing", fname, lnr);
1094               else
1095                 in_pause = 1;
1096             }
1097           else if (n == 8 && !memcmp (line, "@mancont", 8))
1098             {
1099               if (!*section_name)
1100                 err ("%s:%d: continue outside of a man section", fname, lnr);
1101               else if (!in_pause)
1102                 err ("%s:%d: continue while not pausing", fname, lnr);
1103               else
1104                 in_pause = 0;
1105             }
1106           else if (n == 5 && !memcmp (line, "@menu", 5)
1107                    && (line[5]==' '||line[5]=='\t'||!line[5]))
1108             {
1109               skip_to_end = 1;
1110             }
1111           else if (n == 8 && !memcmp (line, "@include", 8)
1112                    && (line[8]==' '||line[8]=='\t'||!line[8]))
1113             {
1114               char *incname = xstrdup (p);
1115               FILE *incfp = fopen (incname, "r");
1116
1117               if (!incfp && opt_include && *opt_include && *p != '/')
1118                 {
1119                   free (incname);
1120                   incname = xmalloc (strlen (opt_include) + 1
1121                                      + strlen (p) + 1);
1122                   strcpy (incname, opt_include);
1123                   if ( incname[strlen (incname)-1] != '/' )
1124                     strcat (incname, "/");
1125                   strcat (incname, p);
1126                   incfp = fopen (incname, "r");
1127                 }
1128
1129               if (!incfp)
1130                 err ("can't open include file `%s':%s",
1131                      incname, strerror (errno));
1132               else
1133                 {
1134                   parse_file (incname, incfp, section_name, in_pause);
1135                   fclose (incfp);
1136                 }
1137               free (incname);
1138             }
1139           else if (n == 4 && !memcmp (line, "@bye", 4)
1140                    && (line[4]==' '||line[4]=='\t'||!line[4]))
1141             {
1142               break;
1143             }
1144           else if (!skip_to_end)
1145             got_line = 1;
1146         }
1147       else if (!skip_to_end)
1148         got_line = 1;
1149
1150       if (got_line && in_verbatim)
1151         add_content (*section_name, line, 1);
1152       else if (got_line && thepage.name && *section_name && !in_pause)
1153         add_content (*section_name, line, 0);
1154
1155     }
1156   if (ferror (fp))
1157     err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1158   free (macroname);
1159   free (macrovalue);
1160   free (line);
1161 }
1162
1163
1164 static void
1165 top_parse_file (const char *fname, FILE *fp)
1166 {
1167   char *section_name = NULL;  /* Name of the current section or NULL
1168                                  if not in a section.  */
1169   while (macrolist)
1170     {
1171       macro_t m = macrolist->next;
1172       free (m->value);
1173       free (m);
1174       macrolist = m;
1175     }
1176
1177   parse_file (fname, fp, &section_name, 0);
1178   free (section_name);
1179   finish_page ();
1180 }
1181
1182
1183 int 
1184 main (int argc, char **argv)
1185 {
1186   int last_argc = -1;
1187
1188   opt_source = "GNU";
1189   opt_release = "";
1190
1191   if (argc)
1192     {
1193       argc--; argv++;
1194     }
1195   while (argc && last_argc != argc )
1196     {
1197       last_argc = argc;
1198       if (!strcmp (*argv, "--"))
1199         {
1200           argc--; argv++;
1201           break;
1202         }
1203       else if (!strcmp (*argv, "--help"))
1204         {
1205           puts (
1206                 "Usage: " PGM " [OPTION] [FILE]\n"
1207                 "Extract man pages from a Texinfo source.\n\n"
1208                 "  --source NAME    use NAME as source field\n"
1209                 "  --release STRING use STRING as the release field\n"
1210                 "  --store          write output using @manpage name\n"
1211                 "  --select NAME    only output pages with @manpage NAME\n"
1212                 "  --verbose        enable extra informational output\n"
1213                 "  --debug          enable additional debug output\n"
1214                 "  --help           display this help and exit\n"
1215                 "  -I DIR           also search in include DIR\n"
1216                 "  -D gpgone        the only useable define\n\n"
1217                 "With no FILE, or when FILE is -, read standard input.\n\n"
1218                 "Report bugs to <bugs@g10code.com>.");
1219           exit (0);
1220         }
1221       else if (!strcmp (*argv, "--version"))
1222         {
1223           puts (PGM " " VERSION "\n"
1224                "Copyright (C) 2005 g10 Code GmbH\n"
1225                "This program comes with ABSOLUTELY NO WARRANTY.\n"
1226                "This is free software, and you are welcome to redistribute it\n"
1227                 "under certain conditions. See the file COPYING for details.");
1228           exit (0);
1229         }
1230       else if (!strcmp (*argv, "--verbose"))
1231         {
1232           verbose = 1;
1233           argc--; argv++;
1234         }
1235       else if (!strcmp (*argv, "--quiet"))
1236         {
1237           quiet = 1;
1238           argc--; argv++;
1239         }
1240       else if (!strcmp (*argv, "--debug"))
1241         {
1242           verbose = debug = 1;
1243           argc--; argv++;
1244         }
1245       else if (!strcmp (*argv, "--source"))
1246         {
1247           argc--; argv++;
1248           if (argc)
1249             {
1250               opt_source = *argv;
1251               argc--; argv++;
1252             }
1253         }
1254       else if (!strcmp (*argv, "--release"))
1255         {
1256           argc--; argv++;
1257           if (argc)
1258             {
1259               opt_release = *argv;
1260               argc--; argv++;
1261             }
1262         }
1263       else if (!strcmp (*argv, "--store"))
1264         {
1265           opt_store = 1;
1266           argc--; argv++;
1267         }
1268       else if (!strcmp (*argv, "--select"))
1269         {
1270           argc--; argv++;
1271           if (argc)
1272             {
1273               opt_select = strrchr (*argv, '/');
1274               if (opt_select)
1275                 opt_select++;
1276               else 
1277                 opt_select = *argv;
1278               argc--; argv++;
1279             }
1280         }
1281       else if (!strcmp (*argv, "-I"))
1282         {
1283           argc--; argv++;
1284           if (argc)
1285             {
1286               opt_include = *argv;
1287               argc--; argv++;
1288             }
1289         }
1290       else if (!strcmp (*argv, "-D"))
1291         {
1292           argc--; argv++;
1293           if (argc)
1294             {
1295               if (!strcmp (*argv, "gpgone"))
1296                 gpgone_defined = 1;
1297               argc--; argv++;
1298             }
1299         }
1300     }          
1301  
1302   if (argc > 1)
1303     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1304
1305   /* Start processing. */
1306   if (argc && strcmp (*argv, "-"))
1307     {
1308       FILE *fp = fopen (*argv, "rb");
1309       if (!fp)
1310         die ("%s:0: can't open file: %s", *argv, strerror (errno));
1311       top_parse_file (*argv, fp);
1312       fclose (fp);
1313     }
1314   else
1315     top_parse_file ("-", stdin);
1316
1317   return !!any_error;
1318 }
1319
1320
1321 /*
1322 Local Variables:
1323 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1324 End:
1325 */