Add a warning note to --allow-multiple-messages.
[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 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 "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, "\\(aq", "\\(aq"  },
453     { "file",    0, "\\(oq\\fI","\\fR\\(cq" }, 
454     { "env",     0, "\\(oq\\fI","\\fR\\(cq" }, 
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 if (*s == '\\')
679         fputs ("\\\\", fp);
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 following 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 */