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