Update docs from master.
[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, 2008, 2011 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 3 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, see <http://www.gnu.org/licenses/>.
17  */
18
19 /*
20     This is a simple textinfo to man page converter.  It needs some
21     special markup in th e texinfo and tries best to get a create man
22     page.  It has been designed for the GnuPG man pages and thus only
23     a few texinfo commands are supported.
24
25     To use this you need to add the following macros into your texinfo
26     source:
27
28       @macro manpage {a}
29       @end macro
30       @macro mansect {a}
31       @end macro
32       @macro manpause
33       @end macro
34       @macro mancont
35       @end macro
36
37     They are used by yat2m to select parts of the Texinfo which should
38     go into the man page. These macros need to be used without leading
39     left space. Processing starts after a "manpage" macro has been
40     seen.  "mansect" identifies the section and yat2m make sure to
41     emit the sections in the proper order.  Note that @mansect skips
42     the next input line if that line begins with @section, @subsection or
43     @chapheading.
44
45     To insert verbatim troff markup, the following texinfo code may be
46     used:
47
48       @ifset manverb
49       .B whateever you want
50       @end ifset
51
52     alternativly a special comment may be used:
53
54       @c man:.B whatever you want
55
56     This is useful in case you need just one line. If you want to
57     include parts only in the man page but keep the texinfo
58     translation you may use:
59
60       @ifset isman
61       stuff to be rendered only on man pages
62       @end ifset
63
64     or to exclude stuff from man pages:
65
66       @ifclear isman
67       stuff not to be rendered on man pages
68       @end ifclear
69
70     the keyword @section is ignored, however @subsection gets rendered
71     as ".SS".  @menu is completely skipped. Several man pages may be
72     extracted from one file, either using the --store or the --select
73     option.
74
75     If you want to indent tables in the source use this style:
76
77       @table foo
78         @item
79         @item
80         @table
81           @item
82         @end
83       @end
84
85     Don't change the indentation within a table and keep the same
86     number of white space at the start of the line.  yat2m simply
87     detects the number of white spaces in front of an @item and remove
88     this number of spaces from all following lines until a new @item
89     is found or there are less spaces than for the last @item.
90 */
91
92 #include <stdio.h>
93 #include <stdlib.h>
94 #include <stddef.h>
95 #include <string.h>
96 #include <errno.h>
97 #include <stdarg.h>
98 #include <assert.h>
99 #include <ctype.h>
100 #include <time.h>
101
102
103 #define PGM "yat2m"
104 #define VERSION "1.0"
105
106 /* The maximum length of a line including the linefeed and one extra
107    character. */
108 #define LINESIZE 1024
109
110 /* Option flags. */
111 static int verbose;
112 static int quiet;
113 static int debug;
114 static const char *opt_source;
115 static const char *opt_release;
116 static const char *opt_select;
117 static const char *opt_include;
118 static int opt_store;
119
120 /* The only define we understand is -D gpgone.  Thus we need a simple
121    boolean tro track it. */
122 static int gpgone_defined;
123
124 /* Flag to keep track whether any error occurred.  */
125 static int any_error;
126
127
128 /* Object to keep macro definitions.  */
129 struct macro_s
130 {
131   struct macro_s *next;
132   char *value;  /* Malloced value. */
133   char name[1];
134 };
135 typedef struct macro_s *macro_t;
136
137 /* List of all defined macros. */
138 static macro_t macrolist;
139
140
141 /* Object to store one line of content.  */
142 struct line_buffer_s
143 {
144   struct line_buffer_s *next;
145   int verbatim;  /* True if LINE contains verbatim data.  The default
146                     is Texinfo source.  */
147   char *line;
148 };
149 typedef struct line_buffer_s *line_buffer_t;
150
151
152 /* Object to collect the data of a section.  */
153 struct section_buffer_s
154 {
155   char *name;           /* Malloced name of the section. This may be
156                            NULL to indicate this slot is not used.  */
157   line_buffer_t lines;  /* Linked list with the lines of the section.  */
158   line_buffer_t *lines_tail; /* Helper for faster appending to the
159                                 linked list.  */
160   line_buffer_t last_line;   /* Points to the last line appended.  */
161 };
162 typedef struct section_buffer_s *section_buffer_t;
163
164 /* Variable to keep info about the current page together.  */
165 static struct
166 {
167   /* Filename of the current page or NULL if no page is active.  Malloced. */
168   char *name;
169
170   /* Number of allocated elements in SECTIONS below.  */
171   size_t n_sections;
172   /* Array with the data of the sections.  */
173   section_buffer_t sections;
174
175 } thepage;
176
177
178 /* The list of standard section names.  COMMANDS and ASSUAN are GnuPG
179    specific. */
180 static const char * const standard_sections[] =
181   { "NAME",  "SYNOPSIS",  "DESCRIPTION",
182     "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS",
183     "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES",
184     "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO",
185     "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL };
186
187
188 /*-- Local prototypes.  --*/
189 static void proc_texi_buffer (FILE *fp, const char *line, size_t len,
190                               int *table_level, int *eol_action);
191
192
193
194 /* Print diagnostic message and exit with failure. */
195 static void
196 die (const char *format, ...)
197 {
198   va_list arg_ptr;
199
200   fflush (stdout);
201   fprintf (stderr, "%s: ", PGM);
202
203   va_start (arg_ptr, format);
204   vfprintf (stderr, format, arg_ptr);
205   va_end (arg_ptr);
206   putc ('\n', stderr);
207
208   exit (1);
209 }
210
211
212 /* Print diagnostic message. */
213 static void
214 err (const char *format, ...)
215 {
216   va_list arg_ptr;
217
218   fflush (stdout);
219   if (strncmp (format, "%s:%d:", 6))
220     fprintf (stderr, "%s: ", PGM);
221
222   va_start (arg_ptr, format);
223   vfprintf (stderr, format, arg_ptr);
224   va_end (arg_ptr);
225   putc ('\n', stderr);
226   any_error = 1;
227 }
228
229 /* Print diagnostic message. */
230 static void
231 inf (const char *format, ...)
232 {
233   va_list arg_ptr;
234
235   fflush (stdout);
236   fprintf (stderr, "%s: ", PGM);
237
238   va_start (arg_ptr, format);
239   vfprintf (stderr, format, arg_ptr);
240   va_end (arg_ptr);
241   putc ('\n', stderr);
242 }
243
244
245 static void *
246 xmalloc (size_t n)
247 {
248   void *p = malloc (n);
249   if (!p)
250     die ("out of core: %s", strerror (errno));
251   return p;
252 }
253
254 static void *
255 xcalloc (size_t n, size_t m)
256 {
257   void *p = calloc (n, m);
258   if (!p)
259     die ("out of core: %s", strerror (errno));
260   return p;
261 }
262
263 static void *
264 xrealloc (void *old, size_t n)
265 {
266   void *p = realloc (old, n);
267   if (!p)
268     die ("out of core: %s", strerror (errno));
269   return p;
270 }
271
272 static char *
273 xstrdup (const char *string)
274 {
275   void *p = malloc (strlen (string)+1);
276   if (!p)
277     die ("out of core: %s", strerror (errno));
278   strcpy (p, string);
279   return p;
280 }
281
282
283 /* Uppercase the ascii characters in STRING.  */
284 static char *
285 ascii_strupr (char *string)
286 {
287   char *p;
288
289   for (p = string; *p; p++)
290     if (!(*p & 0x80))
291       *p = toupper (*p);
292   return string;
293 }
294
295
296 /* Return the current date as an ISO string.  */
297 const char *
298 isodatestring (void)
299 {
300   static char buffer[11+5];
301   struct tm *tp;
302   time_t atime = time (NULL);
303
304   if (atime < 0)
305     strcpy (buffer, "????" "-??" "-??");
306   else
307     {
308       tp = gmtime (&atime);
309       sprintf (buffer,"%04d-%02d-%02d",
310                1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
311     }
312   return buffer;
313 }
314
315
316
317 /* Return a section buffer for the section NAME.  Allocate a new buffer
318    if this is a new section.  Keep track of the sections in THEPAGE.
319    This function may reallocate the section array in THEPAGE.  */
320 static section_buffer_t
321 get_section_buffer (const char *name)
322 {
323   int i;
324   section_buffer_t sect;
325
326   /* If there is no section we put everything into the required NAME
327      section.  Given that this is the first one listed it is likely
328      that error are easily visible.  */
329   if (!name)
330     name = "NAME";
331
332   for (i=0; i < thepage.n_sections; i++)
333     {
334       sect = thepage.sections + i;
335       if (sect->name && !strcmp (name, sect->name))
336         return sect;
337     }
338   for (i=0; i < thepage.n_sections; i++)
339     if (!thepage.sections[i].name)
340       break;
341   if (i < thepage.n_sections)
342     sect = thepage.sections + i;
343   else
344     {
345       /* We need to allocate or reallocate the section array.  */
346       size_t old_n = thepage.n_sections;
347       size_t new_n = 20;
348
349       if (!old_n)
350         thepage.sections = xcalloc (new_n, sizeof *thepage.sections);
351       else
352         {
353           thepage.sections = xrealloc (thepage.sections,
354                                        ((old_n + new_n)
355                                         * sizeof *thepage.sections));
356           memset (thepage.sections + old_n, 0,
357                   new_n * sizeof *thepage.sections);
358         }
359       thepage.n_sections += new_n;
360
361       /* Setup the tail pointers.  */
362       for (i=old_n; i < thepage.n_sections; i++)
363         {
364           sect = thepage.sections + i;
365           sect->lines_tail = &sect->lines;
366         }
367       sect = thepage.sections + old_n;
368     }
369
370   /* Store the name.  */
371   assert (!sect->name);
372   sect->name = xstrdup (name);
373   return sect;
374 }
375
376
377
378 /* Add the content of LINE to the section named SECTNAME.  */
379 static void
380 add_content (const char *sectname, char *line, int verbatim)
381 {
382   section_buffer_t sect;
383   line_buffer_t lb;
384
385   sect = get_section_buffer (sectname);
386   if (sect->last_line && !sect->last_line->verbatim == !verbatim)
387     {
388       /* Lets append that line to the last one.  We do this to keep
389          all lines of the same kind (i.e.verbatim or not) together in
390          one large buffer.  */
391       size_t n1, n;
392
393       lb = sect->last_line;
394       n1 = strlen (lb->line);
395       n = n1 + 1 + strlen (line) + 1;
396       lb->line = xrealloc (lb->line, n);
397       strcpy (lb->line+n1, "\n");
398       strcpy (lb->line+n1+1, line);
399     }
400   else
401     {
402       lb = xcalloc (1, sizeof *lb);
403       lb->verbatim = verbatim;
404       lb->line = xstrdup (line);
405       sect->last_line = lb;
406       *sect->lines_tail = lb;
407       sect->lines_tail = &lb->next;
408     }
409 }
410
411
412 /* Prepare for a new man page using the filename NAME. */
413 static void
414 start_page (char *name)
415 {
416   if (verbose)
417     inf ("starting page '%s'", name);
418   assert (!thepage.name);
419   thepage.name = xstrdup (name);
420   thepage.n_sections = 0;
421 }
422
423
424 /* Write the .TH entry of the current page.  Return -1 if there is a
425    problem with the page. */
426 static int
427 write_th (FILE *fp)
428 {
429   char *name, *p;
430
431   fputs (".\\\" Created from Texinfo source by yat2m " VERSION "\n", fp);
432
433   name = ascii_strupr (xstrdup (thepage.name));
434   p = strrchr (name, '.');
435   if (!p || !p[1])
436     {
437       err ("no section name in man page '%s'", thepage.name);
438       free (name);
439       return -1;
440     }
441   *p++ = 0;
442   fprintf (fp, ".TH %s %s %s \"%s\" \"%s\"\n",
443            name, p, isodatestring (), opt_release, opt_source);
444   return 0;
445 }
446
447
448 /* Process the texinfo command COMMAND (without the leading @) and
449    write output if needed to FP. REST is the remainer of the line
450    which should either point to an opening brace or to a white space.
451    The function returns the number of characters already processed
452    from REST.  LEN is the usable length of REST.  TABLE_LEVEL is used to
453    control the indentation of tables.  */
454 static size_t
455 proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len,
456                int *table_level, int *eol_action)
457 {
458   static struct {
459     const char *name;    /* Name of the command.  */
460     int what;            /* What to do with this command. */
461     const char *lead_in; /* String to print with a opening brace.  */
462     const char *lead_out;/* String to print with the closing brace. */
463   } cmdtbl[] = {
464     { "command", 0, "\\fB", "\\fR" },
465     { "code",    0, "\\fB", "\\fR" },
466     { "sc",      0, "\\fB", "\\fR" },
467     { "var",     0, "\\fI", "\\fR" },
468     { "samp",    0, "\\(aq", "\\(aq"  },
469     { "file",    0, "\\(oq\\fI","\\fR\\(cq" },
470     { "env",     0, "\\(oq\\fI","\\fR\\(cq" },
471     { "acronym", 0 },
472     { "dfn",     0 },
473     { "option",  0, "\\fB", "\\fR"   },
474     { "example", 1, ".RS 2\n.nf\n" },
475     { "smallexample", 1, ".RS 2\n.nf\n" },
476     { "asis",    7 },
477     { "anchor",  7 },
478     { "cartouche", 1 },
479     { "xref",    0, "see: [", "]" },
480     { "pxref",   0, "see: [", "]" },
481     { "uref",    0, "(\\fB", "\\fR)" },
482     { "footnote",0, " ([", "])" },
483     { "emph",    0, "\\fI", "\\fR" },
484     { "w",       1 },
485     { "c",       5 },
486     { "opindex", 1 },
487     { "cpindex", 1 },
488     { "cindex",  1 },
489     { "noindent", 0 },
490     { "section", 1 },
491     { "chapter", 1 },
492     { "subsection", 6, "\n.SS " },
493     { "chapheading", 0},
494     { "item",    2, ".TP\n.B " },
495     { "itemx",   2, ".TP\n.B " },
496     { "table",   3 },
497     { "itemize",   3 },
498     { "bullet",  0, "* " },
499     { "end",     4 },
500     { "quotation",1, ".RS\n\\fB" },
501     { NULL }
502   };
503   size_t n;
504   int i;
505   const char *s;
506   const char *lead_out = NULL;
507   int ignore_args = 0;
508
509   for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
510     ;
511   if (cmdtbl[i].name)
512     {
513       s = cmdtbl[i].lead_in;
514       if (s)
515         fputs (s, fp);
516       lead_out = cmdtbl[i].lead_out;
517       switch (cmdtbl[i].what)
518         {
519         case 1: /* Throw away the entire line.  */
520           s = memchr (rest, '\n', len);
521           return s? (s-rest)+1 : len;
522         case 2: /* Handle @item.  */
523           break;
524         case 3: /* Handle table.  */
525           if (++(*table_level) > 1)
526             fputs (".RS\n", fp);
527           /* Now throw away the entire line. */
528           s = memchr (rest, '\n', len);
529           return s? (s-rest)+1 : len;
530           break;
531         case 4: /* Handle end.  */
532           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
533             ;
534           if (n >= 5 && !memcmp (s, "table", 5)
535               && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
536             {
537               if ((*table_level)-- > 1)
538                 fputs (".RE\n", fp);
539             }
540           else if (n >= 7 && !memcmp (s, "example", 7)
541               && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
542             {
543               fputs (".fi\n.RE\n", fp);
544             }
545           else if (n >= 12 && !memcmp (s, "smallexample", 12)
546               && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
547             {
548               fputs (".fi\n.RE\n", fp);
549             }
550           else if (n >= 9 && !memcmp (s, "quotation", 9)
551               && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
552             {
553               fputs ("\\fR\n.RE\n", fp);
554             }
555           /* Now throw away the entire line. */
556           s = memchr (rest, '\n', len);
557           return s? (s-rest)+1 : len;
558         case 5: /* Handle special comments. */
559           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
560             ;
561           if (n >= 4 && !memcmp (s, "man:", 4))
562             {
563               for (s+=4, n-=4; n && *s != '\n'; n--, s++)
564                 putc (*s, fp);
565               putc ('\n', fp);
566             }
567           /* Now throw away the entire line. */
568           s = memchr (rest, '\n', len);
569           return s? (s-rest)+1 : len;
570         case 6:
571           *eol_action = 1;
572           break;
573         case 7:
574           ignore_args = 1;
575           break;
576         default:
577           break;
578         }
579     }
580   else
581     {
582       macro_t m;
583
584       for (m = macrolist; m ; m = m->next)
585         if (!strcmp (m->name, command))
586             break;
587       if (m)
588         {
589           proc_texi_buffer (fp, m->value, strlen (m->value),
590                             table_level, eol_action);
591           ignore_args = 1; /* Parameterized macros are not yet supported. */
592         }
593       else
594         inf ("texinfo command '%s' not supported (%.*s)", command,
595              ((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
596     }
597
598   if (*rest == '{')
599     {
600       /* Find matching closing brace.  */
601       for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
602         if (*s == '{')
603           i++;
604         else if (*s == '}')
605           i--;
606       if (i)
607         {
608           err ("closing brace for command '%s' not found", command);
609           return len;
610         }
611       if (n > 2 && !ignore_args)
612         proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
613     }
614   else
615     n = 0;
616
617   if (lead_out)
618     fputs (lead_out, fp);
619
620   return n;
621 }
622
623
624
625 /* Process the string LINE with LEN bytes of Texinfo content. */
626 static void
627 proc_texi_buffer (FILE *fp, const char *line, size_t len,
628                   int *table_level, int *eol_action)
629 {
630   const char *s;
631   char cmdbuf[256];
632   int cmdidx = 0;
633   int in_cmd = 0;
634   size_t n;
635
636   for (s=line; *s && len; s++, len--)
637     {
638       if (in_cmd)
639         {
640           if (in_cmd == 1)
641             {
642               switch (*s)
643                 {
644                 case '@': case '{': case '}':
645                   putc (*s, fp); in_cmd = 0;
646                   break;
647                 case ':': /* Not ending a sentence flag.  */
648                   in_cmd = 0;
649                   break;
650                 case '.': case '!': case '?': /* Ending a sentence. */
651                   putc (*s, fp); in_cmd = 0;
652                   break;
653                 case ' ': case '\t': case '\n': /* Non collapsing spaces.  */
654                   putc (*s, fp); in_cmd = 0;
655                   break;
656                 default:
657                   cmdidx = 0;
658                   cmdbuf[cmdidx++] = *s;
659                   in_cmd++;
660                   break;
661                 }
662             }
663           else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
664             {
665               cmdbuf[cmdidx] = 0;
666               n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
667               assert (n <= len);
668               s += n; len -= n;
669               s--; len++;
670               in_cmd = 0;
671             }
672           else if (cmdidx < sizeof cmdbuf -1)
673             cmdbuf[cmdidx++] = *s;
674           else
675             {
676               err ("texinfo command too long - ignored");
677               in_cmd = 0;
678             }
679         }
680       else if (*s == '@')
681         in_cmd = 1;
682       else if (*s == '\n')
683         {
684           switch (*eol_action)
685             {
686             case 1: /* Create a dummy paragraph. */
687               fputs ("\n\\ \n", fp);
688               break;
689             default:
690               putc (*s, fp);
691             }
692           *eol_action = 0;
693         }
694       else if (*s == '\\')
695         fputs ("\\\\", fp);
696       else
697         putc (*s, fp);
698     }
699
700   if (in_cmd > 1)
701     {
702       cmdbuf[cmdidx] = 0;
703       n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
704       assert (n <= len);
705       s += n; len -= n;
706       s--; len++;
707       in_cmd = 0;
708     }
709 }
710
711
712 /* Do something with the Texinfo line LINE.  */
713 static void
714 parse_texi_line (FILE *fp, const char *line, int *table_level)
715 {
716   int eol_action = 0;
717
718   /* A quick test whether there are any texinfo commands.  */
719   if (!strchr (line, '@'))
720     {
721       fputs (line, fp);
722       putc ('\n', fp);
723       return;
724     }
725   proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
726   putc ('\n', fp);
727 }
728
729
730 /* Write all the lines LINES to FP.  */
731 static void
732 write_content (FILE *fp, line_buffer_t lines)
733 {
734   line_buffer_t line;
735   int table_level = 0;
736
737   for (line = lines; line; line = line->next)
738     {
739       if (line->verbatim)
740         {
741           fputs (line->line, fp);
742           putc ('\n', fp);
743         }
744       else
745         {
746 /*           fputs ("TEXI---", fp); */
747 /*           fputs (line->line, fp); */
748 /*           fputs ("---\n", fp); */
749           parse_texi_line (fp, line->line, &table_level);
750         }
751     }
752 }
753
754
755
756 static int
757 is_standard_section (const char *name)
758 {
759   int i;
760   const char *s;
761
762   for (i=0; (s=standard_sections[i]); i++)
763     if (!strcmp (s, name))
764       return 1;
765   return 0;
766 }
767
768
769 /* Finish a page; that is sort the data and write it out to the file.  */
770 static void
771 finish_page (void)
772 {
773   FILE *fp;
774   section_buffer_t sect = NULL;
775   int idx;
776   const char *s;
777   int i;
778
779   if (!thepage.name)
780     return; /* No page active.  */
781
782   if (verbose)
783     inf ("finishing page '%s'", thepage.name);
784
785   if (opt_select)
786     {
787       if (!strcmp (opt_select, thepage.name))
788         {
789           inf ("selected '%s'", thepage.name );
790           fp = stdout;
791         }
792       else
793         {
794           fp = fopen ( "/dev/null", "w" );
795           if (!fp)
796             die ("failed to open /dev/null: %s\n", strerror (errno));
797         }
798     }
799   else if (opt_store)
800     {
801       inf ("writing '%s'", thepage.name );
802       fp = fopen ( thepage.name, "w" );
803       if (!fp)
804         die ("failed to create '%s': %s\n", thepage.name, strerror (errno));
805     }
806   else
807     fp = stdout;
808
809   if (write_th (fp))
810     goto leave;
811
812   for (idx=0; (s=standard_sections[idx]); idx++)
813     {
814       for (i=0; i < thepage.n_sections; i++)
815         {
816           sect = thepage.sections + i;
817           if (sect->name && !strcmp (s, sect->name))
818             break;
819         }
820       if (i == thepage.n_sections)
821         sect = NULL;
822
823       if (sect)
824         {
825           fprintf (fp, ".SH %s\n", sect->name);
826           write_content (fp, sect->lines);
827           /* Now continue with all non standard sections directly
828              following this one. */
829           for (i++; i < thepage.n_sections; i++)
830             {
831               sect = thepage.sections + i;
832               if (sect->name && is_standard_section (sect->name))
833                 break;
834               if (sect->name)
835                 {
836                   fprintf (fp, ".SH %s\n", sect->name);
837                   write_content (fp, sect->lines);
838                 }
839             }
840
841         }
842     }
843
844
845  leave:
846   if (fp != stdout)
847     fclose (fp);
848   free (thepage.name);
849   thepage.name = NULL;
850   /* FIXME: Cleanup the content.  */
851 }
852
853
854
855
856 /* Parse one Texinfo file and create manpages according to the
857    embedded instructions.  */
858 static void
859 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
860 {
861   char *line;
862   int lnr = 0;
863   /* Fixme: The following state variables don't carry over to include
864      files. */
865   int in_verbatim = 0;
866   int skip_to_end = 0;        /* Used to skip over menu entries. */
867   int skip_sect_line = 0;     /* Skip after @mansect.  */
868   int ifset_nesting = 0;      /* How often a ifset has been seen. */
869   int ifclear_nesting = 0;    /* How often a ifclear has been seen. */
870   int in_gpgone = 0;          /* Keep track of "@ifset gpgone" parts.  */
871   int not_in_gpgone = 0;      /* Keep track of "@ifclear gpgone" parts.  */
872   int not_in_man = 0;         /* Keep track of "@ifclear isman" parts.  */
873   int item_indent = 0;        /* How far is the current @item indented.  */
874
875   /* Helper to define a macro. */
876   char *macroname = NULL;
877   char *macrovalue = NULL;
878   size_t macrovaluesize = 0;
879   size_t macrovalueused = 0;
880
881   line = xmalloc (LINESIZE);
882   while (fgets (line, LINESIZE, fp))
883     {
884       size_t n = strlen (line);
885       int got_line = 0;
886       char *p;
887
888       lnr++;
889       if (!n || line[n-1] != '\n')
890         {
891           err ("%s:%d: trailing linefeed missing, line too long or "
892                "embedded Nul character", fname, lnr);
893           break;
894         }
895       line[--n] = 0;
896
897       /* Kludge to allow indentation of tables.  */
898       for (p=line; *p == ' ' || *p == '\t'; p++)
899         ;
900       if (*p)
901         {
902           if (*p == '@' && !strncmp (p+1, "item", 4))
903             item_indent = p - line;  /* Set a new indent level.  */
904           else if (p - line < item_indent)
905             item_indent = 0;         /* Switch off indention.  */
906
907           if (item_indent)
908             {
909               memmove (line, line+item_indent, n - item_indent + 1);
910               n -= item_indent;
911             }
912         }
913
914
915       if (*line == '@')
916         {
917           for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
918             n++;
919           while (*p == ' ' || *p == '\t')
920             p++;
921         }
922       else
923         p = line;
924
925       /* Take action on macro.  */
926       if (macroname)
927         {
928           if (n == 4 && !memcmp (line, "@end", 4)
929               && (line[4]==' '||line[4]=='\t'||!line[4])
930               && !strncmp (p, "macro", 5)
931               && (p[5]==' '||p[5]=='\t'||!p[5]))
932             {
933               macro_t m;
934
935               if (macrovalueused)
936                 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
937               macrovalue[macrovalueused] = 0;     /* Terminate macro. */
938               macrovalue = xrealloc (macrovalue, macrovalueused+1);
939
940               for (m= macrolist; m; m = m->next)
941                 if (!strcmp (m->name, macroname))
942                   break;
943               if (m)
944                 free (m->value);
945               else
946                 {
947                   m = xcalloc (1, sizeof *m + strlen (macroname));
948                   strcpy (m->name, macroname);
949                   m->next = macrolist;
950                   macrolist = m;
951                 }
952               m->value = macrovalue;
953               macrovalue = NULL;
954               free (macroname);
955               macroname = NULL;
956             }
957           else
958             {
959               if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
960                 {
961                   macrovaluesize += strlen (line) + 256;
962                   macrovalue = xrealloc (macrovalue,  macrovaluesize);
963                 }
964               strcpy (macrovalue+macrovalueused, line);
965               macrovalueused += strlen (line);
966               macrovalue[macrovalueused++] = '\n';
967             }
968           continue;
969         }
970
971
972       if (n >= 5 && !memcmp (line, "@node", 5)
973           && (line[5]==' '||line[5]=='\t'||!line[5]))
974         {
975           /* Completey ignore @node lines.  */
976           continue;
977         }
978
979
980       if (skip_sect_line)
981         {
982           skip_sect_line = 0;
983           if (!strncmp (line, "@section", 8)
984               || !strncmp (line, "@subsection", 11)
985               || !strncmp (line, "@chapheading", 12))
986             continue;
987         }
988
989       /* We only parse lines we need and ignore the rest.  There are a
990          few macros used to control this as well as one @ifset
991          command.  Parts we know about are saved away into containers
992          separate for each section. */
993
994       /* First process ifset/ifclear commands. */
995       if (*line == '@')
996         {
997           if (n == 6 && !memcmp (line, "@ifset", 6)
998                    && (line[6]==' '||line[6]=='\t'))
999             {
1000               ifset_nesting++;
1001
1002               if (!strncmp (p, "manverb", 7) && (p[7]==' '||p[7]=='\t'||!p[7]))
1003                 {
1004                   if (in_verbatim)
1005                     err ("%s:%d: nested \"@ifset manverb\"", fname, lnr);
1006                   else
1007                     in_verbatim = ifset_nesting;
1008                 }
1009               else if (!strncmp (p, "gpgone", 6)
1010                        && (p[6]==' '||p[6]=='\t'||!p[6]))
1011                 {
1012                   if (in_gpgone)
1013                     err ("%s:%d: nested \"@ifset gpgone\"", fname, lnr);
1014                   else
1015                     in_gpgone = ifset_nesting;
1016                 }
1017               continue;
1018             }
1019           else if (n == 4 && !memcmp (line, "@end", 4)
1020                    && (line[4]==' '||line[4]=='\t')
1021                    && !strncmp (p, "ifset", 5)
1022                    && (p[5]==' '||p[5]=='\t'||!p[5]))
1023             {
1024               if (in_verbatim && ifset_nesting == in_verbatim)
1025                 in_verbatim = 0;
1026               if (in_gpgone && ifset_nesting == in_gpgone)
1027                 in_gpgone = 0;
1028
1029               if (ifset_nesting)
1030                 ifset_nesting--;
1031               else
1032                 err ("%s:%d: unbalanced \"@end ifset\"", fname, lnr);
1033               continue;
1034             }
1035           else if (n == 8 && !memcmp (line, "@ifclear", 8)
1036                    && (line[8]==' '||line[8]=='\t'))
1037             {
1038               ifclear_nesting++;
1039
1040               if (!strncmp (p, "gpgone", 6)
1041                   && (p[6]==' '||p[6]=='\t'||!p[6]))
1042                 {
1043                   if (not_in_gpgone)
1044                     err ("%s:%d: nested \"@ifclear gpgone\"", fname, lnr);
1045                   else
1046                     not_in_gpgone = ifclear_nesting;
1047                 }
1048
1049               else if (!strncmp (p, "isman", 5)
1050                        && (p[5]==' '||p[5]=='\t'||!p[5]))
1051                 {
1052                   if (not_in_man)
1053                     err ("%s:%d: nested \"@ifclear isman\"", fname, lnr);
1054                   else
1055                     not_in_man = ifclear_nesting;
1056                 }
1057
1058               continue;
1059             }
1060           else if (n == 4 && !memcmp (line, "@end", 4)
1061                    && (line[4]==' '||line[4]=='\t')
1062                    && !strncmp (p, "ifclear", 7)
1063                    && (p[7]==' '||p[7]=='\t'||!p[7]))
1064             {
1065               if (not_in_gpgone && ifclear_nesting == not_in_gpgone)
1066                 not_in_gpgone = 0;
1067               if (not_in_man && ifclear_nesting == not_in_man)
1068                 not_in_man = 0;
1069
1070               if (ifclear_nesting)
1071                 ifclear_nesting--;
1072               else
1073                 err ("%s:%d: unbalanced \"@end ifclear\"", fname, lnr);
1074               continue;
1075             }
1076         }
1077
1078       /* Take action on ifset/ifclear.  */
1079       if ( (in_gpgone && !gpgone_defined)
1080            || (not_in_gpgone && gpgone_defined)
1081            || not_in_man)
1082         continue;
1083
1084       /* Process commands. */
1085       if (*line == '@')
1086         {
1087           if (skip_to_end
1088               && n == 4 && !memcmp (line, "@end", 4)
1089               && (line[4]==' '||line[4]=='\t'||!line[4]))
1090             {
1091               skip_to_end = 0;
1092             }
1093           else if (in_verbatim)
1094             {
1095                 got_line = 1;
1096             }
1097           else if (n == 6 && !memcmp (line, "@macro", 6))
1098             {
1099               macroname = xstrdup (p);
1100               macrovalue = xmalloc ((macrovaluesize = 1024));
1101               macrovalueused = 0;
1102             }
1103           else if (n == 8 && !memcmp (line, "@manpage", 8))
1104             {
1105               free (*section_name);
1106               *section_name = NULL;
1107               finish_page ();
1108               start_page (p);
1109               in_pause = 0;
1110             }
1111           else if (n == 8 && !memcmp (line, "@mansect", 8))
1112             {
1113               if (!thepage.name)
1114                 err ("%s:%d: section outside of a man page", fname, lnr);
1115               else
1116                 {
1117                   free (*section_name);
1118                   *section_name = ascii_strupr (xstrdup (p));
1119                   in_pause = 0;
1120                   skip_sect_line = 1;
1121                 }
1122             }
1123           else if (n == 9 && !memcmp (line, "@manpause", 9))
1124             {
1125               if (!*section_name)
1126                 err ("%s:%d: pausing outside of a man section", fname, lnr);
1127               else if (in_pause)
1128                 err ("%s:%d: already pausing", fname, lnr);
1129               else
1130                 in_pause = 1;
1131             }
1132           else if (n == 8 && !memcmp (line, "@mancont", 8))
1133             {
1134               if (!*section_name)
1135                 err ("%s:%d: continue outside of a man section", fname, lnr);
1136               else if (!in_pause)
1137                 err ("%s:%d: continue while not pausing", fname, lnr);
1138               else
1139                 in_pause = 0;
1140             }
1141           else if (n == 5 && !memcmp (line, "@menu", 5)
1142                    && (line[5]==' '||line[5]=='\t'||!line[5]))
1143             {
1144               skip_to_end = 1;
1145             }
1146           else if (n == 8 && !memcmp (line, "@include", 8)
1147                    && (line[8]==' '||line[8]=='\t'||!line[8]))
1148             {
1149               char *incname = xstrdup (p);
1150               FILE *incfp = fopen (incname, "r");
1151
1152               if (!incfp && opt_include && *opt_include && *p != '/')
1153                 {
1154                   free (incname);
1155                   incname = xmalloc (strlen (opt_include) + 1
1156                                      + strlen (p) + 1);
1157                   strcpy (incname, opt_include);
1158                   if ( incname[strlen (incname)-1] != '/' )
1159                     strcat (incname, "/");
1160                   strcat (incname, p);
1161                   incfp = fopen (incname, "r");
1162                 }
1163
1164               if (!incfp)
1165                 err ("can't open include file '%s':%s",
1166                      incname, strerror (errno));
1167               else
1168                 {
1169                   parse_file (incname, incfp, section_name, in_pause);
1170                   fclose (incfp);
1171                 }
1172               free (incname);
1173             }
1174           else if (n == 4 && !memcmp (line, "@bye", 4)
1175                    && (line[4]==' '||line[4]=='\t'||!line[4]))
1176             {
1177               break;
1178             }
1179           else if (!skip_to_end)
1180             got_line = 1;
1181         }
1182       else if (!skip_to_end)
1183         got_line = 1;
1184
1185       if (got_line && in_verbatim)
1186         add_content (*section_name, line, 1);
1187       else if (got_line && thepage.name && *section_name && !in_pause)
1188         add_content (*section_name, line, 0);
1189
1190     }
1191   if (ferror (fp))
1192     err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1193   free (macroname);
1194   free (macrovalue);
1195   free (line);
1196 }
1197
1198
1199 static void
1200 top_parse_file (const char *fname, FILE *fp)
1201 {
1202   char *section_name = NULL;  /* Name of the current section or NULL
1203                                  if not in a section.  */
1204   while (macrolist)
1205     {
1206       macro_t next = macrolist->next;
1207       free (macrolist->value);
1208       free (macrolist);
1209       macrolist = next;
1210     }
1211
1212   parse_file (fname, fp, &section_name, 0);
1213   free (section_name);
1214   finish_page ();
1215 }
1216
1217
1218 int
1219 main (int argc, char **argv)
1220 {
1221   int last_argc = -1;
1222
1223   opt_source = "GNU";
1224   opt_release = "";
1225
1226   if (argc)
1227     {
1228       argc--; argv++;
1229     }
1230   while (argc && last_argc != argc )
1231     {
1232       last_argc = argc;
1233       if (!strcmp (*argv, "--"))
1234         {
1235           argc--; argv++;
1236           break;
1237         }
1238       else if (!strcmp (*argv, "--help"))
1239         {
1240           puts (
1241                 "Usage: " PGM " [OPTION] [FILE]\n"
1242                 "Extract man pages from a Texinfo source.\n\n"
1243                 "  --source NAME    use NAME as source field\n"
1244                 "  --release STRING use STRING as the release field\n"
1245                 "  --store          write output using @manpage name\n"
1246                 "  --select NAME    only output pages with @manpage NAME\n"
1247                 "  --verbose        enable extra informational output\n"
1248                 "  --debug          enable additional debug output\n"
1249                 "  --help           display this help and exit\n"
1250                 "  -I DIR           also search in include DIR\n"
1251                 "  -D gpgone        the only useable define\n\n"
1252                 "With no FILE, or when FILE is -, read standard input.\n\n"
1253                 "Report bugs to <bugs@g10code.com>.");
1254           exit (0);
1255         }
1256       else if (!strcmp (*argv, "--version"))
1257         {
1258           puts (PGM " " VERSION "\n"
1259                "Copyright (C) 2005 g10 Code GmbH\n"
1260                "This program comes with ABSOLUTELY NO WARRANTY.\n"
1261                "This is free software, and you are welcome to redistribute it\n"
1262                 "under certain conditions. See the file COPYING for details.");
1263           exit (0);
1264         }
1265       else if (!strcmp (*argv, "--verbose"))
1266         {
1267           verbose = 1;
1268           argc--; argv++;
1269         }
1270       else if (!strcmp (*argv, "--quiet"))
1271         {
1272           quiet = 1;
1273           argc--; argv++;
1274         }
1275       else if (!strcmp (*argv, "--debug"))
1276         {
1277           verbose = debug = 1;
1278           argc--; argv++;
1279         }
1280       else if (!strcmp (*argv, "--source"))
1281         {
1282           argc--; argv++;
1283           if (argc)
1284             {
1285               opt_source = *argv;
1286               argc--; argv++;
1287             }
1288         }
1289       else if (!strcmp (*argv, "--release"))
1290         {
1291           argc--; argv++;
1292           if (argc)
1293             {
1294               opt_release = *argv;
1295               argc--; argv++;
1296             }
1297         }
1298       else if (!strcmp (*argv, "--store"))
1299         {
1300           opt_store = 1;
1301           argc--; argv++;
1302         }
1303       else if (!strcmp (*argv, "--select"))
1304         {
1305           argc--; argv++;
1306           if (argc)
1307             {
1308               opt_select = strrchr (*argv, '/');
1309               if (opt_select)
1310                 opt_select++;
1311               else
1312                 opt_select = *argv;
1313               argc--; argv++;
1314             }
1315         }
1316       else if (!strcmp (*argv, "-I"))
1317         {
1318           argc--; argv++;
1319           if (argc)
1320             {
1321               opt_include = *argv;
1322               argc--; argv++;
1323             }
1324         }
1325       else if (!strcmp (*argv, "-D"))
1326         {
1327           argc--; argv++;
1328           if (argc)
1329             {
1330               if (!strcmp (*argv, "gpgone"))
1331                 gpgone_defined = 1;
1332               argc--; argv++;
1333             }
1334         }
1335     }
1336
1337   if (argc > 1)
1338     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1339
1340   /* Start processing. */
1341   if (argc && strcmp (*argv, "-"))
1342     {
1343       FILE *fp = fopen (*argv, "rb");
1344       if (!fp)
1345         die ("%s:0: can't open file: %s", *argv, strerror (errno));
1346       top_parse_file (*argv, fp);
1347       fclose (fp);
1348     }
1349   else
1350     top_parse_file ("-", stdin);
1351
1352   return !!any_error;
1353 }
1354
1355
1356 /*
1357 Local Variables:
1358 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1359 End:
1360 */