Various updates
[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     { "noindent", 0 },
460     { "section", 1 },
461     { "chapter", 1 },
462     { "subsection", 6, "\n.SS " },
463     { "chapheading", 0},
464     { "item",    2, ".TP\n.B " },
465     { "itemx",   2, ".TP\n.B " },
466     { "table",   3 }, 
467     { "itemize",   3 }, 
468     { "bullet",  0, "* " },
469     { "end",     4 },
470     { "quotation",1, ".RS\n\\fB" },
471     { "ifset",   1 },
472     { "ifclear",   1 },
473     { NULL }
474   };
475   size_t n;
476   int i;
477   const char *s;
478   const char *lead_out = NULL;
479   int ignore_args = 0;
480
481   for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
482     ;
483   if (cmdtbl[i].name)
484     {
485       s = cmdtbl[i].lead_in;
486       if (s)
487         fputs (s, fp);
488       lead_out = cmdtbl[i].lead_out;
489       switch (cmdtbl[i].what)
490         {
491         case 1: /* Throw away the entire line.  */
492           s = memchr (rest, '\n', len);
493           return s? (s-rest)+1 : len;  
494         case 2: /* Handle @item.  */
495           break;
496         case 3: /* Handle table.  */
497           if (++(*table_level) > 1)
498             fputs (".RS\n", fp);
499           /* Now throw away the entire line. */
500           s = memchr (rest, '\n', len);
501           return s? (s-rest)+1 : len;  
502           break;
503         case 4: /* Handle end.  */
504           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
505             ;
506           if (n >= 5 && !memcmp (s, "table", 5)
507               && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
508             {
509               if ((*table_level)-- > 1)
510                 fputs (".RE\n", fp);
511             }
512           else if (n >= 7 && !memcmp (s, "example", 7)
513               && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
514             {
515               fputs (".fi\n.RE\n", fp);
516             }
517           else if (n >= 12 && !memcmp (s, "smallexample", 12)
518               && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
519             {
520               fputs (".fi\n.RE\n", fp);
521             }
522           else if (n >= 9 && !memcmp (s, "quotation", 9)
523               && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
524             {
525               fputs ("\\fR\n.RE\n", fp);
526             }
527           /* Now throw away the entire line. */
528           s = memchr (rest, '\n', len);
529           return s? (s-rest)+1 : len;  
530         case 5: /* Handle special comments. */
531           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
532             ;
533           if (n >= 4 && !memcmp (s, "man:", 4))
534             {
535               for (s+=4, n-=4; n && *s != '\n'; n--, s++)
536                 putc (*s, fp);
537               putc ('\n', fp);
538             }
539           /* Now throw away the entire line. */
540           s = memchr (rest, '\n', len);
541           return s? (s-rest)+1 : len;  
542         case 6:
543           *eol_action = 1;
544           break;
545         case 7:
546           ignore_args = 1;
547           break;
548         default:
549           break;
550         }
551     }
552   else
553     {
554       inf ("texinfo command `%s' not supported (%.*s)", command,
555            ((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
556     }
557
558   if (*rest == '{')
559     {
560       /* Find matching closing brace.  */
561       for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
562         if (*s == '{')
563           i++;
564         else if (*s == '}')
565           i--;
566       if (i)
567         {
568           err ("closing brace for command `%s' not found", command);
569           return len;
570         }
571       if (n > 2 && !ignore_args)
572         proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
573     }
574   else
575     n = 0;
576
577   if (lead_out)
578     fputs (lead_out, fp);
579
580   return n;
581 }
582
583
584
585 /* Process the string LINE with LEN bytes of Texinfo content. */
586 static void
587 proc_texi_buffer (FILE *fp, const char *line, size_t len,
588                   int *table_level, int *eol_action)
589 {
590   const char *s;
591   char cmdbuf[256];
592   int cmdidx = 0;
593   int in_cmd = 0;
594   size_t n;
595
596   for (s=line; *s && len; s++, len--)
597     {
598       if (in_cmd)
599         {
600           if (in_cmd == 1)
601             {
602               switch (*s)
603                 {
604                 case '@': case '{': case '}': 
605                   putc (*s, fp); in_cmd = 0; 
606                   break;
607                 case ':': /* Not ending a sentence flag.  */
608                   in_cmd = 0;
609                   break;
610                 case '.': case '!': case '?': /* Ending a sentence. */
611                   putc (*s, fp); in_cmd = 0; 
612                   break;
613                 case ' ': case '\t': case '\n': /* Non collapsing spaces.  */
614                   putc (*s, fp); in_cmd = 0; 
615                   break;
616                 default:
617                   cmdidx = 0;
618                   cmdbuf[cmdidx++] = *s;
619                   in_cmd++;
620                   break;
621                 }
622             }
623           else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
624             {
625               cmdbuf[cmdidx] = 0;
626               n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
627               assert (n <= len);
628               s += n; len -= n;
629               s--; len++;
630               in_cmd = 0;
631             }
632           else if (cmdidx < sizeof cmdbuf -1)  
633             cmdbuf[cmdidx++] = *s;
634           else
635             {
636               err ("texinfo command too long - ignored");
637               in_cmd = 0;
638             }
639         }
640       else if (*s == '@')
641         in_cmd = 1;
642       else if (*s == '\n')
643         {
644           switch (*eol_action)
645             {
646             case 1: /* Create a dummy paragraph. */
647               fputs ("\n\\ \n", fp);
648               break;
649             default:
650               putc (*s, fp);
651             }
652           *eol_action = 0;
653         }
654       else
655         putc (*s, fp);
656     }
657 }
658
659
660 /* Do something with the Texinfo line LINE.  */
661 static void
662 parse_texi_line (FILE *fp, const char *line, int *table_level)
663 {
664   int eol_action = 0;
665
666   /* A quick test whether there are any texinfo commands.  */
667   if (!strchr (line, '@'))
668     {
669       fputs (line, fp);
670       putc ('\n', fp);
671       return;
672     }
673   proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
674   putc ('\n', fp);
675 }
676
677
678 /* Write all the lines LINES to FP.  */
679 static void
680 write_content (FILE *fp, line_buffer_t lines)
681 {
682   line_buffer_t line;
683   int table_level = 0;
684
685   for (line = lines; line; line = line->next)
686     {
687       if (line->verbatim)
688         {
689           fputs (line->line, fp);
690           putc ('\n', fp);
691         }
692       else
693         {
694 /*           fputs ("TEXI---", fp); */
695 /*           fputs (line->line, fp); */
696 /*           fputs ("---\n", fp); */
697           parse_texi_line (fp, line->line, &table_level);
698         }
699     }  
700 }
701
702
703
704 static int
705 is_standard_section (const char *name)
706 {
707   int i;
708   const char *s;
709
710   for (i=0; (s=standard_sections[i]); i++)
711     if (!strcmp (s, name))
712       return 1;
713   return 0;
714 }
715
716
717 /* Finish a page; that is sort the data and write it out to the file.  */
718 static void
719 finish_page (void)
720 {
721   FILE *fp;
722   section_buffer_t sect;
723   int idx;
724   const char *s;
725   int i;
726
727   if (!thepage.name)
728     return; /* No page active.  */
729
730   if (verbose)
731     inf ("finishing page `%s'", thepage.name);
732
733   if (opt_select)
734     {
735       if (!strcmp (opt_select, thepage.name))
736         {
737           inf ("selected `%s'", thepage.name );
738           fp = stdout;
739         }
740       else
741         {
742           fp = fopen ( "/dev/null", "w" );
743           if (!fp)
744             die ("failed to open /dev/null: %s\n", strerror (errno));
745         }
746     }
747   else if (opt_store)
748     {
749       inf ("writing `%s'", thepage.name );
750       fp = fopen ( thepage.name, "w" );
751       if (!fp)
752         die ("failed to create `%s': %s\n", thepage.name, strerror (errno));
753     }
754   else
755     fp = stdout;
756
757   if (write_th (fp))
758     goto leave;
759
760   for (idx=0; (s=standard_sections[idx]); idx++)
761     {
762       for (i=0; i < thepage.n_sections; i++)
763         {
764           sect = thepage.sections + i;
765           if (sect->name && !strcmp (s, sect->name))
766             break;
767         }
768       if (i == thepage.n_sections)
769         sect = NULL;
770
771       if (sect)
772         {
773           fprintf (fp, ".SH %s\n", sect->name);
774           write_content (fp, sect->lines);
775           /* Now continue with all non standard sections directly
776              following this one. */
777           for (i++; i < thepage.n_sections; i++)
778             {
779               sect = thepage.sections + i;
780               if (sect->name && is_standard_section (sect->name))
781                 break;
782               if (sect->name)
783                 {
784                   fprintf (fp, ".SH %s\n", sect->name);
785                   write_content (fp, sect->lines);
786                 }
787             }
788           
789         }
790     }
791
792
793  leave:
794   if (fp != stdout)
795     fclose (fp);
796   free (thepage.name);
797   thepage.name = NULL;
798   /* FIXME: Cleanup the content.  */
799 }
800
801
802
803
804 /* Parse one Texinfo file and create manpages according to the
805    embedded instructions.  */
806 static void
807 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
808 {
809   char *line;
810   int lnr = 0;
811   int in_verbatim = 0;
812   int skip_to_end = 0;        /* Used to skip over menu entries. */
813   int skip_sect_line = 0;     /* Skip after @mansect.  */
814
815   line = xmalloc (LINESIZE);
816   while (fgets (line, LINESIZE, fp))
817     {
818       size_t n = strlen (line);
819       int got_line = 0;
820       char *p;
821
822       lnr++;
823       if (!n || line[n-1] != '\n')
824         {
825           err ("%s:%d: trailing linefeed missing, line too long or "
826                "embedded Nul character", fname, lnr);
827           break;
828         }
829       line[--n] = 0;
830
831       if (n >= 5 && !memcmp (line, "@node", 5)
832           && (line[5]==' '||line[5]=='\t'||!line[5]))
833         {
834           /* Completey ignore @node lines.  */
835           continue;
836         }
837
838
839       if (skip_sect_line)
840         {
841           skip_sect_line = 0;
842           if (!strncmp (line, "@section", 8)
843               || !strncmp (line, "@subsection", 11)
844               || !strncmp (line, "@chapheading", 12))
845             continue;
846         }
847
848       /* We only parse lines we need and ignore the rest.  There are a
849          few macros used to control this as well as one @ifset
850          command.  Parts we know about are saved away into containers
851          separate for each section. */
852       if (*line == '@')
853         {
854           for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
855             n++;
856           while (*p == ' ' || *p == '\t')
857             p++;
858
859           if (skip_to_end
860               && n == 4 && !memcmp (line, "@end", 4)
861               && (line[4]==' '||line[4]=='\t'||!line[4]))
862             {
863               skip_to_end = 0;
864             }
865           else if (n == 6 && !memcmp (line, "@ifset", 6)
866               && !strncmp (p, "manverb", 7) && (p[7]==' '||p[7]=='\t'||!p[7]))
867             {
868               if (in_verbatim)
869                 err ("%s:%d: nested \"@ifset manverb\"", fname, lnr);
870               else
871                 in_verbatim = 1;
872             }
873           else if (in_verbatim && n == 4 && !memcmp (line, "@end", 4)
874                    && !strncmp (p, "ifset", 5)
875                    && (p[5]==' '||p[5]=='\t'||!p[5]))
876             {
877               in_verbatim = 0;
878             }
879           else if (in_verbatim)
880             {
881               got_line = 1;
882             }
883           else if (n == 8 && !memcmp (line, "@manpage", 8))
884             {
885               free (*section_name);
886               *section_name = NULL;
887               finish_page ();
888               start_page (p);
889               in_pause = 0;
890             }
891           else if (n == 8 && !memcmp (line, "@mansect", 8))
892             {
893               if (!thepage.name)
894                 err ("%s:%d: section outside of a man page", fname, lnr);
895               else
896                 {
897                   free (*section_name);
898                   *section_name = ascii_strupr (xstrdup (p));
899                   in_pause = 0;
900                   skip_sect_line = 1;
901                 }
902             }
903           else if (n == 9 && !memcmp (line, "@manpause", 9))
904             {
905               if (!*section_name)
906                 err ("%s:%d: pausing outside of a man section", fname, lnr);
907               else if (in_pause)
908                 err ("%s:%d: already pausing", fname, lnr);
909               else
910                 in_pause = 1;
911             }
912           else if (n == 8 && !memcmp (line, "@mancont", 8))
913             {
914               if (!*section_name)
915                 err ("%s:%d: continue outside of a man section", fname, lnr);
916               else if (!in_pause)
917                 err ("%s:%d: continue while not pausing", fname, lnr);
918               else
919                 in_pause = 0;
920             }
921           else if (n == 5 && !memcmp (line, "@menu", 5)
922                    && (line[5]==' '||line[5]=='\t'||!line[5]))
923             {
924               skip_to_end = 1;
925             }
926           else if (n == 8 && !memcmp (line, "@ifclear", 8)
927               && !strncmp (p, "isman", 5) && (p[5]==' '||p[5]=='\t'||!p[5]))
928             {
929               skip_to_end = 1;
930             }
931           else if (n == 8 && !memcmp (line, "@include", 8)
932                    && (line[8]==' '||line[8]=='\t'||!line[8]))
933             {
934               char *incname = xstrdup (p);
935               FILE *incfp = fopen (incname, "r");
936
937               if (!incfp && opt_include && *opt_include && *p != '/')
938                 {
939                   free (incname);
940                   incname = xmalloc (strlen (opt_include) + 1
941                                      + strlen (p) + 1);
942                   strcpy (incname, opt_include);
943                   if ( incname[strlen (incname)-1] != '/' )
944                     strcat (incname, "/");
945                   strcat (incname, p);
946                   incfp = fopen (incname, "r");
947                 }
948
949               if (!incfp)
950                 err ("can't open include file `%s':%s",
951                      incname, strerror (errno));
952               else
953                 {
954                   parse_file (incname, incfp, section_name, in_pause);
955                   fclose (incfp);
956                 }
957               free (incname);
958             }
959           else if (!skip_to_end)
960             got_line = 1;
961         }
962       else if (!skip_to_end)
963         got_line = 1;
964
965       if (got_line && in_verbatim)
966         add_content (*section_name, line, 1);
967       else if (got_line && thepage.name && *section_name && !in_pause)
968         add_content (*section_name, line, 0);
969
970     }
971   if (ferror (fp))
972     err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
973   free (line);
974 }
975
976
977 static void
978 top_parse_file (const char *fname, FILE *fp)
979 {
980   char *section_name = NULL;  /* Name of the current section or NULL
981                                  if not in a section.  */
982   parse_file (fname, fp, &section_name, 0);
983   free (section_name);
984   finish_page ();
985 }
986
987
988 int 
989 main (int argc, char **argv)
990 {
991   int last_argc = -1;
992
993   opt_source = "GNU";
994   opt_release = "";
995
996   if (argc)
997     {
998       argc--; argv++;
999     }
1000   while (argc && last_argc != argc )
1001     {
1002       last_argc = argc;
1003       if (!strcmp (*argv, "--"))
1004         {
1005           argc--; argv++;
1006           break;
1007         }
1008       else if (!strcmp (*argv, "--help"))
1009         {
1010           puts (
1011                 "Usage: " PGM " [OPTION] [FILE]\n"
1012                 "Extract man pages from a Texinfo source.\n\n"
1013                 "  --source NAME    use NAME as source field\n"
1014                 "  --release STRING use STRING as the release field\n"
1015                 "  --store          write output using @manpage name\n"
1016                 "  --select NAME    only output pages with @manpage NAME\n"
1017                 "  --verbose        enable extra informational output\n"
1018                 "  --debug          enable additional debug output\n"
1019                 "  --help           display this help and exit\n"
1020                 "  -I DIR           also search in include DIR\n\n"
1021                 "With no FILE, or when FILE is -, read standard input.\n\n"
1022                 "Report bugs to <bugs@g10code.com>.");
1023           exit (0);
1024         }
1025       else if (!strcmp (*argv, "--version"))
1026         {
1027           puts (PGM " " VERSION "\n"
1028                "Copyright (C) 2005 g10 Code GmbH\n"
1029                "This program comes with ABSOLUTELY NO WARRANTY.\n"
1030                "This is free software, and you are welcome to redistribute it\n"
1031                 "under certain conditions. See the file COPYING for details.");
1032           exit (0);
1033         }
1034       else if (!strcmp (*argv, "--verbose"))
1035         {
1036           verbose = 1;
1037           argc--; argv++;
1038         }
1039       else if (!strcmp (*argv, "--quiet"))
1040         {
1041           quiet = 1;
1042           argc--; argv++;
1043         }
1044       else if (!strcmp (*argv, "--debug"))
1045         {
1046           verbose = debug = 1;
1047           argc--; argv++;
1048         }
1049       else if (!strcmp (*argv, "--source"))
1050         {
1051           argc--; argv++;
1052           if (argc)
1053             {
1054               opt_source = *argv;
1055               argc--; argv++;
1056             }
1057         }
1058       else if (!strcmp (*argv, "--release"))
1059         {
1060           argc--; argv++;
1061           if (argc)
1062             {
1063               opt_release = *argv;
1064               argc--; argv++;
1065             }
1066         }
1067       else if (!strcmp (*argv, "--store"))
1068         {
1069           opt_store = 1;
1070           argc--; argv++;
1071         }
1072       else if (!strcmp (*argv, "--select"))
1073         {
1074           argc--; argv++;
1075           if (argc)
1076             {
1077               opt_select = strrchr (*argv, '/');
1078               if (opt_select)
1079                 opt_select++;
1080               else 
1081                 opt_select = *argv;
1082               argc--; argv++;
1083             }
1084         }
1085       else if (!strcmp (*argv, "-I"))
1086         {
1087           argc--; argv++;
1088           if (argc)
1089             {
1090               opt_include = *argv;
1091               argc--; argv++;
1092             }
1093         }
1094     }          
1095  
1096   if (argc > 1)
1097     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1098
1099   /* Start processing. */
1100   if (argc && strcmp (*argv, "-"))
1101     {
1102       FILE *fp = fopen (*argv, "rb");
1103       if (!fp)
1104         die ("%s:0: can't open file: %s", *argv, strerror (errno));
1105       top_parse_file (*argv, fp);
1106       fclose (fp);
1107     }
1108   else
1109     top_parse_file ("-", stdin);
1110
1111   return !!any_error;
1112 }
1113
1114
1115 /*
1116 Local Variables:
1117 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1118 End:
1119 */