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