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