1 /* yat2m.c - Yet Another Texi 2 Man converter
2 * Copyright (C) 2005 g10 Code GmbH
3 * Copyright (C) 2006 2006 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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 This is a simple textinfo to man page converter. It needs some
23 special markup in th e texinfo and tries best to get a create man
24 page. It has been designed for the GnuPG man pages and thus only
25 a few texinfo commands are supported.
27 To use this you need to add the following macros into your texinfo
39 They are used by yat2m to select parts of the Texinfo which should
40 go into the man page. These macros need to be used without leading
41 left space. Processing starts after a "manpage" macro has been
42 seen. "mansect" identifies the section and yat2m make sure to
43 emit the sections in the proper order. Note that @mansect skips
44 the next input line if that line begins with @section, @subsection or
47 To insert verbatim troff markup, the follwing texinfo code may be
54 alternativly a special comment may be used:
56 @c man:.B whatever you want
58 This is useful in case you need just one line. If you want to
59 include parts only in the man page but keep the texinfo
60 translation you may use:
63 stuff to be rendered only on man pages
66 or to exclude stuff from man pages:
69 stuff not to be rendered on man pages
72 the keyword @section is ignored, however @subsection gets rendered
73 as ".SS". @menu is completely skipped. Several man pages may be
74 extracted from one file, either using the --store or the --select
94 /* The maximum length of a line including the linefeed and one extra
102 static const char *opt_source
;
103 static const char *opt_release
;
104 static const char *opt_select
;
105 static const char *opt_include
;
106 static int opt_store
;
109 /* Flag to keep track whether any error occurred. */
110 static int any_error
;
113 /* Object to store one line of content. */
116 struct line_buffer_s
*next
;
117 int verbatim
; /* True if LINE contains verbatim data. The default
118 is Texinfo source. */
121 typedef struct line_buffer_s
*line_buffer_t
;
124 /* Object to collect the data of a section. */
125 struct section_buffer_s
127 char *name
; /* Malloced name of the section. This may be
128 NULL to indicate this slot is not used. */
129 line_buffer_t lines
; /* Linked list with the lines of the section. */
130 line_buffer_t
*lines_tail
; /* Helper for faster appending to the
132 line_buffer_t last_line
; /* Points to the last line appended. */
134 typedef struct section_buffer_s
*section_buffer_t
;
136 /* Variable to keep info about the current page together. */
139 /* Filename of the current page or NULL if no page is active. Malloced. */
142 /* Number of allocated elements in SECTIONS below. */
144 /* Array with the data of the sections. */
145 section_buffer_t sections
;
150 /* The list of standard section names. COMMANDS and ASSUAN are GnuPG
152 static const char * const standard_sections
[] =
153 { "NAME", "SYNOPSIS", "DESCRIPTION",
154 "RETURN VALUE", "EXIT STATUS", "ERROR HANDLING", "ERRORS",
155 "COMMANDS", "OPTIONS", "USAGE", "EXAMPLES", "FILES",
156 "ENVIRONMENT", "DIAGNOSTICS", "SECURITY", "CONFORMING TO",
157 "ASSUAN", "NOTES", "BUGS", "AUTHOR", "SEE ALSO", NULL
};
160 /*-- Local prototypes. --*/
161 static void proc_texi_buffer (FILE *fp
, const char *line
, size_t len
,
162 int *table_level
, int *eol_action
);
166 /* Print diagnostic message and exit with failure. */
168 die (const char *format
, ...)
173 fprintf (stderr
, "%s: ", PGM
);
175 va_start (arg_ptr
, format
);
176 vfprintf (stderr
, format
, arg_ptr
);
184 /* Print diagnostic message. */
186 err (const char *format
, ...)
191 if (strncmp (format
, "%s:%d:", 6))
192 fprintf (stderr
, "%s: ", PGM
);
194 va_start (arg_ptr
, format
);
195 vfprintf (stderr
, format
, arg_ptr
);
201 /* Print diagnostic message. */
203 inf (const char *format
, ...)
208 fprintf (stderr
, "%s: ", PGM
);
210 va_start (arg_ptr
, format
);
211 vfprintf (stderr
, format
, arg_ptr
);
220 void *p
= malloc (n
);
222 die ("out of core: %s", strerror (errno
));
227 xcalloc (size_t n
, size_t m
)
229 void *p
= calloc (n
, m
);
231 die ("out of core: %s", strerror (errno
));
236 xrealloc (void *old
, size_t n
)
238 void *p
= realloc (old
, n
);
240 die ("out of core: %s", strerror (errno
));
245 xstrdup (const char *string
)
247 void *p
= malloc (strlen (string
)+1);
249 die ("out of core: %s", strerror (errno
));
255 /* Uppercase the ascii characters in STRING. */
257 ascii_strupr (char *string
)
261 for (p
= string
; *p
; p
++)
268 /* Return the current date as an ISO string. */
272 static char buffer
[11+5];
274 time_t atime
= time (NULL
);
277 strcpy (buffer
, "????" "-??" "-??");
280 tp
= gmtime (&atime
);
281 sprintf (buffer
,"%04d-%02d-%02d",
282 1900+tp
->tm_year
, tp
->tm_mon
+1, tp
->tm_mday
);
289 /* Return a section buffer for the section NAME. Allocate a new buffer
290 if this is a new section. Keep track of the sections in THEPAGE.
291 This function may reallocate the section array in THEPAGE. */
292 static section_buffer_t
293 get_section_buffer (const char *name
)
296 section_buffer_t sect
;
298 /* If there is no section we put everything into the required NAME
299 section. Given that this is the first one listed it is likely
300 that error are easily visible. */
304 for (i
=0; i
< thepage
.n_sections
; i
++)
306 sect
= thepage
.sections
+ i
;
307 if (sect
->name
&& !strcmp (name
, sect
->name
))
310 for (i
=0; i
< thepage
.n_sections
; i
++)
311 if (!thepage
.sections
[i
].name
)
313 if (i
< thepage
.n_sections
)
314 sect
= thepage
.sections
+ i
;
317 /* We need to allocate or reallocate the section array. */
318 size_t old_n
= thepage
.n_sections
;
322 thepage
.sections
= xcalloc (new_n
, sizeof *thepage
.sections
);
325 thepage
.sections
= xrealloc (thepage
.sections
,
327 * sizeof *thepage
.sections
));
328 memset (thepage
.sections
+ old_n
, 0,
329 new_n
* sizeof *thepage
.sections
);
331 thepage
.n_sections
+= new_n
;
333 /* Setup the tail pointers. */
334 for (i
=old_n
; i
< thepage
.n_sections
; i
++)
336 sect
= thepage
.sections
+ i
;
337 sect
->lines_tail
= §
->lines
;
339 sect
= thepage
.sections
+ old_n
;
342 /* Store the name. */
343 assert (!sect
->name
);
344 sect
->name
= xstrdup (name
);
350 /* Add the content of LINE to the section named SECTNAME. */
352 add_content (const char *sectname
, char *line
, int verbatim
)
354 section_buffer_t sect
;
357 sect
= get_section_buffer (sectname
);
358 if (sect
->last_line
&& !sect
->last_line
->verbatim
== !verbatim
)
360 /* Lets append that line to the last one. We do this to keep
361 all lines of the same kind (i.e.verbatim or not) together in
365 lb
= sect
->last_line
;
366 n1
= strlen (lb
->line
);
367 n
= n1
+ 1 + strlen (line
) + 1;
368 lb
->line
= xrealloc (lb
->line
, n
);
369 strcpy (lb
->line
+n1
, "\n");
370 strcpy (lb
->line
+n1
+1, line
);
374 lb
= xcalloc (1, sizeof *lb
);
375 lb
->verbatim
= verbatim
;
376 lb
->line
= xstrdup (line
);
377 sect
->last_line
= lb
;
378 *sect
->lines_tail
= lb
;
379 sect
->lines_tail
= &lb
->next
;
384 /* Prepare for a new man page using the filename NAME. */
386 start_page (char *name
)
389 inf ("starting page `%s'", name
);
390 assert (!thepage
.name
);
391 thepage
.name
= xstrdup (name
);
392 thepage
.n_sections
= 0;
396 /* Write the .TH entry of the current page. Return -1 if there is a
397 problem with the page. */
403 name
= ascii_strupr (xstrdup (thepage
.name
));
404 p
= strrchr (name
, '.');
407 err ("no section name in man page `%s'", thepage
.name
);
412 fprintf (fp
, ".TH %s %s %s \"%s\" \"%s\"\n",
413 name
, p
, isodatestring (), opt_release
, opt_source
);
418 /* Process the texinfo command COMMAND (without the leading @) and
419 write output if needed to FP. REST is the remainer of the line
420 which should either point to an opening brace or to a white space.
421 The function returns the number of characters already processed
422 from REST. LEN is the usable length of REST. TABLE_LEVEL is used to
423 control the indentation of tables. */
425 proc_texi_cmd (FILE *fp
, const char *command
, const char *rest
, size_t len
,
426 int *table_level
, int *eol_action
)
429 const char *name
; /* Name of the command. */
430 int what
; /* What to do with this command. */
431 const char *lead_in
; /* String to print with a opening brace. */
432 const char *lead_out
;/* String to print with the closing brace. */
434 { "command", 0, "\\fB", "\\fR" },
435 { "code", 0, "\\fB", "\\fR" },
436 { "sc", 0, "\\fB", "\\fR" },
437 { "var", 0, "\\fI", "\\fR" },
438 { "samp", 0, "\n'", "'\n" },
439 { "file", 0, "`\\fI","\\fR'" },
440 { "env", 0, "`\\fI","\\fR'" },
443 { "option", 0, "\\fB", "\\fR" },
444 { "example", 1, ".RS 2\n.nf\n" },
445 { "smallexample", 1, ".RS 2\n.nf\n" },
449 { "xref", 0, "see: [", "]" },
450 { "pxref", 0, "see: [", "]" },
451 { "uref", 0, "(\\fB", "\\fR)" },
452 { "footnote",0, " ([", "])" },
453 { "emph", 0, "\\fI", "\\fR" },
463 { "subsection", 6, "\n.SS " },
465 { "item", 2, ".TP\n.B " },
466 { "itemx", 2, ".TP\n.B " },
469 { "quotation",1, ".RS\n\\fB" },
477 const char *lead_out
= NULL
;
480 for (i
=0; cmdtbl
[i
].name
&& strcmp (cmdtbl
[i
].name
, command
); i
++)
484 s
= cmdtbl
[i
].lead_in
;
487 lead_out
= cmdtbl
[i
].lead_out
;
488 switch (cmdtbl
[i
].what
)
490 case 1: /* Throw away the entire line. */
491 s
= memchr (rest
, '\n', len
);
492 return s
? (s
-rest
)+1 : len
;
493 case 2: /* Handle @item. */
495 case 3: /* Handle table. */
496 if (++(*table_level
) > 1)
498 /* Now throw away the entire line. */
499 s
= memchr (rest
, '\n', len
);
500 return s
? (s
-rest
)+1 : len
;
502 case 4: /* Handle end. */
503 for (s
=rest
, n
=len
; n
&& (*s
== ' ' || *s
== '\t'); s
++, n
--)
505 if (n
>= 5 && !memcmp (s
, "table", 5)
506 && (!n
|| s
[5] == ' ' || s
[5] == '\t' || s
[5] == '\n'))
508 if ((*table_level
)-- > 1)
511 else if (n
>= 7 && !memcmp (s
, "example", 7)
512 && (!n
|| s
[7] == ' ' || s
[7] == '\t' || s
[7] == '\n'))
514 fputs (".fi\n.RE\n", fp
);
516 else if (n
>= 12 && !memcmp (s
, "smallexample", 12)
517 && (!n
|| s
[12] == ' ' || s
[12] == '\t' || s
[12] == '\n'))
519 fputs (".fi\n.RE\n", fp
);
521 else if (n
>= 9 && !memcmp (s
, "quotation", 9)
522 && (!n
|| s
[9] == ' ' || s
[9] == '\t' || s
[9] == '\n'))
524 fputs ("\\fR\n.RE\n", fp
);
526 else if (n
>= 5 && !memcmp (s
, "ifset", 5)
527 && (!n
|| s
[5] == ' ' || s
[5] == '\t' || s
[5] == '\n'))
529 fputs ("\\fR\n.RE\n", fp
);
531 /* Now throw away the entire line. */
532 s
= memchr (rest
, '\n', len
);
533 return s
? (s
-rest
)+1 : len
;
534 case 5: /* Handle special comments. */
535 for (s
=rest
, n
=len
; n
&& (*s
== ' ' || *s
== '\t'); s
++, n
--)
537 if (n
>= 4 && !memcmp (s
, "man:", 4))
539 for (s
+=4, n
-=4; n
&& *s
!= '\n'; n
--, s
++)
543 /* Now throw away the entire line. */
544 s
= memchr (rest
, '\n', len
);
545 return s
? (s
-rest
)+1 : len
;
558 inf ("texinfo command `%s' not supported (%.*s)", command
,
559 ((s
= memchr (rest
, '\n', len
)), (s
? (s
-rest
) : len
)), rest
);
564 /* Find matching closing brace. */
565 for (s
=rest
+1, n
=1, i
=1; i
&& *s
&& n
< len
; s
++, n
++)
572 err ("closing brace for command `%s' not found", command
);
575 if (n
> 2 && !ignore_args
)
576 proc_texi_buffer (fp
, rest
+1, n
-2, table_level
, eol_action
);
582 fputs (lead_out
, fp
);
589 /* Process the string LINE with LEN bytes of Texinfo content. */
591 proc_texi_buffer (FILE *fp
, const char *line
, size_t len
,
592 int *table_level
, int *eol_action
)
600 for (s
=line
; *s
&& len
; s
++, len
--)
608 case '@': case '{': case '}':
609 putc (*s
, fp
); in_cmd
= 0;
611 case ':': /* Not ending a sentence flag. */
614 case '.': case '!': case '?': /* Ending a sentence. */
615 putc (*s
, fp
); in_cmd
= 0;
617 case ' ': case '\t': case '\n': /* Non collapsing spaces. */
618 putc (*s
, fp
); in_cmd
= 0;
622 cmdbuf
[cmdidx
++] = *s
;
627 else if (*s
== '{' || *s
== ' ' || *s
== '\t' || *s
== '\n')
630 n
= proc_texi_cmd (fp
, cmdbuf
, s
, len
, table_level
, eol_action
);
636 else if (cmdidx
< sizeof cmdbuf
-1)
637 cmdbuf
[cmdidx
++] = *s
;
640 err ("texinfo command too long - ignored");
650 case 1: /* Create a dummy paragraph. */
651 fputs ("\n\\ \n", fp
);
664 /* Do something with the Texinfo line LINE. */
666 parse_texi_line (FILE *fp
, const char *line
, int *table_level
)
670 /* A quick test whether there are any texinfo commands. */
671 if (!strchr (line
, '@'))
677 proc_texi_buffer (fp
, line
, strlen (line
), table_level
, &eol_action
);
682 /* Write all the lines LINES to FP. */
684 write_content (FILE *fp
, line_buffer_t lines
)
689 for (line
= lines
; line
; line
= line
->next
)
693 fputs (line
->line
, fp
);
698 /* fputs ("TEXI---", fp); */
699 /* fputs (line->line, fp); */
700 /* fputs ("---\n", fp); */
701 parse_texi_line (fp
, line
->line
, &table_level
);
709 is_standard_section (const char *name
)
714 for (i
=0; (s
=standard_sections
[i
]); i
++)
715 if (!strcmp (s
, name
))
721 /* Finish a page; that is sort the data and write it out to the file. */
726 section_buffer_t sect
;
732 return; /* No page active. */
735 inf ("finishing page `%s'", thepage
.name
);
739 if (!strcmp (opt_select
, thepage
.name
))
741 inf ("selected `%s'", thepage
.name
);
746 fp
= fopen ( "/dev/null", "w" );
748 die ("failed to open /dev/null: %s\n", strerror (errno
));
753 inf ("writing `%s'", thepage
.name
);
754 fp
= fopen ( thepage
.name
, "w" );
756 die ("failed to create `%s': %s\n", thepage
.name
, strerror (errno
));
764 for (idx
=0; (s
=standard_sections
[idx
]); idx
++)
766 for (i
=0; i
< thepage
.n_sections
; i
++)
768 sect
= thepage
.sections
+ i
;
769 if (sect
->name
&& !strcmp (s
, sect
->name
))
772 if (i
== thepage
.n_sections
)
777 fprintf (fp
, ".SH %s\n", sect
->name
);
778 write_content (fp
, sect
->lines
);
779 /* Now continue with all non standard sections directly
780 following this one. */
781 for (i
++; i
< thepage
.n_sections
; i
++)
783 sect
= thepage
.sections
+ i
;
784 if (sect
->name
&& is_standard_section (sect
->name
))
788 fprintf (fp
, ".SH %s\n", sect
->name
);
789 write_content (fp
, sect
->lines
);
802 /* FIXME: Cleanup the content. */
808 /* Parse one Texinfo file and create manpages according to the
809 embedded instructions. */
811 parse_file (const char *fname
, FILE *fp
, char **section_name
, int in_pause
)
816 int skip_to_end
= 0; /* Used to skip over menu entries. */
817 int skip_sect_line
= 0; /* Skip after @mansect. */
819 line
= xmalloc (LINESIZE
);
820 while (fgets (line
, LINESIZE
, fp
))
822 size_t n
= strlen (line
);
827 if (!n
|| line
[n
-1] != '\n')
829 err ("%s:$d: trailing linefeed missing, line too long or "
830 "embedded Nul character", fname
, lnr
);
838 if (!strncmp (line
, "@section", 8)
839 || !strncmp (line
, "@subsection", 11)
840 || !strncmp (line
, "@chapheading", 12))
844 /* We only parse lines we need and ignore the rest. There are a
845 few macros used to control this as well as one @ifset
846 command. Parts we know about are saved away into containers
847 separate for each section. */
850 for (p
=line
+1, n
=1; *p
&& *p
!= ' ' && *p
!= '\t'; p
++)
852 while (*p
== ' ' || *p
== '\t')
856 && n
== 4 && !memcmp (line
, "@end", 4)
857 && (line
[4]==' '||line
[4]=='\t'||!line
[4]))
861 else if (n
== 6 && !memcmp (line
, "@ifset", 6)
862 && !strncmp (p
, "manverb", 7) && (p
[7]==' '||p
[7]=='\t'||!p
[7]))
865 err ("%s:%d: nested \"@ifset manverb\"", fname
, lnr
);
869 else if (in_verbatim
&& n
== 4 && !memcmp (line
, "@end", 4)
870 && !strncmp (p
, "ifset", 5)
871 && (p
[5]==' '||p
[5]=='\t'||!p
[5]))
875 else if (in_verbatim
)
879 else if (n
== 8 && !memcmp (line
, "@manpage", 8))
881 free (*section_name
);
882 *section_name
= NULL
;
887 else if (n
== 8 && !memcmp (line
, "@mansect", 8))
890 err ("%s:%d: section outside of a man page", fname
, lnr
);
893 free (*section_name
);
894 *section_name
= ascii_strupr (xstrdup (p
));
899 else if (n
== 9 && !memcmp (line
, "@manpause", 9))
902 err ("%s:%d: pausing outside of a man section", fname
, lnr
);
904 err ("%s:%d: already pausing", fname
, lnr
);
908 else if (n
== 8 && !memcmp (line
, "@mancont", 8))
911 err ("%s:%d: continue outside of a man section", fname
, lnr
);
913 err ("%s:%d: continue while not pausing", fname
, lnr
);
917 else if (n
== 5 && !memcmp (line
, "@menu", 5)
918 && (line
[5]==' '||line
[5]=='\t'||!line
[5]))
922 else if (n
== 8 && !memcmp (line
, "@ifclear", 8)
923 && !strncmp (p
, "isman", 5) && (p
[5]==' '||p
[5]=='\t'||!p
[5]))
927 else if (n
== 8 && !memcmp (line
, "@include", 8)
928 && (line
[8]==' '||line
[8]=='\t'||!line
[8]))
930 char *incname
= xstrdup (p
);
931 FILE *incfp
= fopen (incname
, "r");
933 if (!incfp
&& opt_include
&& *opt_include
&& *p
!= '/')
936 incname
= xmalloc (strlen (opt_include
) + 1
938 strcpy (incname
, opt_include
);
939 if ( incname
[strlen (incname
)-1] != '/' )
940 strcat (incname
, "/");
942 incfp
= fopen (incname
, "r");
946 err ("can't open include file `%s':%s",
947 incname
, strerror (errno
));
950 parse_file (incname
, incfp
, section_name
, in_pause
);
955 else if (!skip_to_end
)
958 else if (!skip_to_end
)
961 if (got_line
&& in_verbatim
)
962 add_content (*section_name
, line
, 1);
963 else if (got_line
&& thepage
.name
&& *section_name
&& !in_pause
)
964 add_content (*section_name
, line
, 0);
968 err ("%s:%d: read error: %s", fname
, lnr
, strerror (errno
));
974 top_parse_file (const char *fname
, FILE *fp
)
976 char *section_name
= NULL
; /* Name of the current section or NULL
977 if not in a section. */
978 parse_file (fname
, fp
, §ion_name
, 0);
985 main (int argc
, char **argv
)
996 while (argc
&& last_argc
!= argc
)
999 if (!strcmp (*argv
, "--"))
1004 else if (!strcmp (*argv
, "--help"))
1007 "Usage: " PGM
" [OPTION] [FILE]\n"
1008 "Extract man pages from a Texinfo source.\n\n"
1009 " --source NAME use NAME as source field\n"
1010 " --release STRING use STRING as the release field\n"
1011 " --store write output using @manpage name\n"
1012 " --select NAME only output pages with @manpage NAME\n"
1013 " --verbose enable extra informational output\n"
1014 " --debug enable additional debug output\n"
1015 " --help display this help and exit\n"
1016 " -I DIR also search in include DIR\n\n"
1017 "With no FILE, or when FILE is -, read standard input.\n\n"
1018 "Report bugs to <bugs@g10code.com>.");
1021 else if (!strcmp (*argv
, "--version"))
1023 puts (PGM
" " VERSION
"\n"
1024 "Copyright (C) 2005 g10 Code GmbH\n"
1025 "This program comes with ABSOLUTELY NO WARRANTY.\n"
1026 "This is free software, and you are welcome to redistribute it\n"
1027 "under certain conditions. See the file COPYING for details.");
1030 else if (!strcmp (*argv
, "--verbose"))
1035 else if (!strcmp (*argv
, "--quiet"))
1040 else if (!strcmp (*argv
, "--debug"))
1042 verbose
= debug
= 1;
1045 else if (!strcmp (*argv
, "--source"))
1054 else if (!strcmp (*argv
, "--release"))
1059 opt_release
= *argv
;
1063 else if (!strcmp (*argv
, "--store"))
1068 else if (!strcmp (*argv
, "--select"))
1073 opt_select
= strrchr (*argv
, '/');
1081 else if (!strcmp (*argv
, "-I"))
1086 opt_include
= *argv
;
1093 die ("usage: " PGM
" [OPTION] [FILE] (try --help for more information)\n");
1095 /* Start processing. */
1096 if (argc
&& strcmp (*argv
, "-"))
1098 FILE *fp
= fopen (*argv
, "rb");
1100 die ("%s:0: can't open file: %s", *argv
, strerror (errno
));
1101 top_parse_file (*argv
, fp
);
1105 top_parse_file ("-", stdin
);
1113 compile-command: "gcc -Wall -g -Wall -o yat2m yat2m.c"