1 /* yat2m.c - Yet Another Texi 2 Man converter
2 * Copyright (C) 2005, 2013, 2015, 2016, 2017 g10 Code GmbH
3 * Copyright (C) 2006, 2008, 2011 Free Software Foundation, Inc.
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.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <https://www.gnu.org/licenses/>.
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.
25 To use this you need to add the following macros into your texinfo
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
45 To insert verbatim troff markup, the following texinfo code may be
52 alternatively a special comment may be used:
54 @c man:.B whatever you want
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:
61 stuff to be rendered only on man pages
64 or to exclude stuff from man pages:
67 stuff not to be rendered on man pages
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
75 If you want to indent tables in the source use this style:
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.
91 Note that @* does only work correctly if used at the end of an
108 # define MY_GCC_VERSION (__GNUC__ * 10000 \
109 + __GNUC_MINOR__ * 100 \
110 + __GNUC_PATCHLEVEL__)
112 # define MY_GCC_VERSION 0
115 #if MY_GCC_VERSION >= 20500
116 # define ATTR_PRINTF(f, a) __attribute__ ((format(printf,f,a)))
117 # define ATTR_NR_PRINTF(f, a) __attribute__ ((__noreturn__, format(printf,f,a)))
119 # define ATTR_PRINTF(f, a)
120 # define ATTR_NR_PRINTF(f, a)
122 #if MY_GCC_VERSION >= 30200
123 # define ATTR_MALLOC __attribute__ ((__malloc__))
131 #ifdef PACKAGE_VERSION
132 # define VERSION PACKAGE_VERSION
134 # define VERSION "1.0"
137 /* The maximum length of a line including the linefeed and one extra
139 #define LINESIZE 1024
141 /* Number of allowed condition nestings. */
142 #define MAX_CONDITION_NESTING 10
144 static char const default_css
[] =
145 "<style type=\"text/css\">\n"
147 " font-family: monospace;\n"
150 " text-decoration: underline;\n"
153 " font-variant: small-caps;\n"
156 " margin-top: 1em;\n"
160 " font-weight: bold;\n"
163 " font-weight: normal;\n"
174 static const char *opt_source
;
175 static const char *opt_release
;
176 static const char *opt_date
;
177 static const char *opt_select
;
178 static const char *opt_include
;
179 static int opt_store
;
181 /* Flag to keep track whether any error occurred. */
182 static int any_error
;
185 /* Object to keep macro definitions. */
188 struct macro_s
*next
;
189 char *value
; /* Malloced value. */
192 typedef struct macro_s
*macro_t
;
194 /* List of all defined macros. */
195 static macro_t macrolist
;
197 /* List of variables set by @set. */
198 static macro_t variablelist
;
200 /* List of global macro names. The value part is not used. */
201 static macro_t predefinedmacrolist
;
203 /* Object to keep track of @isset and @ifclear. */
206 int manverb
; /* "manverb" needs special treatment. */
207 int isset
; /* This is an @isset condition. */
208 char name
[1]; /* Name of the condition macro. */
210 typedef struct condition_s
*condition_t
;
212 /* The stack used to evaluate conditions. And the current states. */
213 static condition_t condition_stack
[MAX_CONDITION_NESTING
];
214 static int condition_stack_idx
;
215 static int cond_is_active
; /* State of ifset/ifclear */
216 static int cond_in_verbatim
; /* State of "manverb". */
219 /* Object to store one line of content. */
222 struct line_buffer_s
*next
;
223 int verbatim
; /* True if LINE contains verbatim data. The default
224 is Texinfo source. */
227 typedef struct line_buffer_s
*line_buffer_t
;
230 /* Object to collect the data of a section. */
231 struct section_buffer_s
233 char *name
; /* Malloced name of the section. This may be
234 NULL to indicate this slot is not used. */
235 line_buffer_t lines
; /* Linked list with the lines of the section. */
236 line_buffer_t
*lines_tail
; /* Helper for faster appending to the
238 line_buffer_t last_line
; /* Points to the last line appended. */
240 typedef struct section_buffer_s
*section_buffer_t
;
242 /* Variable to keep info about the current page together. */
245 /* Filename of the current page or NULL if no page is active. Malloced. */
248 /* Number of allocated elements in SECTIONS below. */
250 /* Array with the data of the sections. */
251 section_buffer_t sections
;
256 /* The list of standard section names. COMMANDS and ASSUAN are GnuPG
258 static const char * const standard_sections
[] =
259 { "NAME", "SYNOPSIS", "DESCRIPTION",
260 "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS",
261 "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES",
262 "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO",
263 "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL
};
266 /*-- Local prototypes. --*/
267 static void proc_texi_buffer (FILE *fp
, const char *line
, size_t len
,
268 int *table_level
, int *eol_action
);
270 static void die (const char *format
, ...) ATTR_NR_PRINTF(1,2);
271 static void err (const char *format
, ...) ATTR_PRINTF(1,2);
272 static void inf (const char *format
, ...) ATTR_PRINTF(1,2);
273 static void *xmalloc (size_t n
) ATTR_MALLOC
;
274 static void *xcalloc (size_t n
, size_t m
) ATTR_MALLOC
;
280 /* Print diagnostic message and exit with failure. */
282 die (const char *format
, ...)
287 fprintf (stderr
, "%s: ", PGM
);
289 va_start (arg_ptr
, format
);
290 vfprintf (stderr
, format
, arg_ptr
);
298 /* Print diagnostic message. */
300 err (const char *format
, ...)
305 if (strncmp (format
, "%s:%d:", 6))
306 fprintf (stderr
, "%s: ", PGM
);
308 va_start (arg_ptr
, format
);
309 vfprintf (stderr
, format
, arg_ptr
);
315 /* Print diagnostic message. */
317 inf (const char *format
, ...)
322 fprintf (stderr
, "%s: ", PGM
);
324 va_start (arg_ptr
, format
);
325 vfprintf (stderr
, format
, arg_ptr
);
334 void *p
= malloc (n
);
336 die ("out of core: %s", strerror (errno
));
341 xcalloc (size_t n
, size_t m
)
343 void *p
= calloc (n
, m
);
345 die ("out of core: %s", strerror (errno
));
350 xrealloc (void *old
, size_t n
)
352 void *p
= realloc (old
, n
);
354 die ("out of core: %s", strerror (errno
));
359 xstrdup (const char *string
)
361 void *p
= malloc (strlen (string
)+1);
363 die ("out of core: %s", strerror (errno
));
369 /* Uppercase the ascii characters in STRING. */
371 ascii_strupr (char *string
)
375 for (p
= string
; *p
; p
++)
382 /* Return the current date as an ISO string. */
386 static char buffer
[36];
390 if (opt_date
&& *opt_date
)
391 atime
= strtoul (opt_date
, NULL
, 10);
395 strcpy (buffer
, "????" "-??" "-??");
398 tp
= gmtime (&atime
);
399 sprintf (buffer
,"%04d-%02d-%02d",
400 1900+tp
->tm_year
, tp
->tm_mon
+1, tp
->tm_mday
);
406 /* Add NAME to the list of predefined macros which are global for all
409 add_predefined_macro (const char *name
)
413 for (m
=predefinedmacrolist
; m
; m
= m
->next
)
414 if (!strcmp (m
->name
, name
))
418 m
= xcalloc (1, sizeof *m
+ strlen (name
));
419 strcpy (m
->name
, name
);
420 m
->next
= predefinedmacrolist
;
421 predefinedmacrolist
= m
;
426 /* Create or update a macro with name MACRONAME and set its values TO
427 MACROVALUE. Note that ownership of the macro value is transferred
430 set_macro (const char *macroname
, char *macrovalue
)
434 for (m
=macrolist
; m
; m
= m
->next
)
435 if (!strcmp (m
->name
, macroname
))
441 m
= xcalloc (1, sizeof *m
+ strlen (macroname
));
442 strcpy (m
->name
, macroname
);
446 m
->value
= macrovalue
;
451 /* Create or update a variable with name and value given in NAMEANDVALUE. */
453 set_variable (char *nameandvalue
)
459 for (p
= nameandvalue
; *p
&& *p
!= ' ' && *p
!= '\t'; p
++)
466 while (*p
== ' ' || *p
== '\t')
471 for (m
=variablelist
; m
; m
= m
->next
)
472 if (!strcmp (m
->name
, nameandvalue
))
478 m
= xcalloc (1, sizeof *m
+ strlen (nameandvalue
));
479 strcpy (m
->name
, nameandvalue
);
480 m
->next
= variablelist
;
483 m
->value
= xstrdup (value
);
487 /* Return true if the macro or variable NAME is set, i.e. not the
488 empty string and not evaluating to 0. */
490 macro_set_p (const char *name
)
494 for (m
= macrolist
; m
; m
= m
->next
)
495 if (!strcmp (m
->name
, name
))
498 for (m
= variablelist
; m
; m
= m
->next
)
499 if (!strcmp (m
->name
, name
))
501 if (!m
|| !m
->value
|| !*m
->value
)
503 if ((*m
->value
& 0x80) || !isdigit (*m
->value
))
504 return 1; /* Not a digit but some other string. */
505 return !!atoi (m
->value
);
509 /* Evaluate the current conditions. */
511 evaluate_conditions (const char *fname
, int lnr
)
518 /* for (i=0; i < condition_stack_idx; i++) */
519 /* inf ("%s:%d: stack[%d] %s %s %c", */
520 /* fname, lnr, i, condition_stack[i]->isset? "set":"clr", */
521 /* condition_stack[i]->name, */
522 /* (macro_set_p (condition_stack[i]->name) */
523 /* ^ !condition_stack[i]->isset)? 't':'f'); */
526 cond_in_verbatim
= 0;
527 if (condition_stack_idx
)
529 for (i
=0; i
< condition_stack_idx
; i
++)
531 if (condition_stack
[i
]->manverb
)
532 cond_in_verbatim
= (macro_set_p (condition_stack
[i
]->name
)
533 ^ !condition_stack
[i
]->isset
);
534 else if (!(macro_set_p (condition_stack
[i
]->name
)
535 ^ !condition_stack
[i
]->isset
))
543 /* inf ("%s:%d: active=%d verbatim=%d", */
544 /* fname, lnr, cond_is_active, cond_in_verbatim); */
548 /* Push a condition with condition macro NAME onto the stack. If
549 ISSET is true, a @isset condition is pushed. */
551 push_condition (const char *name
, int isset
, const char *fname
, int lnr
)
556 if (condition_stack_idx
>= MAX_CONDITION_NESTING
)
558 err ("%s:%d: condition nested too deep", fname
, lnr
);
562 if (!strcmp (name
, "manverb"))
566 err ("%s:%d: using \"@ifclear manverb\" is not allowed", fname
, lnr
);
572 cond
= xcalloc (1, sizeof *cond
+ strlen (name
));
573 cond
->manverb
= manverb
;
575 strcpy (cond
->name
, name
);
577 condition_stack
[condition_stack_idx
++] = cond
;
578 evaluate_conditions (fname
, lnr
);
582 /* Remove the last condition from the stack. ISSET is used for error
585 pop_condition (int isset
, const char *fname
, int lnr
)
587 if (!condition_stack_idx
)
589 err ("%s:%d: unbalanced \"@end %s\"",
590 fname
, lnr
, isset
?"isset":"isclear");
593 condition_stack_idx
--;
594 free (condition_stack
[condition_stack_idx
]);
595 condition_stack
[condition_stack_idx
] = NULL
;
596 evaluate_conditions (fname
, lnr
);
601 /* Return a section buffer for the section NAME. Allocate a new buffer
602 if this is a new section. Keep track of the sections in THEPAGE.
603 This function may reallocate the section array in THEPAGE. */
604 static section_buffer_t
605 get_section_buffer (const char *name
)
608 section_buffer_t sect
;
610 /* If there is no section we put everything into the required NAME
611 section. Given that this is the first one listed it is likely
612 that error are easily visible. */
616 for (i
=0; i
< thepage
.n_sections
; i
++)
618 sect
= thepage
.sections
+ i
;
619 if (sect
->name
&& !strcmp (name
, sect
->name
))
622 for (i
=0; i
< thepage
.n_sections
; i
++)
623 if (!thepage
.sections
[i
].name
)
625 if (thepage
.n_sections
&& i
< thepage
.n_sections
)
626 sect
= thepage
.sections
+ i
;
629 /* We need to allocate or reallocate the section array. */
630 size_t old_n
= thepage
.n_sections
;
634 thepage
.sections
= xcalloc (new_n
, sizeof *thepage
.sections
);
637 thepage
.sections
= xrealloc (thepage
.sections
,
639 * sizeof *thepage
.sections
));
640 memset (thepage
.sections
+ old_n
, 0,
641 new_n
* sizeof *thepage
.sections
);
643 thepage
.n_sections
+= new_n
;
645 /* Setup the tail pointers. */
646 for (i
=old_n
; i
< thepage
.n_sections
; i
++)
648 sect
= thepage
.sections
+ i
;
649 sect
->lines_tail
= §
->lines
;
651 sect
= thepage
.sections
+ old_n
;
654 /* Store the name. */
655 assert (!sect
->name
);
656 sect
->name
= xstrdup (name
);
662 /* Add the content of LINE to the section named SECTNAME. */
664 add_content (const char *sectname
, char *line
, int verbatim
)
666 section_buffer_t sect
;
669 sect
= get_section_buffer (sectname
);
670 if (sect
->last_line
&& !sect
->last_line
->verbatim
== !verbatim
)
672 /* Lets append that line to the last one. We do this to keep
673 all lines of the same kind (i.e.verbatim or not) together in
677 lb
= sect
->last_line
;
678 n1
= strlen (lb
->line
);
679 n
= n1
+ 1 + strlen (line
) + 1;
680 lb
->line
= xrealloc (lb
->line
, n
);
681 strcpy (lb
->line
+n1
, "\n");
682 strcpy (lb
->line
+n1
+1, line
);
686 lb
= xcalloc (1, sizeof *lb
);
687 lb
->verbatim
= verbatim
;
688 lb
->line
= xstrdup (line
);
689 sect
->last_line
= lb
;
690 *sect
->lines_tail
= lb
;
691 sect
->lines_tail
= &lb
->next
;
696 /* Prepare for a new man page using the filename NAME. */
698 start_page (char *name
)
701 inf ("starting page '%s'", name
);
702 assert (!thepage
.name
);
703 thepage
.name
= xstrdup (name
);
704 thepage
.n_sections
= 0;
708 /* Write a character to FP. */
710 writechr (int c
, FILE *fp
)
716 /* Write depending on HTMLMODE either ROFF or HTML to FP. */
718 writestr (const char *roff
, const char *html
, FILE *fp
)
720 const char *s
= htmlmode
? html
: roff
;
727 /* Write the .TH entry of the current page. Return -1 if there is a
728 problem with the page. */
734 writestr (".\\\" Created from Texinfo source by yat2m " VERSION
"\n",
735 "<!-- Created from Texinfo source by yat2m " VERSION
" -->\n",
738 name
= ascii_strupr (xstrdup (thepage
.name
));
739 p
= strrchr (name
, '.');
742 err ("no section name in man page '%s'", thepage
.name
);
752 fprintf (fp
, " <title>%s(%s)</title>\n", name
, p
);
753 fputs (default_css
, fp
);
756 fputs ("<div class=\"y2m\">\n", fp
);
760 * .TH GPG 1 2016-12-20 "GnuPG 2.1.17" "GNU Privacy Guard 2.1"
761 * is rendered by man like this:
762 * GPG(1) GNU Privacy Guard 2.1 GPG(1)
764 * GnuPG 2.1.17 2016-12-20 GPG(1)
768 fprintf (fp
, "<p class=\"y2m y2m-top\">"
769 "<span class=\"y2m-left\">%s(%s)</span> "
770 "<span class=\"y2m-center\">%s</span> "
771 "<span class=\"y2m-right\">%s(%s)</span>"
773 name
, p
, opt_source
, name
, p
);
774 /* Note that the HTML footer is written by write_bottom(). */
778 fprintf (fp
, ".TH %s %s %s \"%s\" \"%s\"\n",
779 name
, p
, isodatestring (), opt_release
, opt_source
);
786 /* In HTML mode we need to render a footer. */
788 write_bottom (FILE *fp
)
795 name
= ascii_strupr (xstrdup (thepage
.name
));
796 p
= strrchr (name
, '.');
799 err ("no section name in man page '%s'", thepage
.name
);
806 * .TH GPG 1 2016-12-20 "GnuPG 2.1.17" "GNU Privacy Guard 2.1"
807 * is rendered by man to this footer:
808 * GnuPG 2.1.17 2016-12-20 GPG(1)
810 fprintf (fp
, "<p class=\"y2m y2m-footer\">"
811 "<span class=\"y2m-left\">%s</span> "
812 "<span class=\"y2m-center\">%s</span> "
813 "<span class=\"y2m-right\">%s(%s)</span>"
815 opt_release
, isodatestring (), name
, p
);
816 fputs ("</div><!-- class y2m -->\n", fp
);
825 /* Write the .SH header. With NULL passed for NAME just close a
826 * section in html mode if there is an open section. */
828 write_sh (FILE *fp
, const char *name
)
830 static int in_section
;
832 if (htmlmode
&& in_section
)
833 fprintf (fp
, "</div>\n");
840 "<div class=\"y2m-section\">\n"
841 "<p class=\"y2m-sh\">%s</p>\n", name
);
843 fprintf (fp
, ".SH %s\n", name
);
848 /* Render a @item line to HTML. (LINE,LEN) gives the arguments of
849 * @item. Use NULL for LINE to close a possible open <li>. ITEMX
850 * flags a @itemx line. */
852 write_html_item (FILE *fp
, const char *line
, size_t len
, int itemx
)
862 fprintf (fp
, "</li>\n");
868 /* Trim the LF and skip leading spaces. */
869 if (len
&& line
[len
-1] == '\n')
871 for (; len
&& (*line
== ' ' || *line
== '\t'); len
--, line
++)
876 for (n
=0; n
< len
&& !(*rest
== ' ' || *rest
== '\t'); n
++, rest
++)
879 for (; n
< len
&& (*rest
== ' ' || *rest
== '\t'); n
++, rest
++)
882 /* Now the first word is (LINE,N0) and the args are (REST,LEN) */
883 fprintf (fp
, "%s<span class=\"y2m-item\">%.*s",
884 itemx
? " ":"<li>", (int)n0
, line
);
887 fputs (" <span class=\"y2m-args\">", fp
);
888 proc_texi_buffer (fp
, rest
, len
, &table_level
, &eol_action
);
889 fputs ("</span>", fp
);
891 fputs ("</span>\n", fp
);
898 /* Process the texinfo command COMMAND (without the leading @) and
899 write output if needed to FP. REST is the remainder of the line
900 which should either point to an opening brace or to a white space.
901 The function returns the number of characters already processed
902 from REST. LEN is the usable length of REST. TABLE_LEVEL is used to
903 control the indentation of tables. */
905 proc_texi_cmd (FILE *fp
, const char *command
, const char *rest
, size_t len
,
906 int *table_level
, int *eol_action
)
909 const char *name
; /* Name of the command. */
910 int what
; /* What to do with this command. */
911 const char *lead_in
; /* String to print with a opening brace. */
912 const char *lead_out
;/* String to print with the closing brace. */
913 const char *html_in
; /* Same as LEAD_IN but for HTML. */
914 const char *html_out
;/* Same as LEAD_OUT but for HTML. */
916 { "command", 0, "\\fB", "\\fR", "<i>", "</i>" },
917 { "code", 0, "\\fB", "\\fR", "<samp>", "</samp>" },
918 { "url", 0, "\\fB", "\\fR", "<strong>", "</strong>" },
919 { "sc", 0, "\\fB", "\\fR", "<span class=\"y2m-sc\">", "</span>" },
920 { "var", 0, "\\fI", "\\fR", "<u>", "</u>" },
921 { "samp", 0, "\\(oq", "\\(cq" },
922 { "kbd", 0, "\\(oq", "\\(cq" },
923 { "file", 0, "\\(oq\\fI","\\fR\\(cq" },
924 { "env", 0, "\\(oq\\fI","\\fR\\(cq" },
927 { "option", 0, "\\fB", "\\fR", "<samp>", "</samp>" },
928 { "example", 1, ".RS 2\n.nf\n", NULL
, "\n<pre>\n", "\n</pre>\n" },
929 { "smallexample", 1, ".RS 2\n.nf\n", NULL
, "\n<pre>\n", "\n</pre>\n" },
933 { "ref", 0, "[", "]" },
934 { "xref", 0, "See: [", "]" },
935 { "pxref", 0, "see: [", "]" },
936 { "uref", 0, "(\\fB", "\\fR)" },
937 { "footnote",0, " ([", "])" },
938 { "emph", 0, "\\fI", "\\fR", "<em>", "</em>" },
948 { "subsection", 6, "\n.SS " },
950 { "item", 2, ".TP\n.B " },
951 { "itemx", 2, ".TQ\n.B " },
952 { "table", 3, NULL
, NULL
, "<ul>\n", "</ul>\n" },
954 { "bullet", 0, "* " },
958 { "quotation",1, ".RS\n\\fB" },
965 const char *lead_out
= NULL
;
966 const char *html_out
= NULL
;
969 for (i
=0; cmdtbl
[i
].name
&& strcmp (cmdtbl
[i
].name
, command
); i
++)
973 writestr (cmdtbl
[i
].lead_in
, cmdtbl
[i
].html_in
, fp
);
974 lead_out
= cmdtbl
[i
].lead_out
;
975 html_out
= cmdtbl
[i
].html_out
;
976 switch (cmdtbl
[i
].what
)
978 case 1: /* Throw away the entire line. */
979 s
= memchr (rest
, '\n', len
);
980 return s
? (s
-rest
)+1 : len
;
981 case 2: /* Handle @item. */
984 s
= memchr (rest
, '\n', len
);
985 n
= s
? (s
-rest
)+1 : len
;
986 write_html_item (fp
, rest
, n
, !strcmp(cmdtbl
[i
].name
, "itemx"));
990 case 3: /* Handle table. */
991 if (++(*table_level
) > 1)
993 write_html_item (fp
, NULL
, 0, 0);
994 writestr (".RS\n", "<ul>\n", fp
);
996 /* Now throw away the entire line. */
997 s
= memchr (rest
, '\n', len
);
998 return s
? (s
-rest
)+1 : len
;
1000 case 4: /* Handle end. */
1001 for (s
=rest
, n
=len
; n
&& (*s
== ' ' || *s
== '\t'); s
++, n
--)
1003 if (n
>= 5 && !memcmp (s
, "table", 5)
1004 && (!n
|| s
[5] == ' ' || s
[5] == '\t' || s
[5] == '\n'))
1007 write_html_item (fp
, NULL
, 0, 0);
1008 if ((*table_level
)-- > 1)
1009 writestr (".RE\n", "</ul>\n", fp
);
1011 writestr (".P\n", "</ul>\n", fp
);
1013 else if (n
>= 7 && !memcmp (s
, "example", 7)
1014 && (!n
|| s
[7] == ' ' || s
[7] == '\t' || s
[7] == '\n'))
1016 writestr (".fi\n.RE\n", "</pre>\n", fp
);
1018 else if (n
>= 12 && !memcmp (s
, "smallexample", 12)
1019 && (!n
|| s
[12] == ' ' || s
[12] == '\t' || s
[12] == '\n'))
1021 writestr (".fi\n.RE\n", "</pre>\n", fp
);
1023 else if (n
>= 9 && !memcmp (s
, "quotation", 9)
1024 && (!n
|| s
[9] == ' ' || s
[9] == '\t' || s
[9] == '\n'))
1026 writestr ("\\fR\n.RE\n", "xx", fp
);
1028 /* Now throw away the entire line. */
1029 s
= memchr (rest
, '\n', len
);
1030 return s
? (s
-rest
)+1 : len
;
1031 case 5: /* Handle special comments. */
1032 for (s
=rest
, n
=len
; n
&& (*s
== ' ' || *s
== '\t'); s
++, n
--)
1034 if (n
>= 4 && !memcmp (s
, "man:", 4))
1040 if (!strncmp (s
, ".RE\n", 4)
1041 || !strncmp (s
, ".RS\n", 4))
1044 inf ("unknown special comment \"man:\"");
1048 for (; n
&& *s
!= '\n'; n
--, s
++)
1050 writechr ('\n', fp
);
1053 /* Now throw away the entire line. */
1054 s
= memchr (rest
, '\n', len
);
1055 return s
? (s
-rest
)+1 : len
;
1066 err ("opening brace for command '%s' missing", command
);
1071 /* Find closing brace. */
1072 for (s
=rest
+1, n
=1; *s
&& n
< len
; s
++, n
++)
1077 err ("closing brace for command '%s' not found", command
);
1082 size_t rlen
= s
- (rest
+ 1);
1085 for (m
= variablelist
; m
; m
= m
->next
)
1087 if (strlen (m
->name
) == rlen
1088 && !strncmp (m
->name
, rest
+1, rlen
))
1092 writestr (m
->value
, m
->value
, fp
);
1094 inf ("texinfo variable '%.*s' is not set",
1107 for (m
= macrolist
; m
; m
= m
->next
)
1108 if (!strcmp (m
->name
, command
))
1112 proc_texi_buffer (fp
, m
->value
, strlen (m
->value
),
1113 table_level
, eol_action
);
1114 ignore_args
= 1; /* Parameterized macros are not yet supported. */
1117 inf ("texinfo command '%s' not supported (%.*s)", command
,
1118 (int)((s
= memchr (rest
, '\n', len
)), (s
? (s
-rest
) : len
)), rest
);
1123 /* Find matching closing brace. */
1124 for (s
=rest
+1, n
=1, i
=1; i
&& *s
&& n
< len
; s
++, n
++)
1131 err ("closing brace for command '%s' not found", command
);
1134 if (n
> 2 && !ignore_args
)
1135 proc_texi_buffer (fp
, rest
+1, n
-2, table_level
, eol_action
);
1140 writestr (lead_out
, html_out
, fp
);
1147 /* Process the string LINE with LEN bytes of Texinfo content. */
1149 proc_texi_buffer (FILE *fp
, const char *line
, size_t len
,
1150 int *table_level
, int *eol_action
)
1158 for (s
=line
; *s
&& len
; s
++, len
--)
1166 case '@': case '{': case '}':
1167 writechr (*s
, fp
); in_cmd
= 0;
1169 case ':': /* Not ending a sentence flag. */
1172 case '.': case '!': case '?': /* Ending a sentence. */
1173 writechr (*s
, fp
); in_cmd
= 0;
1175 case ' ': case '\t': case '\n': /* Non collapsing spaces. */
1176 writechr (*s
, fp
); in_cmd
= 0;
1180 cmdbuf
[cmdidx
++] = *s
;
1185 else if (*s
== '{' || *s
== ' ' || *s
== '\t' || *s
== '\n')
1188 n
= proc_texi_cmd (fp
, cmdbuf
, s
, len
, table_level
, eol_action
);
1194 else if (cmdidx
< sizeof cmdbuf
-1)
1195 cmdbuf
[cmdidx
++] = *s
;
1198 err ("texinfo command too long - ignored");
1204 else if (*s
== '\n')
1206 switch (*eol_action
)
1208 case 1: /* Create a dummy paragraph. */
1209 writestr ("\n\\ \n", "\n<-- dummy par -->\n", fp
);
1216 else if (*s
== '\\')
1217 writestr ("\\\\", "\\\\", fp
);
1225 n
= proc_texi_cmd (fp
, cmdbuf
, s
, len
, table_level
, eol_action
);
1229 /* in_cmd = 0; -- doc only */
1234 /* Do something with the Texinfo line LINE. */
1236 parse_texi_line (FILE *fp
, const char *line
, int *table_level
)
1240 /* A quick test whether there are any texinfo commands. */
1241 if (!strchr (line
, '@'))
1243 /* FIXME: In html mode escape HTML stuff. */
1244 writestr (line
, line
, fp
);
1245 writechr ('\n', fp
);
1248 proc_texi_buffer (fp
, line
, strlen (line
), table_level
, &eol_action
);
1249 writechr ('\n', fp
);
1253 /* Write all the lines LINES to FP. */
1255 write_content (FILE *fp
, line_buffer_t lines
)
1258 int table_level
= 0;
1260 for (line
= lines
; line
; line
= line
->next
)
1264 /* FIXME: IN HTML mode we need to employ a parser for roff
1266 writestr (line
->line
, line
->line
, fp
);
1267 writechr ('\n', fp
);
1271 /* fputs ("TEXI---", fp); */
1272 /* fputs (line->line, fp); */
1273 /* fputs ("---\n", fp); */
1274 parse_texi_line (fp
, line
->line
, &table_level
);
1282 is_standard_section (const char *name
)
1287 for (i
=0; (s
=standard_sections
[i
]); i
++)
1288 if (!strcmp (s
, name
))
1294 /* Finish a page; that is sort the data and write it out to the file. */
1299 section_buffer_t sect
= NULL
;
1305 return; /* No page active. */
1308 inf ("finishing page '%s'", thepage
.name
);
1312 if (!strcmp (opt_select
, thepage
.name
))
1314 inf ("selected '%s'", thepage
.name
);
1319 fp
= fopen ( "/dev/null", "w" );
1321 die ("failed to open /dev/null: %s\n", strerror (errno
));
1327 inf ("writing '%s'", thepage
.name
);
1328 fp
= fopen ( thepage
.name
, "w" );
1330 die ("failed to create '%s': %s\n", thepage
.name
, strerror (errno
));
1338 for (idx
=0; (s
=standard_sections
[idx
]); idx
++)
1340 for (i
=0; i
< thepage
.n_sections
; i
++)
1342 sect
= thepage
.sections
+ i
;
1343 if (sect
->name
&& !strcmp (s
, sect
->name
))
1346 if (i
== thepage
.n_sections
)
1351 write_sh (fp
, sect
->name
);
1352 write_content (fp
, sect
->lines
);
1353 /* Now continue with all non standard sections directly
1354 following this one. */
1355 for (i
++; i
< thepage
.n_sections
; i
++)
1357 sect
= thepage
.sections
+ i
;
1358 if (sect
->name
&& is_standard_section (sect
->name
))
1362 write_sh (fp
, sect
->name
);
1363 write_content (fp
, sect
->lines
);
1370 write_sh (fp
, NULL
);
1371 if (write_bottom (fp
))
1377 free (thepage
.name
);
1378 thepage
.name
= NULL
;
1379 /* FIXME: Cleanup the content. */
1385 /* Parse one Texinfo file and create manpages according to the
1386 embedded instructions. */
1388 parse_file (const char *fname
, FILE *fp
, char **section_name
, int in_pause
)
1392 /* Fixme: The following state variables don't carry over to include
1394 int skip_to_end
= 0; /* Used to skip over menu entries. */
1395 int skip_sect_line
= 0; /* Skip after @mansect. */
1396 int item_indent
= 0; /* How far is the current @item indented. */
1398 /* Helper to define a macro. */
1399 char *macroname
= NULL
;
1400 char *macrovalue
= NULL
;
1401 size_t macrovaluesize
= 0;
1402 size_t macrovalueused
= 0;
1404 line
= xmalloc (LINESIZE
);
1405 while (fgets (line
, LINESIZE
, fp
))
1407 size_t n
= strlen (line
);
1412 if (!n
|| line
[n
-1] != '\n')
1414 err ("%s:%d: trailing linefeed missing, line too long or "
1415 "embedded Nul character", fname
, lnr
);
1420 /* Kludge to allow indentation of tables. */
1421 for (p
=line
; *p
== ' ' || *p
== '\t'; p
++)
1425 if (*p
== '@' && !strncmp (p
+1, "item", 4))
1426 item_indent
= p
- line
; /* Set a new indent level. */
1427 else if (p
- line
< item_indent
)
1428 item_indent
= 0; /* Switch off indention. */
1432 memmove (line
, line
+item_indent
, n
- item_indent
+ 1);
1440 for (p
=line
+1, n
=1; *p
&& *p
!= ' ' && *p
!= '\t'; p
++)
1442 while (*p
== ' ' || *p
== '\t')
1448 /* Take action on macro. */
1451 if (n
== 4 && !memcmp (line
, "@end", 4)
1452 && (line
[4]==' '||line
[4]=='\t'||!line
[4])
1453 && !strncmp (p
, "macro", 5)
1454 && (p
[5]==' '||p
[5]=='\t'||!p
[5]))
1457 macrovalue
[--macrovalueused
] = 0; /* Kill the last LF. */
1458 macrovalue
[macrovalueused
] = 0; /* Terminate macro. */
1459 macrovalue
= xrealloc (macrovalue
, macrovalueused
+1);
1461 set_macro (macroname
, macrovalue
);
1468 if (macrovalueused
+ strlen (line
) + 2 >= macrovaluesize
)
1470 macrovaluesize
+= strlen (line
) + 256;
1471 macrovalue
= xrealloc (macrovalue
, macrovaluesize
);
1473 strcpy (macrovalue
+macrovalueused
, line
);
1474 macrovalueused
+= strlen (line
);
1475 macrovalue
[macrovalueused
++] = '\n';
1481 if (n
>= 5 && !memcmp (line
, "@node", 5)
1482 && (line
[5]==' '||line
[5]=='\t'||!line
[5]))
1484 /* Completey ignore @node lines. */
1492 if (!strncmp (line
, "@section", 8)
1493 || !strncmp (line
, "@subsection", 11)
1494 || !strncmp (line
, "@chapheading", 12))
1498 /* We only parse lines we need and ignore the rest. There are a
1499 few macros used to control this as well as one @ifset
1500 command. Parts we know about are saved away into containers
1501 separate for each section. */
1503 /* First process ifset/ifclear commands. */
1506 if (n
== 6 && !memcmp (line
, "@ifset", 6)
1507 && (line
[6]==' '||line
[6]=='\t'))
1509 for (p
=line
+7; *p
== ' ' || *p
== '\t'; p
++)
1513 err ("%s:%d: name missing after \"@ifset\"", fname
, lnr
);
1516 for (pend
=p
; *pend
&& *pend
!= ' ' && *pend
!= '\t'; pend
++)
1518 *pend
= 0; /* Ignore rest of the line. */
1519 push_condition (p
, 1, fname
, lnr
);
1522 else if (n
== 8 && !memcmp (line
, "@ifclear", 8)
1523 && (line
[8]==' '||line
[8]=='\t'))
1525 for (p
=line
+9; *p
== ' ' || *p
== '\t'; p
++)
1529 err ("%s:%d: name missing after \"@ifsclear\"", fname
, lnr
);
1532 for (pend
=p
; *pend
&& *pend
!= ' ' && *pend
!= '\t'; pend
++)
1534 *pend
= 0; /* Ignore rest of the line. */
1535 push_condition (p
, 0, fname
, lnr
);
1538 else if (n
== 4 && !memcmp (line
, "@end", 4)
1539 && (line
[4]==' '||line
[4]=='\t')
1540 && !strncmp (p
, "ifset", 5)
1541 && (p
[5]==' '||p
[5]=='\t'||!p
[5]))
1543 pop_condition (1, fname
, lnr
);
1546 else if (n
== 4 && !memcmp (line
, "@end", 4)
1547 && (line
[4]==' '||line
[4]=='\t')
1548 && !strncmp (p
, "ifclear", 7)
1549 && (p
[7]==' '||p
[7]=='\t'||!p
[7]))
1551 pop_condition (0, fname
, lnr
);
1556 /* Take action on ifset/ifclear. */
1557 if (!cond_is_active
)
1560 /* Process commands. */
1564 && n
== 4 && !memcmp (line
, "@end", 4)
1565 && (line
[4]==' '||line
[4]=='\t'||!line
[4]))
1569 else if (cond_in_verbatim
)
1573 else if (n
== 6 && !memcmp (line
, "@macro", 6))
1575 macroname
= xstrdup (p
);
1576 macrovalue
= xmalloc ((macrovaluesize
= 1024));
1579 else if (n
== 4 && !memcmp (line
, "@set", 4))
1583 else if (n
== 8 && !memcmp (line
, "@manpage", 8))
1585 free (*section_name
);
1586 *section_name
= NULL
;
1591 else if (n
== 8 && !memcmp (line
, "@mansect", 8))
1594 err ("%s:%d: section outside of a man page", fname
, lnr
);
1597 free (*section_name
);
1598 *section_name
= ascii_strupr (xstrdup (p
));
1603 else if (n
== 9 && !memcmp (line
, "@manpause", 9))
1606 err ("%s:%d: pausing outside of a man section", fname
, lnr
);
1608 err ("%s:%d: already pausing", fname
, lnr
);
1612 else if (n
== 8 && !memcmp (line
, "@mancont", 8))
1615 err ("%s:%d: continue outside of a man section", fname
, lnr
);
1617 err ("%s:%d: continue while not pausing", fname
, lnr
);
1621 else if (n
== 5 && !memcmp (line
, "@menu", 5)
1622 && (line
[5]==' '||line
[5]=='\t'||!line
[5]))
1626 else if (n
== 8 && !memcmp (line
, "@include", 8)
1627 && (line
[8]==' '||line
[8]=='\t'||!line
[8]))
1629 char *incname
= xstrdup (p
);
1630 FILE *incfp
= fopen (incname
, "r");
1632 if (!incfp
&& opt_include
&& *opt_include
&& *p
!= '/')
1635 incname
= xmalloc (strlen (opt_include
) + 1
1637 strcpy (incname
, opt_include
);
1638 if ( incname
[strlen (incname
)-1] != '/' )
1639 strcat (incname
, "/");
1640 strcat (incname
, p
);
1641 incfp
= fopen (incname
, "r");
1645 err ("can't open include file '%s': %s",
1646 incname
, strerror (errno
));
1649 parse_file (incname
, incfp
, section_name
, in_pause
);
1654 else if (n
== 4 && !memcmp (line
, "@bye", 4)
1655 && (line
[4]==' '||line
[4]=='\t'||!line
[4]))
1659 else if (!skip_to_end
)
1662 else if (!skip_to_end
)
1665 if (got_line
&& cond_in_verbatim
)
1666 add_content (*section_name
, line
, 1);
1667 else if (got_line
&& thepage
.name
&& *section_name
&& !in_pause
)
1668 add_content (*section_name
, line
, 0);
1672 err ("%s:%d: read error: %s", fname
, lnr
, strerror (errno
));
1680 top_parse_file (const char *fname
, FILE *fp
)
1682 char *section_name
= NULL
; /* Name of the current section or NULL
1683 if not in a section. */
1688 macro_t next
= macrolist
->next
;
1689 free (macrolist
->value
);
1693 while (variablelist
)
1695 macro_t next
= variablelist
->next
;
1696 free (variablelist
->value
);
1697 free (variablelist
);
1698 variablelist
= next
;
1700 for (m
=predefinedmacrolist
; m
; m
= m
->next
)
1701 set_macro (m
->name
, xstrdup ("1"));
1703 cond_in_verbatim
= 0;
1705 parse_file (fname
, fp
, §ion_name
, 0);
1706 free (section_name
);
1712 main (int argc
, char **argv
)
1720 /* Define default macros. The trick is that these macros are not
1721 defined when using the actual texinfo renderer. */
1722 add_predefined_macro ("isman");
1723 add_predefined_macro ("manverb");
1725 /* Option parsing. */
1730 while (argc
&& last_argc
!= argc
)
1733 if (!strcmp (*argv
, "--"))
1738 else if (!strcmp (*argv
, "--help"))
1741 "Usage: " PGM
" [OPTION] [FILE]\n"
1742 "Extract man pages from a Texinfo source.\n\n"
1743 " --html render output as HTML\n"
1744 " --source NAME use NAME as source field\n"
1745 " --release STRING use STRING as the release field\n"
1746 " --date EPOCH use EPOCH as publication date\n"
1747 " --store write output using @manpage name\n"
1748 " --select NAME only output pages with @manpage NAME\n"
1749 " --verbose enable extra informational output\n"
1750 " --debug enable additional debug output\n"
1751 " --help display this help and exit\n"
1752 " -I DIR also search in include DIR\n"
1753 " -D MACRO define MACRO to 1\n\n"
1754 "With no FILE, or when FILE is -, read standard input.\n\n"
1755 "Report bugs to <https://bugs.gnupg.org>.");
1758 else if (!strcmp (*argv
, "--version"))
1760 puts (PGM
" " VERSION
"\n"
1761 "Copyright (C) 2005, 2017 g10 Code GmbH\n"
1762 "This program comes with ABSOLUTELY NO WARRANTY.\n"
1763 "This is free software, and you are welcome to redistribute it\n"
1764 "under certain conditions. See the file COPYING for details.");
1767 else if (!strcmp (*argv
, "--html"))
1772 else if (!strcmp (*argv
, "--verbose"))
1777 else if (!strcmp (*argv
, "--quiet"))
1782 else if (!strcmp (*argv
, "--debug"))
1784 verbose
= debug
= 1;
1787 else if (!strcmp (*argv
, "--source"))
1796 else if (!strcmp (*argv
, "--release"))
1801 opt_release
= *argv
;
1805 else if (!strcmp (*argv
, "--date"))
1814 else if (!strcmp (*argv
, "--store"))
1819 else if (!strcmp (*argv
, "--select"))
1824 opt_select
= strrchr (*argv
, '/');
1832 else if (!strcmp (*argv
, "-I"))
1837 opt_include
= *argv
;
1841 else if (!strcmp (*argv
, "-D"))
1846 add_predefined_macro (*argv
);
1853 die ("usage: " PGM
" [OPTION] [FILE] (try --help for more information)\n");
1855 /* Take care of supplied timestamp for reproducible builds. See
1856 * https://reproducible-builds.org/specs/source-date-epoch/ */
1857 if (!opt_date
&& (s
= getenv ("SOURCE_DATE_EPOCH")) && *s
)
1860 /* Start processing. */
1861 if (argc
&& strcmp (*argv
, "-"))
1863 FILE *fp
= fopen (*argv
, "rb");
1865 die ("%s:0: can't open file: %s", *argv
, strerror (errno
));
1866 top_parse_file (*argv
, fp
);
1870 top_parse_file ("-", stdin
);
1878 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"