gpg: Add list-option "show-usage".
[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   return 0;
613 }
614
615
616 /* Process the texinfo command COMMAND (without the leading @) and
617    write output if needed to FP. REST is the remainer of the line
618    which should either point to an opening brace or to a white space.
619    The function returns the number of characters already processed
620    from REST.  LEN is the usable length of REST.  TABLE_LEVEL is used to
621    control the indentation of tables.  */
622 static size_t
623 proc_texi_cmd (FILE *fp, const char *command, const char *rest, size_t len,
624                int *table_level, int *eol_action)
625 {
626   static struct {
627     const char *name;    /* Name of the command.  */
628     int what;            /* What to do with this command. */
629     const char *lead_in; /* String to print with a opening brace.  */
630     const char *lead_out;/* String to print with the closing brace. */
631   } cmdtbl[] = {
632     { "command", 0, "\\fB", "\\fR" },
633     { "code",    0, "\\fB", "\\fR" },
634     { "sc",      0, "\\fB", "\\fR" },
635     { "var",     0, "\\fI", "\\fR" },
636     { "samp",    0, "\\(aq", "\\(aq"  },
637     { "file",    0, "\\(oq\\fI","\\fR\\(cq" },
638     { "env",     0, "\\(oq\\fI","\\fR\\(cq" },
639     { "acronym", 0 },
640     { "dfn",     0 },
641     { "option",  0, "\\fB", "\\fR"   },
642     { "example", 1, ".RS 2\n.nf\n" },
643     { "smallexample", 1, ".RS 2\n.nf\n" },
644     { "asis",    7 },
645     { "anchor",  7 },
646     { "cartouche", 1 },
647     { "xref",    0, "see: [", "]" },
648     { "pxref",   0, "see: [", "]" },
649     { "uref",    0, "(\\fB", "\\fR)" },
650     { "footnote",0, " ([", "])" },
651     { "emph",    0, "\\fI", "\\fR" },
652     { "w",       1 },
653     { "c",       5 },
654     { "opindex", 1 },
655     { "cpindex", 1 },
656     { "cindex",  1 },
657     { "noindent", 0 },
658     { "section", 1 },
659     { "chapter", 1 },
660     { "subsection", 6, "\n.SS " },
661     { "chapheading", 0},
662     { "item",    2, ".TP\n.B " },
663     { "itemx",   2, ".TP\n.B " },
664     { "table",   3 },
665     { "itemize",   3 },
666     { "bullet",  0, "* " },
667     { "end",     4 },
668     { "quotation",1, ".RS\n\\fB" },
669     { NULL }
670   };
671   size_t n;
672   int i;
673   const char *s;
674   const char *lead_out = NULL;
675   int ignore_args = 0;
676
677   for (i=0; cmdtbl[i].name && strcmp (cmdtbl[i].name, command); i++)
678     ;
679   if (cmdtbl[i].name)
680     {
681       s = cmdtbl[i].lead_in;
682       if (s)
683         fputs (s, fp);
684       lead_out = cmdtbl[i].lead_out;
685       switch (cmdtbl[i].what)
686         {
687         case 1: /* Throw away the entire line.  */
688           s = memchr (rest, '\n', len);
689           return s? (s-rest)+1 : len;
690         case 2: /* Handle @item.  */
691           break;
692         case 3: /* Handle table.  */
693           if (++(*table_level) > 1)
694             fputs (".RS\n", fp);
695           /* Now throw away the entire line. */
696           s = memchr (rest, '\n', len);
697           return s? (s-rest)+1 : len;
698           break;
699         case 4: /* Handle end.  */
700           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
701             ;
702           if (n >= 5 && !memcmp (s, "table", 5)
703               && (!n || s[5] == ' ' || s[5] == '\t' || s[5] == '\n'))
704             {
705               if ((*table_level)-- > 1)
706                 fputs (".RE\n", fp);
707             }
708           else if (n >= 7 && !memcmp (s, "example", 7)
709               && (!n || s[7] == ' ' || s[7] == '\t' || s[7] == '\n'))
710             {
711               fputs (".fi\n.RE\n", fp);
712             }
713           else if (n >= 12 && !memcmp (s, "smallexample", 12)
714               && (!n || s[12] == ' ' || s[12] == '\t' || s[12] == '\n'))
715             {
716               fputs (".fi\n.RE\n", fp);
717             }
718           else if (n >= 9 && !memcmp (s, "quotation", 9)
719               && (!n || s[9] == ' ' || s[9] == '\t' || s[9] == '\n'))
720             {
721               fputs ("\\fR\n.RE\n", fp);
722             }
723           /* Now throw away the entire line. */
724           s = memchr (rest, '\n', len);
725           return s? (s-rest)+1 : len;
726         case 5: /* Handle special comments. */
727           for (s=rest, n=len; n && (*s == ' ' || *s == '\t'); s++, n--)
728             ;
729           if (n >= 4 && !memcmp (s, "man:", 4))
730             {
731               for (s+=4, n-=4; n && *s != '\n'; n--, s++)
732                 putc (*s, fp);
733               putc ('\n', fp);
734             }
735           /* Now throw away the entire line. */
736           s = memchr (rest, '\n', len);
737           return s? (s-rest)+1 : len;
738         case 6:
739           *eol_action = 1;
740           break;
741         case 7:
742           ignore_args = 1;
743           break;
744         default:
745           break;
746         }
747     }
748   else
749     {
750       macro_t m;
751
752       for (m = macrolist; m ; m = m->next)
753         if (!strcmp (m->name, command))
754             break;
755       if (m)
756         {
757           proc_texi_buffer (fp, m->value, strlen (m->value),
758                             table_level, eol_action);
759           ignore_args = 1; /* Parameterized macros are not yet supported. */
760         }
761       else
762         inf ("texinfo command '%s' not supported (%.*s)", command,
763              ((s = memchr (rest, '\n', len)), (s? (s-rest) : len)), rest);
764     }
765
766   if (*rest == '{')
767     {
768       /* Find matching closing brace.  */
769       for (s=rest+1, n=1, i=1; i && *s && n < len; s++, n++)
770         if (*s == '{')
771           i++;
772         else if (*s == '}')
773           i--;
774       if (i)
775         {
776           err ("closing brace for command '%s' not found", command);
777           return len;
778         }
779       if (n > 2 && !ignore_args)
780         proc_texi_buffer (fp, rest+1, n-2, table_level, eol_action);
781     }
782   else
783     n = 0;
784
785   if (lead_out)
786     fputs (lead_out, fp);
787
788   return n;
789 }
790
791
792
793 /* Process the string LINE with LEN bytes of Texinfo content. */
794 static void
795 proc_texi_buffer (FILE *fp, const char *line, size_t len,
796                   int *table_level, int *eol_action)
797 {
798   const char *s;
799   char cmdbuf[256];
800   int cmdidx = 0;
801   int in_cmd = 0;
802   size_t n;
803
804   for (s=line; *s && len; s++, len--)
805     {
806       if (in_cmd)
807         {
808           if (in_cmd == 1)
809             {
810               switch (*s)
811                 {
812                 case '@': case '{': case '}':
813                   putc (*s, fp); in_cmd = 0;
814                   break;
815                 case ':': /* Not ending a sentence flag.  */
816                   in_cmd = 0;
817                   break;
818                 case '.': case '!': case '?': /* Ending a sentence. */
819                   putc (*s, fp); in_cmd = 0;
820                   break;
821                 case ' ': case '\t': case '\n': /* Non collapsing spaces.  */
822                   putc (*s, fp); in_cmd = 0;
823                   break;
824                 default:
825                   cmdidx = 0;
826                   cmdbuf[cmdidx++] = *s;
827                   in_cmd++;
828                   break;
829                 }
830             }
831           else if (*s == '{' || *s == ' ' || *s == '\t' || *s == '\n')
832             {
833               cmdbuf[cmdidx] = 0;
834               n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
835               assert (n <= len);
836               s += n; len -= n;
837               s--; len++;
838               in_cmd = 0;
839             }
840           else if (cmdidx < sizeof cmdbuf -1)
841             cmdbuf[cmdidx++] = *s;
842           else
843             {
844               err ("texinfo command too long - ignored");
845               in_cmd = 0;
846             }
847         }
848       else if (*s == '@')
849         in_cmd = 1;
850       else if (*s == '\n')
851         {
852           switch (*eol_action)
853             {
854             case 1: /* Create a dummy paragraph. */
855               fputs ("\n\\ \n", fp);
856               break;
857             default:
858               putc (*s, fp);
859             }
860           *eol_action = 0;
861         }
862       else if (*s == '\\')
863         fputs ("\\\\", fp);
864       else
865         putc (*s, fp);
866     }
867
868   if (in_cmd > 1)
869     {
870       cmdbuf[cmdidx] = 0;
871       n = proc_texi_cmd (fp, cmdbuf, s, len, table_level, eol_action);
872       assert (n <= len);
873       s += n; len -= n;
874       s--; len++;
875       in_cmd = 0;
876     }
877 }
878
879
880 /* Do something with the Texinfo line LINE.  */
881 static void
882 parse_texi_line (FILE *fp, const char *line, int *table_level)
883 {
884   int eol_action = 0;
885
886   /* A quick test whether there are any texinfo commands.  */
887   if (!strchr (line, '@'))
888     {
889       fputs (line, fp);
890       putc ('\n', fp);
891       return;
892     }
893   proc_texi_buffer (fp, line, strlen (line), table_level, &eol_action);
894   putc ('\n', fp);
895 }
896
897
898 /* Write all the lines LINES to FP.  */
899 static void
900 write_content (FILE *fp, line_buffer_t lines)
901 {
902   line_buffer_t line;
903   int table_level = 0;
904
905   for (line = lines; line; line = line->next)
906     {
907       if (line->verbatim)
908         {
909           fputs (line->line, fp);
910           putc ('\n', fp);
911         }
912       else
913         {
914 /*           fputs ("TEXI---", fp); */
915 /*           fputs (line->line, fp); */
916 /*           fputs ("---\n", fp); */
917           parse_texi_line (fp, line->line, &table_level);
918         }
919     }
920 }
921
922
923
924 static int
925 is_standard_section (const char *name)
926 {
927   int i;
928   const char *s;
929
930   for (i=0; (s=standard_sections[i]); i++)
931     if (!strcmp (s, name))
932       return 1;
933   return 0;
934 }
935
936
937 /* Finish a page; that is sort the data and write it out to the file.  */
938 static void
939 finish_page (void)
940 {
941   FILE *fp;
942   section_buffer_t sect = NULL;
943   int idx;
944   const char *s;
945   int i;
946
947   if (!thepage.name)
948     return; /* No page active.  */
949
950   if (verbose)
951     inf ("finishing page '%s'", thepage.name);
952
953   if (opt_select)
954     {
955       if (!strcmp (opt_select, thepage.name))
956         {
957           inf ("selected '%s'", thepage.name );
958           fp = stdout;
959         }
960       else
961         {
962           fp = fopen ( "/dev/null", "w" );
963           if (!fp)
964             die ("failed to open /dev/null: %s\n", strerror (errno));
965         }
966     }
967   else if (opt_store)
968     {
969       inf ("writing '%s'", thepage.name );
970       fp = fopen ( thepage.name, "w" );
971       if (!fp)
972         die ("failed to create '%s': %s\n", thepage.name, strerror (errno));
973     }
974   else
975     fp = stdout;
976
977   if (write_th (fp))
978     goto leave;
979
980   for (idx=0; (s=standard_sections[idx]); idx++)
981     {
982       for (i=0; i < thepage.n_sections; i++)
983         {
984           sect = thepage.sections + i;
985           if (sect->name && !strcmp (s, sect->name))
986             break;
987         }
988       if (i == thepage.n_sections)
989         sect = NULL;
990
991       if (sect)
992         {
993           fprintf (fp, ".SH %s\n", sect->name);
994           write_content (fp, sect->lines);
995           /* Now continue with all non standard sections directly
996              following this one. */
997           for (i++; i < thepage.n_sections; i++)
998             {
999               sect = thepage.sections + i;
1000               if (sect->name && is_standard_section (sect->name))
1001                 break;
1002               if (sect->name)
1003                 {
1004                   fprintf (fp, ".SH %s\n", sect->name);
1005                   write_content (fp, sect->lines);
1006                 }
1007             }
1008
1009         }
1010     }
1011
1012
1013  leave:
1014   if (fp != stdout)
1015     fclose (fp);
1016   free (thepage.name);
1017   thepage.name = NULL;
1018   /* FIXME: Cleanup the content.  */
1019 }
1020
1021
1022
1023
1024 /* Parse one Texinfo file and create manpages according to the
1025    embedded instructions.  */
1026 static void
1027 parse_file (const char *fname, FILE *fp, char **section_name, int in_pause)
1028 {
1029   char *line;
1030   int lnr = 0;
1031   /* Fixme: The following state variables don't carry over to include
1032      files. */
1033   int skip_to_end = 0;        /* Used to skip over menu entries. */
1034   int skip_sect_line = 0;     /* Skip after @mansect.  */
1035   int item_indent = 0;        /* How far is the current @item indented.  */
1036
1037   /* Helper to define a macro. */
1038   char *macroname = NULL;
1039   char *macrovalue = NULL;
1040   size_t macrovaluesize = 0;
1041   size_t macrovalueused = 0;
1042
1043   line = xmalloc (LINESIZE);
1044   while (fgets (line, LINESIZE, fp))
1045     {
1046       size_t n = strlen (line);
1047       int got_line = 0;
1048       char *p, *pend;
1049
1050       lnr++;
1051       if (!n || line[n-1] != '\n')
1052         {
1053           err ("%s:%d: trailing linefeed missing, line too long or "
1054                "embedded Nul character", fname, lnr);
1055           break;
1056         }
1057       line[--n] = 0;
1058
1059       /* Kludge to allow indentation of tables.  */
1060       for (p=line; *p == ' ' || *p == '\t'; p++)
1061         ;
1062       if (*p)
1063         {
1064           if (*p == '@' && !strncmp (p+1, "item", 4))
1065             item_indent = p - line;  /* Set a new indent level.  */
1066           else if (p - line < item_indent)
1067             item_indent = 0;         /* Switch off indention.  */
1068
1069           if (item_indent)
1070             {
1071               memmove (line, line+item_indent, n - item_indent + 1);
1072               n -= item_indent;
1073             }
1074         }
1075
1076
1077       if (*line == '@')
1078         {
1079           for (p=line+1, n=1; *p && *p != ' ' && *p != '\t'; p++)
1080             n++;
1081           while (*p == ' ' || *p == '\t')
1082             p++;
1083         }
1084       else
1085         p = line;
1086
1087       /* Take action on macro.  */
1088       if (macroname)
1089         {
1090           if (n == 4 && !memcmp (line, "@end", 4)
1091               && (line[4]==' '||line[4]=='\t'||!line[4])
1092               && !strncmp (p, "macro", 5)
1093               && (p[5]==' '||p[5]=='\t'||!p[5]))
1094             {
1095               if (macrovalueused)
1096                 macrovalue[--macrovalueused] = 0; /* Kill the last LF. */
1097               macrovalue[macrovalueused] = 0;     /* Terminate macro. */
1098               macrovalue = xrealloc (macrovalue, macrovalueused+1);
1099
1100               set_macro (macroname, macrovalue);
1101               macrovalue = NULL;
1102               free (macroname);
1103               macroname = NULL;
1104             }
1105           else
1106             {
1107               if (macrovalueused + strlen (line) + 2 >= macrovaluesize)
1108                 {
1109                   macrovaluesize += strlen (line) + 256;
1110                   macrovalue = xrealloc (macrovalue,  macrovaluesize);
1111                 }
1112               strcpy (macrovalue+macrovalueused, line);
1113               macrovalueused += strlen (line);
1114               macrovalue[macrovalueused++] = '\n';
1115             }
1116           continue;
1117         }
1118
1119
1120       if (n >= 5 && !memcmp (line, "@node", 5)
1121           && (line[5]==' '||line[5]=='\t'||!line[5]))
1122         {
1123           /* Completey ignore @node lines.  */
1124           continue;
1125         }
1126
1127
1128       if (skip_sect_line)
1129         {
1130           skip_sect_line = 0;
1131           if (!strncmp (line, "@section", 8)
1132               || !strncmp (line, "@subsection", 11)
1133               || !strncmp (line, "@chapheading", 12))
1134             continue;
1135         }
1136
1137       /* We only parse lines we need and ignore the rest.  There are a
1138          few macros used to control this as well as one @ifset
1139          command.  Parts we know about are saved away into containers
1140          separate for each section. */
1141
1142       /* First process ifset/ifclear commands. */
1143       if (*line == '@')
1144         {
1145           if (n == 6 && !memcmp (line, "@ifset", 6)
1146                    && (line[6]==' '||line[6]=='\t'))
1147             {
1148               for (p=line+7; *p == ' ' || *p == '\t'; p++)
1149                 ;
1150               if (!*p)
1151                 {
1152                   err ("%s:%d: name missing after \"@ifset\"", fname, lnr);
1153                   continue;
1154                 }
1155               for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1156                 ;
1157               *pend = 0;  /* Ignore rest of the line.  */
1158               push_condition (p, 1, fname, lnr);
1159               continue;
1160             }
1161           else if (n == 8 && !memcmp (line, "@ifclear", 8)
1162                    && (line[8]==' '||line[8]=='\t'))
1163             {
1164               for (p=line+9; *p == ' ' || *p == '\t'; p++)
1165                 ;
1166               if (!*p)
1167                 {
1168                   err ("%s:%d: name missing after \"@ifsclear\"", fname, lnr);
1169                   continue;
1170                 }
1171               for (pend=p; *pend && *pend != ' ' && *pend != '\t'; pend++)
1172                 ;
1173               *pend = 0;  /* Ignore rest of the line.  */
1174               push_condition (p, 0, fname, lnr);
1175               continue;
1176             }
1177           else if (n == 4 && !memcmp (line, "@end", 4)
1178                    && (line[4]==' '||line[4]=='\t')
1179                    && !strncmp (p, "ifset", 5)
1180                    && (p[5]==' '||p[5]=='\t'||!p[5]))
1181             {
1182               pop_condition (1, fname, lnr);
1183               continue;
1184             }
1185           else if (n == 4 && !memcmp (line, "@end", 4)
1186                    && (line[4]==' '||line[4]=='\t')
1187                    && !strncmp (p, "ifclear", 7)
1188                    && (p[7]==' '||p[7]=='\t'||!p[7]))
1189             {
1190               pop_condition (0, fname, lnr);
1191               continue;
1192             }
1193         }
1194
1195       /* Take action on ifset/ifclear.  */
1196       if (!cond_is_active)
1197         continue;
1198
1199       /* Process commands. */
1200       if (*line == '@')
1201         {
1202           if (skip_to_end
1203               && n == 4 && !memcmp (line, "@end", 4)
1204               && (line[4]==' '||line[4]=='\t'||!line[4]))
1205             {
1206               skip_to_end = 0;
1207             }
1208           else if (cond_in_verbatim)
1209             {
1210                 got_line = 1;
1211             }
1212           else if (n == 6 && !memcmp (line, "@macro", 6))
1213             {
1214               macroname = xstrdup (p);
1215               macrovalue = xmalloc ((macrovaluesize = 1024));
1216               macrovalueused = 0;
1217             }
1218           else if (n == 8 && !memcmp (line, "@manpage", 8))
1219             {
1220               free (*section_name);
1221               *section_name = NULL;
1222               finish_page ();
1223               start_page (p);
1224               in_pause = 0;
1225             }
1226           else if (n == 8 && !memcmp (line, "@mansect", 8))
1227             {
1228               if (!thepage.name)
1229                 err ("%s:%d: section outside of a man page", fname, lnr);
1230               else
1231                 {
1232                   free (*section_name);
1233                   *section_name = ascii_strupr (xstrdup (p));
1234                   in_pause = 0;
1235                   skip_sect_line = 1;
1236                 }
1237             }
1238           else if (n == 9 && !memcmp (line, "@manpause", 9))
1239             {
1240               if (!*section_name)
1241                 err ("%s:%d: pausing outside of a man section", fname, lnr);
1242               else if (in_pause)
1243                 err ("%s:%d: already pausing", fname, lnr);
1244               else
1245                 in_pause = 1;
1246             }
1247           else if (n == 8 && !memcmp (line, "@mancont", 8))
1248             {
1249               if (!*section_name)
1250                 err ("%s:%d: continue outside of a man section", fname, lnr);
1251               else if (!in_pause)
1252                 err ("%s:%d: continue while not pausing", fname, lnr);
1253               else
1254                 in_pause = 0;
1255             }
1256           else if (n == 5 && !memcmp (line, "@menu", 5)
1257                    && (line[5]==' '||line[5]=='\t'||!line[5]))
1258             {
1259               skip_to_end = 1;
1260             }
1261           else if (n == 8 && !memcmp (line, "@include", 8)
1262                    && (line[8]==' '||line[8]=='\t'||!line[8]))
1263             {
1264               char *incname = xstrdup (p);
1265               FILE *incfp = fopen (incname, "r");
1266
1267               if (!incfp && opt_include && *opt_include && *p != '/')
1268                 {
1269                   free (incname);
1270                   incname = xmalloc (strlen (opt_include) + 1
1271                                      + strlen (p) + 1);
1272                   strcpy (incname, opt_include);
1273                   if ( incname[strlen (incname)-1] != '/' )
1274                     strcat (incname, "/");
1275                   strcat (incname, p);
1276                   incfp = fopen (incname, "r");
1277                 }
1278
1279               if (!incfp)
1280                 err ("can't open include file '%s':%s",
1281                      incname, strerror (errno));
1282               else
1283                 {
1284                   parse_file (incname, incfp, section_name, in_pause);
1285                   fclose (incfp);
1286                 }
1287               free (incname);
1288             }
1289           else if (n == 4 && !memcmp (line, "@bye", 4)
1290                    && (line[4]==' '||line[4]=='\t'||!line[4]))
1291             {
1292               break;
1293             }
1294           else if (!skip_to_end)
1295             got_line = 1;
1296         }
1297       else if (!skip_to_end)
1298         got_line = 1;
1299
1300       if (got_line && cond_in_verbatim)
1301         add_content (*section_name, line, 1);
1302       else if (got_line && thepage.name && *section_name && !in_pause)
1303         add_content (*section_name, line, 0);
1304
1305     }
1306   if (ferror (fp))
1307     err ("%s:%d: read error: %s", fname, lnr, strerror (errno));
1308   free (macroname);
1309   free (macrovalue);
1310   free (line);
1311 }
1312
1313
1314 static void
1315 top_parse_file (const char *fname, FILE *fp)
1316 {
1317   char *section_name = NULL;  /* Name of the current section or NULL
1318                                  if not in a section.  */
1319   macro_t m;
1320
1321   while (macrolist)
1322     {
1323       macro_t next = macrolist->next;
1324       free (macrolist->value);
1325       free (macrolist);
1326       macrolist = next;
1327     }
1328   for (m=predefinedmacrolist; m; m = m->next)
1329     set_macro (m->name, xstrdup ("1"));
1330   cond_is_active = 1;
1331   cond_in_verbatim = 0;
1332
1333   parse_file (fname, fp, &section_name, 0);
1334   free (section_name);
1335   finish_page ();
1336 }
1337
1338
1339 int
1340 main (int argc, char **argv)
1341 {
1342   int last_argc = -1;
1343
1344   opt_source = "GNU";
1345   opt_release = "";
1346
1347   /* Define default macros.  The trick is that these macros are not
1348      defined when using the actual texinfo renderer. */
1349   add_predefined_macro ("isman");
1350   add_predefined_macro ("manverb");
1351
1352   /* Option parsing.  */
1353   if (argc)
1354     {
1355       argc--; argv++;
1356     }
1357   while (argc && last_argc != argc )
1358     {
1359       last_argc = argc;
1360       if (!strcmp (*argv, "--"))
1361         {
1362           argc--; argv++;
1363           break;
1364         }
1365       else if (!strcmp (*argv, "--help"))
1366         {
1367           puts (
1368                 "Usage: " PGM " [OPTION] [FILE]\n"
1369                 "Extract man pages from a Texinfo source.\n\n"
1370                 "  --source NAME    use NAME as source field\n"
1371                 "  --release STRING use STRING as the release field\n"
1372                 "  --store          write output using @manpage name\n"
1373                 "  --select NAME    only output pages with @manpage NAME\n"
1374                 "  --verbose        enable extra informational output\n"
1375                 "  --debug          enable additional debug output\n"
1376                 "  --help           display this help and exit\n"
1377                 "  -I DIR           also search in include DIR\n"
1378                 "  -D gpgone        the only useable define\n\n"
1379                 "With no FILE, or when FILE is -, read standard input.\n\n"
1380                 "Report bugs to <bugs@g10code.com>.");
1381           exit (0);
1382         }
1383       else if (!strcmp (*argv, "--version"))
1384         {
1385           puts (PGM " " VERSION "\n"
1386                "Copyright (C) 2005 g10 Code GmbH\n"
1387                "This program comes with ABSOLUTELY NO WARRANTY.\n"
1388                "This is free software, and you are welcome to redistribute it\n"
1389                 "under certain conditions. See the file COPYING for details.");
1390           exit (0);
1391         }
1392       else if (!strcmp (*argv, "--verbose"))
1393         {
1394           verbose = 1;
1395           argc--; argv++;
1396         }
1397       else if (!strcmp (*argv, "--quiet"))
1398         {
1399           quiet = 1;
1400           argc--; argv++;
1401         }
1402       else if (!strcmp (*argv, "--debug"))
1403         {
1404           verbose = debug = 1;
1405           argc--; argv++;
1406         }
1407       else if (!strcmp (*argv, "--source"))
1408         {
1409           argc--; argv++;
1410           if (argc)
1411             {
1412               opt_source = *argv;
1413               argc--; argv++;
1414             }
1415         }
1416       else if (!strcmp (*argv, "--release"))
1417         {
1418           argc--; argv++;
1419           if (argc)
1420             {
1421               opt_release = *argv;
1422               argc--; argv++;
1423             }
1424         }
1425       else if (!strcmp (*argv, "--store"))
1426         {
1427           opt_store = 1;
1428           argc--; argv++;
1429         }
1430       else if (!strcmp (*argv, "--select"))
1431         {
1432           argc--; argv++;
1433           if (argc)
1434             {
1435               opt_select = strrchr (*argv, '/');
1436               if (opt_select)
1437                 opt_select++;
1438               else
1439                 opt_select = *argv;
1440               argc--; argv++;
1441             }
1442         }
1443       else if (!strcmp (*argv, "-I"))
1444         {
1445           argc--; argv++;
1446           if (argc)
1447             {
1448               opt_include = *argv;
1449               argc--; argv++;
1450             }
1451         }
1452       else if (!strcmp (*argv, "-D"))
1453         {
1454           argc--; argv++;
1455           if (argc)
1456             {
1457               add_predefined_macro (*argv);
1458               argc--; argv++;
1459             }
1460         }
1461     }
1462
1463   if (argc > 1)
1464     die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
1465
1466   /* Start processing. */
1467   if (argc && strcmp (*argv, "-"))
1468     {
1469       FILE *fp = fopen (*argv, "rb");
1470       if (!fp)
1471         die ("%s:0: can't open file: %s", *argv, strerror (errno));
1472       top_parse_file (*argv, fp);
1473       fclose (fp);
1474     }
1475   else
1476     top_parse_file ("-", stdin);
1477
1478   return !!any_error;
1479 }
1480
1481
1482 /*
1483 Local Variables:
1484 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"
1485 End:
1486 */