]>
git.ipfire.org Git - thirdparty/cups.git/blob - man/mantohtml.c
2 * Man page to HTML conversion program.
4 * Copyright 2007-2010, 2014 by Apple Inc.
5 * Copyright 2004-2006 by Easy Software Products.
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * Include necessary headers.
18 #include <cups/string-private.h>
19 #include <cups/array-private.h>
27 static const char /* Start/end tags for fonts */
28 * const start_fonts
[] = { "", "<b>", "<i>" },
29 * const end_fonts
[] = { "", "</b>", "</i>" };
36 static void html_alternate(const char *s
, const char *first
, const char *second
, FILE *fp
);
37 static void html_fputs(const char *s
, int *font
, FILE *fp
);
38 static void html_putc(int ch
, FILE *fp
);
39 static void strmove(char *d
, const char *s
);
43 * 'main()' - Convert a man page to HTML.
46 int /* O - Exit status */
47 main(int argc
, /* I - Number of command-line args */
48 char *argv
[]) /* I - Command-line arguments */
50 FILE *infile
, /* Input file */
51 *outfile
; /* Output file */
52 char line
[1024], /* Line from file */
53 *lineptr
, /* Pointer into line */
54 anchor
[1024], /* Anchor */
55 name
[1024], /* Man page name */
56 ddpost
[256]; /* Tagged list post markup */
57 int section
= -1, /* Man page section */
58 pre
= 0, /* Preformatted */
59 font
= 0, /* Current font */
60 linenum
= 0; /* Current line number */
61 float list_indent
= 0.0f
, /* Current list indentation */
62 nested_indent
= 0.0f
; /* Nested list indentation, if any */
63 const char *list
= NULL
, /* Current list, if any */
64 *nested
= NULL
; /* Nested list, if any */
65 const char *post
= NULL
; /* Text to add after the current line */
74 fputs("Usage: mantohtml [filename.man [filename.html]]\n", stderr
);
79 * Open files as needed...
84 if ((infile
= fopen(argv
[1], "r")) == NULL
)
95 if ((outfile
= fopen(argv
[2], "w")) == NULL
)
106 * Read from input and write the output...
109 fputs("<!DOCTYPE HTML>\n"
111 "<!-- SECTION: Man Pages -->\n"
113 "\t<link rel=\"stylesheet\" type=\"text/css\" "
114 "href=\"../cups-printable.css\">\n", outfile
);
118 while (fgets(line
, sizeof(line
), infile
))
120 size_t linelen
= strlen(line
); /* Length of line */
122 if (linelen
> 0 && line
[linelen
- 1] == '\n')
123 line
[linelen
- 1] = '\0';
130 * Strip leading whitespace...
133 while (line
[1] == ' ' || line
[1] == '\t')
134 strmove(line
+ 1, line
+ 2);
137 * Process man page commands...
140 if (!strncmp(line
, ".TH ", 4) && section
< 0)
143 * Grab man page title...
146 sscanf(line
+ 4, "%s%d", name
, §ion
);
149 "\t<title>%s(%d)</title>\n"
152 "<h1 class=\"title\">%s(%d)</h1>\n"
154 name
, section
, name
, section
, start_fonts
[font
]);
156 else if (section
< 0)
158 else if (!strncmp(line
, ".SH ", 4) || !strncmp(line
, ".SS ", 4))
166 fputs(end_fonts
[font
], outfile
);
171 fprintf(outfile
, "</%s>\n", list
);
176 fputs("<h2 class=\"title\"><a name=\"", outfile
);
178 fputs("<h3><a name=\"", outfile
);
182 fputs(anchor
, outfile
);
187 for (lineptr
= line
+ 4; *lineptr
; lineptr
++)
188 if (*lineptr
== '\"')
190 else if (isalnum(*lineptr
& 255))
191 html_putc(*lineptr
, outfile
);
193 html_putc('_', outfile
);
196 fputs("\">", outfile
);
198 for (lineptr
= line
+ 4; *lineptr
; lineptr
++)
200 if (*lineptr
== '\"')
202 else if (*lineptr
== ' ')
204 html_putc(' ', outfile
);
211 html_putc(*lineptr
, outfile
);
213 html_putc(tolower(*lineptr
& 255), outfile
);
220 fputs("</a></h2>\n", outfile
);
222 fputs("</a></h3>\n", outfile
);
224 else if (!strncmp(line
, ".B ", 3))
230 fputs(end_fonts
[font
], outfile
);
234 fprintf(outfile
, "<a name=\"%s\">", anchor
);
236 html_alternate(line
+ 3, "b", "b", outfile
);
240 fputs("</a>", outfile
);
246 fputs(post
, outfile
);
250 else if (!strncmp(line
, ".I ", 3))
253 * Grab italic text...
256 fputs(end_fonts
[font
], outfile
);
260 fprintf(outfile
, "<a name=\"%s\">", anchor
);
262 html_alternate(line
+ 3, "i", "i", outfile
);
266 fputs("</a>", outfile
);
272 fputs(post
, outfile
);
276 else if (!strncmp(line
, ".BI ", 4))
279 * Alternating bold and italic text...
282 fputs(end_fonts
[font
], outfile
);
286 fprintf(outfile
, "<a name=\"%s\">", anchor
);
288 html_alternate(line
+ 4, "b", "i", outfile
);
292 fputs("</a>", outfile
);
298 fputs(post
, outfile
);
302 else if (!strncmp(line
, ".BR ", 4))
305 * Alternating bold and roman (plain) text...
308 fputs(end_fonts
[font
], outfile
);
312 fprintf(outfile
, "<a name=\"%s\">", anchor
);
314 html_alternate(line
+ 4, "b", NULL
, outfile
);
318 fputs("</a>", outfile
);
324 fputs(post
, outfile
);
328 else if (!strncmp(line
, ".IB ", 4))
331 * Alternating italic and bold text...
334 fputs(end_fonts
[font
], outfile
);
338 fprintf(outfile
, "<a name=\"%s\">", anchor
);
340 html_alternate(line
+ 4, "i", "b", outfile
);
344 fputs("</a>", outfile
);
350 fputs(post
, outfile
);
354 else if (!strncmp(line
, ".IR ", 4))
357 * Alternating italic and roman (plain) text...
360 fputs(end_fonts
[font
], outfile
);
364 fprintf(outfile
, "<a name=\"%s\">", anchor
);
366 html_alternate(line
+ 4, "i", NULL
, outfile
);
370 fputs("</a>", outfile
);
376 fputs(post
, outfile
);
380 else if (!strncmp(line
, ".RB ", 4))
383 * Alternating roman (plain) and bold text...
386 fputs(end_fonts
[font
], outfile
);
390 fprintf(outfile
, "<a name=\"%s\">", anchor
);
392 html_alternate(line
+ 4, NULL
, "b", outfile
);
396 fputs("</a>", outfile
);
402 fputs(post
, outfile
);
406 else if (!strncmp(line
, ".RI ", 4))
409 * Alternating roman (plain) and italic text...
412 fputs(end_fonts
[font
], outfile
);
416 fprintf(outfile
, "<a name=\"%s\">", anchor
);
418 html_alternate(line
+ 4, NULL
, "i", outfile
);
422 fputs("</a>", outfile
);
428 fputs(post
, outfile
);
432 else if (!strncmp(line
, ".SB ", 4))
435 * Alternating small and bold text...
438 fputs(end_fonts
[font
], outfile
);
442 fprintf(outfile
, "<a name=\"%s\">", anchor
);
444 html_alternate(line
+ 4, "small", "b", outfile
);
448 fputs("</a>", outfile
);
454 fputs(post
, outfile
);
458 else if (!strncmp(line
, ".SM ", 4))
464 fputs(end_fonts
[font
], outfile
);
468 fprintf(outfile
, "<a name=\"%s\">", anchor
);
470 html_alternate(line
+ 4, "small", "small", outfile
);
474 fputs("</a>", outfile
);
480 fputs(post
, outfile
);
484 else if (!strcmp(line
, ".LP") || !strcmp(line
, ".PP") || !strcmp(line
, ".P"))
490 fputs(end_fonts
[font
], outfile
);
495 fprintf(outfile
, "</%s>\n", list
);
499 fputs("<p>", outfile
);
503 fprintf(outfile
, "<a name=\"%s\"></a>", anchor
);
507 else if (!strcmp(line
, ".RS") || !strncmp(line
, ".RS ", 4))
513 float amount
= 3.0; /* Indentation */
516 amount
= (float)atof(line
+ 4);
518 fputs(end_fonts
[font
], outfile
);
525 nested_indent
= list_indent
;
529 fprintf(outfile
, "<div style=\"margin-left: %.1fem;\">\n", amount
- nested_indent
);
531 else if (!strcmp(line
, ".RE"))
537 fputs(end_fonts
[font
], outfile
);
540 fputs("</div>\n", outfile
);
547 list_indent
= nested_indent
;
548 nested_indent
= 0.0f
;
551 else if (!strcmp(line
, ".HP") || !strncmp(line
, ".HP ", 4))
554 * Hanging paragraph...
559 float amount
= 3.0; /* Indentation */
562 amount
= (float)atof(line
+ 4);
564 fputs(end_fonts
[font
], outfile
);
569 fprintf(outfile
, "</%s>\n", list
);
573 fprintf(outfile
, "<p style=\"margin-left: %.1fem; text-indent: %.1fem\">", amount
, -amount
);
577 fprintf(outfile
, "<a name=\"%s\"></a>", anchor
);
584 else if (!strcmp(line
, ".TP") || !strncmp(line
, ".TP ", 4))
592 float amount
= 3.0; /* Indentation */
595 amount
= (float)atof(line
+ 4);
597 fputs(end_fonts
[font
], outfile
);
600 if (list
&& strcmp(list
, "dl"))
602 fprintf(outfile
, "</%s>\n", list
);
608 fputs("<dl class=\"man\">\n", outfile
);
610 list_indent
= amount
;
613 fputs("<dt>", outfile
);
614 snprintf(ddpost
, sizeof(ddpost
), "<dd style=\"margin-left: %.1fem\">", amount
);
619 fprintf(outfile
, "<a name=\"%s\"></a>", anchor
);
623 else if (!strncmp(line
, ".IP ", 4))
626 * Indented paragraph...
631 float amount
= 3.0; /* Indentation */
632 const char *newlist
= NULL
; /* New list style */
633 const char *newtype
= NULL
; /* New list numbering type */
635 fputs(end_fonts
[font
], outfile
);
639 while (isspace(*lineptr
& 255))
642 if (!strncmp(lineptr
, "\\(bu", 4) || !strncmp(lineptr
, "\\(em", 4))
650 else if (isdigit(*lineptr
& 255))
658 else if (islower(*lineptr
& 255))
661 * Lowercase alpha list...
667 else if (isupper(*lineptr
& 255))
670 * Lowercase alpha list...
677 while (!isspace(*lineptr
& 255))
679 while (isspace(*lineptr
& 255))
682 if (isdigit(*lineptr
& 255))
683 amount
= (float)atof(lineptr
);
685 if (newlist
&& list
&& strcmp(newlist
, list
))
687 fprintf(outfile
, "</%s>\n", list
);
691 if (newlist
&& !list
)
694 fprintf(outfile
, "<%s type=\"%s\">\n", newlist
, newtype
);
696 fprintf(outfile
, "<%s>\n", newlist
);
702 fprintf(outfile
, "<li style=\"margin-left: %.1fem;\">", amount
);
704 fprintf(outfile
, "<p style=\"margin-left: %.1fem;\">", amount
);
708 fprintf(outfile
, "<a name=\"%s\"></a>", anchor
);
712 else if (!strncmp(line
, ".br", 3))
718 fputs("<br>\n", outfile
);
720 else if (!strncmp(line
, ".de ", 4))
723 * Define macro - ignore...
726 while (fgets(line
, sizeof(line
), infile
))
730 if (!strncmp(line
, "..", 2))
734 else if (!strncmp(line
, ".ds ", 4) || !strncmp(line
, ".rm ", 4) ||
735 !strncmp(line
, ".tr ", 4) || !strncmp(line
, ".hy ", 4) ||
736 !strncmp(line
, ".IX ", 4) || !strncmp(line
, ".PD", 3) ||
737 !strncmp(line
, ".Sp", 3))
740 * Ignore unused commands...
743 else if (!strncmp(line
, ".Vb", 3) || !strncmp(line
, ".nf", 3) || !strncmp(line
, ".EX", 3))
746 * Start preformatted...
749 fputs(end_fonts
[font
], outfile
);
754 // fprintf(outfile, "</%s>\n", list);
759 fputs("<pre class=\"man\">\n", outfile
);
761 else if (!strncmp(line
, ".Ve", 3) || !strncmp(line
, ".fi", 3) || !strncmp(line
, ".EE", 3))
764 * End preformatted...
767 fputs(end_fonts
[font
], outfile
);
773 fputs("</pre>\n", outfile
);
776 else if (!strncmp(line
, ".\\}", 3))
779 * Ignore close block...
782 else if (!strncmp(line
, ".ie", 3) || !strncmp(line
, ".if", 3) ||
783 !strncmp(line
, ".el", 3))
786 * If/else - ignore...
789 if (strchr(line
, '{') != NULL
)
792 * Skip whole block...
795 while (fgets(line
, sizeof(line
), infile
))
799 if (strchr(line
, '}') != NULL
)
805 else if (!strncmp(line
, ". ", 4))
812 else if (!strncmp(line
, ".\\\"#", 4))
815 * Anchor for HTML output...
818 strlcpy(anchor
, line
+ 4, sizeof(anchor
));
820 else if (strncmp(line
, ".\\\"", 3))
826 if ((lineptr
= strchr(line
, ' ')) != NULL
)
828 else if ((lineptr
= strchr(line
, '\n')) != NULL
)
831 fprintf(stderr
, "mantohtml: Unknown man page command \'%s\' on line %d.\n", line
, linenum
);
835 * Skip continuation lines...
838 lineptr
= line
+ strlen(line
) - 1;
839 if (lineptr
>= line
&& *lineptr
== '\\')
841 while (fgets(line
, sizeof(line
), infile
))
844 lineptr
= line
+ strlen(line
) - 2;
846 if (lineptr
< line
|| *lineptr
!= '\\')
854 * Process man page text...
857 html_fputs(line
, &font
, outfile
);
862 fputs(post
, outfile
);
868 fprintf(outfile
, "%s\n", end_fonts
[font
]);
873 fprintf(outfile
, "</%s>\n", list
);
878 "</html>\n", outfile
);
887 if (outfile
!= stdout
)
891 * Return with no errors...
899 * 'html_alternate()' - Alternate words between two styles of text.
903 html_alternate(const char *s
, /* I - String */
904 const char *first
, /* I - First style or NULL */
905 const char *second
, /* I - Second style of NULL */
906 FILE *fp
) /* I - File */
908 int i
= 0; /* Which style */
909 int quote
= 0; /* Saw quote? */
910 int dolinks
, /* Do hyperlinks to other man pages? */
911 link
= 0; /* Doing a link now? */
915 * Skip leading whitespace...
918 while (isspace(*s
& 255))
921 dolinks
= first
&& !strcmp(first
, "b") && !second
;
928 * See if we need to make a link to a man page...
931 const char *end
; /* End of current word */
932 const char *next
; /* Start of next word */
934 for (end
= s
; *end
&& !isspace(*end
& 255); end
++);
935 for (next
= end
; isspace(*next
& 255); next
++);
937 if (isalnum(*s
& 255) && *next
== '(')
940 * See if the man file is available locally...
943 char name
[1024], /* Name */
944 manfile
[1024], /* Man page filename */
945 manurl
[1024]; /* Man page URL */
947 strlcpy(name
, s
, sizeof(name
));
948 if ((size_t)(end
- s
) < sizeof(name
))
949 name
[end
- s
] = '\0';
951 snprintf(manfile
, sizeof(manfile
), "%s.man", name
);
952 snprintf(manurl
, sizeof(manurl
), "man-%s.html?TOPIC=Man+Pages", name
);
954 if (!access(manfile
, 0))
957 * Local man page, do a link...
960 fprintf(fp
, "<a href=\"%s\">", manurl
);
967 fprintf(fp
, "<%s>", first
);
968 else if (i
&& second
)
969 fprintf(fp
, "<%s>", second
);
971 while ((!isspace(*s
& 255) || quote
) && *s
)
976 if (*s
== '\\' && s
[1])
986 fprintf(fp
, "</%s>", first
);
987 else if (i
&& second
)
988 fprintf(fp
, "</%s>", second
);
999 * Skip trailing whitespace...
1002 while (isspace(*s
& 255))
1010 * 'html_fputs()' - Output a string, quoting as needed HTML entities.
1014 html_fputs(const char *s
, /* I - String */
1015 int *font
, /* IO - Font */
1016 FILE *fp
) /* I - File */
1028 int newfont
; /* New font */
1058 fprintf(stderr
, "mantohtml: Unknown font \"\\f%c\" ignored.\n", s
[-1]);
1063 if (newfont
!= *font
)
1065 fputs(end_fonts
[*font
], fp
);
1067 fputs(start_fonts
[*font
], fp
);
1073 * Substitute macro...
1087 if (!strncmp(s
, "lq", 2))
1088 fputs("“", fp
);
1089 else if (!strncmp(s
, "rq", 2))
1090 fputs("”", fp
);
1091 else if (!strncmp(s
, "Tm", 2))
1092 fputs("<sup>TM</sup>", fp
);
1094 fprintf(stderr
, "mantohtml: Unknown macro \"\\*(%2s\" ignored.\n", s
);
1103 fprintf(stderr
, "mantohtml: Unknown macro \"\\*%c\" ignored.\n", s
[-1]);
1109 if (!strncmp(s
, "(em", 3))
1111 fputs("—", fp
);
1114 else if (!strncmp(s
, "(en", 3))
1116 fputs("–", fp
);
1128 * Substitute escaped character...
1132 if (!strncmp(s
, "co]", 3))
1133 fputs("©", fp
);
1134 else if (!strncmp(s
, "de]", 3))
1136 else if (!strncmp(s
, "rg]", 3))
1138 else if (!strncmp(s
, "tm]", 3))
1139 fputs("<sup>TM</sup>", fp
);
1148 else if (isdigit(s
[0]) && isdigit(s
[1]) &&
1151 fprintf(fp
, "&#%d;", ((s
[0] - '0') * 8 + s
[1] - '0') * 8 + s
[2] - '0');
1156 if (*s
!= '\\' && *s
== '\"' && *s
== '\'' && *s
== '-')
1157 fprintf(stderr
, "mantohtml: Unrecognized escape \"\\%c\" ignored.\n", *s
);
1159 html_putc(*s
++, fp
);
1162 else if (!strncmp(s
, "http://", 7) || !strncmp(s
, "https://", 8) || !strncmp(s
, "ftp://", 6))
1168 char temp
[1024]; /* Temporary string */
1169 const char *end
= s
+ 6; /* End of URL */
1171 while (*end
&& !isspace(*end
& 255))
1174 if (end
[-1] == ',' || end
[-1] == '.' || end
[-1] == ')')
1177 strlcpy(temp
, s
, sizeof(temp
));
1178 if ((size_t)(end
-s
) < sizeof(temp
))
1179 temp
[end
- s
] = '\0';
1181 fprintf(fp
, "<a href=\"%s\">%s</a>", temp
, temp
);
1185 html_putc(*s
++ & 255, fp
);
1191 * 'html_putc()' - Put a single character, using entities as needed.
1195 html_putc(int ch
, /* I - Character */
1196 FILE *fp
) /* I - File */
1208 * 'strmove()' - Move characters within a string.
1212 strmove(char *d
, /* I - Destination */
1213 const char *s
) /* I - Source */