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