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