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