]>
git.ipfire.org Git - thirdparty/cups.git/blob - man/mantohtml.c
2 * Man page to HTML conversion program.
4 * Copyright 2007-2017 by Apple Inc.
5 * Copyright 2004-2006 by Easy Software Products.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
11 * Include necessary headers.
14 #include <cups/string-private.h>
15 #include <cups/array-private.h>
23 static const char /* Start/end tags for fonts */
24 * const start_fonts
[] = { "", "<b>", "<i>" },
25 * const end_fonts
[] = { "", "</b>", "</i>" };
32 static void html_alternate(const char *s
, const char *first
, const char *second
, FILE *fp
);
33 static void html_fputs(const char *s
, int *font
, FILE *fp
);
34 static void html_putc(int ch
, FILE *fp
);
35 static void strmove(char *d
, const char *s
);
39 * 'main()' - Convert a man page to HTML.
42 int /* O - Exit status */
43 main(int argc
, /* I - Number of command-line args */
44 char *argv
[]) /* I - Command-line arguments */
46 FILE *infile
, /* Input file */
47 *outfile
; /* Output file */
48 char line
[1024], /* Line from file */
49 *lineptr
, /* Pointer into line */
50 anchor
[1024], /* Anchor */
51 name
[1024], /* Man page name */
52 ddpost
[256]; /* Tagged list post markup */
53 int section
= -1, /* Man page section */
54 pre
= 0, /* Preformatted */
55 font
= 0, /* Current font */
56 linenum
= 0; /* Current line number */
57 float list_indent
= 0.0f
, /* Current list indentation */
58 nested_indent
= 0.0f
; /* Nested list indentation, if any */
59 const char *list
= NULL
, /* Current list, if any */
60 *nested
= NULL
; /* Nested list, if any */
61 const char *post
= NULL
; /* Text to add after the current line */
70 fputs("Usage: mantohtml [filename.man [filename.html]]\n", stderr
);
75 * Open files as needed...
80 if ((infile
= fopen(argv
[1], "r")) == NULL
)
91 if ((outfile
= fopen(argv
[2], "w")) == NULL
)
102 * Read from input and write the output...
105 fputs("<!DOCTYPE HTML>\n"
107 "<!-- SECTION: Man Pages -->\n"
109 "\t<link rel=\"stylesheet\" type=\"text/css\" "
110 "href=\"../cups-printable.css\">\n", outfile
);
114 while (fgets(line
, sizeof(line
), infile
))
116 size_t linelen
= strlen(line
); /* Length of line */
118 if (linelen
> 0 && line
[linelen
- 1] == '\n')
119 line
[linelen
- 1] = '\0';
126 * Strip leading whitespace...
129 while (line
[1] == ' ' || line
[1] == '\t')
130 strmove(line
+ 1, line
+ 2);
133 * Process man page commands...
136 if (!strncmp(line
, ".TH ", 4) && section
< 0)
139 * Grab man page title...
142 sscanf(line
+ 4, "%s%d", name
, §ion
);
145 "\t<title>%s(%d)</title>\n"
148 "<h1 class=\"title\">%s(%d)</h1>\n"
150 name
, section
, name
, section
, start_fonts
[font
]);
152 else if (section
< 0)
154 else if (!strncmp(line
, ".SH ", 4) || !strncmp(line
, ".SS ", 4))
162 fputs(end_fonts
[font
], outfile
);
167 fprintf(outfile
, "</%s>\n", list
);
172 fputs("<h2 class=\"title\"><a name=\"", outfile
);
174 fputs("<h3><a name=\"", outfile
);
178 fputs(anchor
, outfile
);
183 for (lineptr
= line
+ 4; *lineptr
; lineptr
++)
184 if (*lineptr
== '\"')
186 else if (isalnum(*lineptr
& 255))
187 html_putc(*lineptr
, outfile
);
189 html_putc('_', outfile
);
192 fputs("\">", outfile
);
194 for (lineptr
= line
+ 4; *lineptr
; lineptr
++)
196 if (*lineptr
== '\"')
198 else if (*lineptr
== ' ')
200 html_putc(' ', outfile
);
207 html_putc(*lineptr
, outfile
);
209 html_putc(tolower(*lineptr
& 255), outfile
);
216 fputs("</a></h2>\n", outfile
);
218 fputs("</a></h3>\n", outfile
);
220 else if (!strncmp(line
, ".B ", 3))
226 fputs(end_fonts
[font
], outfile
);
230 fprintf(outfile
, "<a name=\"%s\">", anchor
);
232 html_alternate(line
+ 3, "b", "b", outfile
);
236 fputs("</a>", outfile
);
242 fputs(post
, outfile
);
246 else if (!strncmp(line
, ".I ", 3))
249 * Grab italic text...
252 fputs(end_fonts
[font
], outfile
);
256 fprintf(outfile
, "<a name=\"%s\">", anchor
);
258 html_alternate(line
+ 3, "i", "i", outfile
);
262 fputs("</a>", outfile
);
268 fputs(post
, outfile
);
272 else if (!strncmp(line
, ".BI ", 4))
275 * Alternating bold and italic text...
278 fputs(end_fonts
[font
], outfile
);
282 fprintf(outfile
, "<a name=\"%s\">", anchor
);
284 html_alternate(line
+ 4, "b", "i", outfile
);
288 fputs("</a>", outfile
);
294 fputs(post
, outfile
);
298 else if (!strncmp(line
, ".BR ", 4))
301 * Alternating bold and roman (plain) text...
304 fputs(end_fonts
[font
], outfile
);
308 fprintf(outfile
, "<a name=\"%s\">", anchor
);
310 html_alternate(line
+ 4, "b", NULL
, outfile
);
314 fputs("</a>", outfile
);
320 fputs(post
, outfile
);
324 else if (!strncmp(line
, ".IB ", 4))
327 * Alternating italic and bold text...
330 fputs(end_fonts
[font
], outfile
);
334 fprintf(outfile
, "<a name=\"%s\">", anchor
);
336 html_alternate(line
+ 4, "i", "b", outfile
);
340 fputs("</a>", outfile
);
346 fputs(post
, outfile
);
350 else if (!strncmp(line
, ".IR ", 4))
353 * Alternating italic and roman (plain) text...
356 fputs(end_fonts
[font
], outfile
);
360 fprintf(outfile
, "<a name=\"%s\">", anchor
);
362 html_alternate(line
+ 4, "i", NULL
, outfile
);
366 fputs("</a>", outfile
);
372 fputs(post
, outfile
);
376 else if (!strncmp(line
, ".RB ", 4))
379 * Alternating roman (plain) and bold text...
382 fputs(end_fonts
[font
], outfile
);
386 fprintf(outfile
, "<a name=\"%s\">", anchor
);
388 html_alternate(line
+ 4, NULL
, "b", outfile
);
392 fputs("</a>", outfile
);
398 fputs(post
, outfile
);
402 else if (!strncmp(line
, ".RI ", 4))
405 * Alternating roman (plain) and italic text...
408 fputs(end_fonts
[font
], outfile
);
412 fprintf(outfile
, "<a name=\"%s\">", anchor
);
414 html_alternate(line
+ 4, NULL
, "i", outfile
);
418 fputs("</a>", outfile
);
424 fputs(post
, outfile
);
428 else if (!strncmp(line
, ".SB ", 4))
431 * Alternating small and bold text...
434 fputs(end_fonts
[font
], outfile
);
438 fprintf(outfile
, "<a name=\"%s\">", anchor
);
440 html_alternate(line
+ 4, "small", "b", outfile
);
444 fputs("</a>", outfile
);
450 fputs(post
, outfile
);
454 else if (!strncmp(line
, ".SM ", 4))
460 fputs(end_fonts
[font
], outfile
);
464 fprintf(outfile
, "<a name=\"%s\">", anchor
);
466 html_alternate(line
+ 4, "small", "small", outfile
);
470 fputs("</a>", outfile
);
476 fputs(post
, outfile
);
480 else if (!strcmp(line
, ".LP") || !strcmp(line
, ".PP") || !strcmp(line
, ".P"))
486 fputs(end_fonts
[font
], outfile
);
491 fprintf(outfile
, "</%s>\n", list
);
495 fputs("<p>", outfile
);
499 fprintf(outfile
, "<a name=\"%s\"></a>", anchor
);
503 else if (!strcmp(line
, ".RS") || !strncmp(line
, ".RS ", 4))
509 float amount
= 3.0; /* Indentation */
512 amount
= (float)atof(line
+ 4);
514 fputs(end_fonts
[font
], outfile
);
521 nested_indent
= list_indent
;
525 fprintf(outfile
, "<div style=\"margin-left: %.1fem;\">\n", amount
- nested_indent
);
527 else if (!strcmp(line
, ".RE"))
533 fputs(end_fonts
[font
], outfile
);
536 fputs("</div>\n", outfile
);
543 list_indent
= nested_indent
;
544 nested_indent
= 0.0f
;
547 else if (!strcmp(line
, ".HP") || !strncmp(line
, ".HP ", 4))
550 * Hanging paragraph...
555 float amount
= 3.0; /* Indentation */
558 amount
= (float)atof(line
+ 4);
560 fputs(end_fonts
[font
], outfile
);
565 fprintf(outfile
, "</%s>\n", list
);
569 fprintf(outfile
, "<p style=\"margin-left: %.1fem; text-indent: %.1fem\">", amount
, -amount
);
573 fprintf(outfile
, "<a name=\"%s\"></a>", anchor
);
580 else if (!strcmp(line
, ".TP") || !strncmp(line
, ".TP ", 4))
588 float amount
= 3.0; /* Indentation */
591 amount
= (float)atof(line
+ 4);
593 fputs(end_fonts
[font
], outfile
);
596 if (list
&& strcmp(list
, "dl"))
598 fprintf(outfile
, "</%s>\n", list
);
604 fputs("<dl class=\"man\">\n", outfile
);
606 list_indent
= amount
;
609 fputs("<dt>", outfile
);
610 snprintf(ddpost
, sizeof(ddpost
), "<dd style=\"margin-left: %.1fem\">", amount
);
615 fprintf(outfile
, "<a name=\"%s\"></a>", anchor
);
619 else if (!strncmp(line
, ".IP ", 4))
622 * Indented paragraph...
627 float amount
= 3.0; /* Indentation */
628 const char *newlist
= NULL
; /* New list style */
629 const char *newtype
= NULL
; /* New list numbering type */
631 fputs(end_fonts
[font
], outfile
);
635 while (isspace(*lineptr
& 255))
638 if (!strncmp(lineptr
, "\\(bu", 4) || !strncmp(lineptr
, "\\(em", 4))
646 else if (isdigit(*lineptr
& 255))
654 else if (islower(*lineptr
& 255))
657 * Lowercase alpha list...
663 else if (isupper(*lineptr
& 255))
666 * Lowercase alpha list...
673 while (!isspace(*lineptr
& 255))
675 while (isspace(*lineptr
& 255))
678 if (isdigit(*lineptr
& 255))
679 amount
= (float)atof(lineptr
);
681 if (newlist
&& list
&& strcmp(newlist
, list
))
683 fprintf(outfile
, "</%s>\n", list
);
687 if (newlist
&& !list
)
690 fprintf(outfile
, "<%s type=\"%s\">\n", newlist
, newtype
);
692 fprintf(outfile
, "<%s>\n", newlist
);
698 fprintf(outfile
, "<li style=\"margin-left: %.1fem;\">", amount
);
700 fprintf(outfile
, "<p style=\"margin-left: %.1fem;\">", amount
);
704 fprintf(outfile
, "<a name=\"%s\"></a>", anchor
);
708 else if (!strncmp(line
, ".br", 3))
714 fputs("<br>\n", outfile
);
716 else if (!strncmp(line
, ".de ", 4))
719 * Define macro - ignore...
722 while (fgets(line
, sizeof(line
), infile
))
726 if (!strncmp(line
, "..", 2))
730 else if (!strncmp(line
, ".ds ", 4) || !strncmp(line
, ".rm ", 4) ||
731 !strncmp(line
, ".tr ", 4) || !strncmp(line
, ".hy ", 4) ||
732 !strncmp(line
, ".IX ", 4) || !strncmp(line
, ".PD", 3) ||
733 !strncmp(line
, ".Sp", 3))
736 * Ignore unused commands...
739 else if (!strncmp(line
, ".Vb", 3) || !strncmp(line
, ".nf", 3) || !strncmp(line
, ".EX", 3))
742 * Start preformatted...
745 fputs(end_fonts
[font
], outfile
);
750 // fprintf(outfile, "</%s>\n", list);
755 fputs("<pre class=\"man\">\n", outfile
);
757 else if (!strncmp(line
, ".Ve", 3) || !strncmp(line
, ".fi", 3) || !strncmp(line
, ".EE", 3))
760 * End preformatted...
763 fputs(end_fonts
[font
], outfile
);
769 fputs("</pre>\n", outfile
);
772 else if (!strncmp(line
, ".\\}", 3))
775 * Ignore close block...
778 else if (!strncmp(line
, ".ie", 3) || !strncmp(line
, ".if", 3) ||
779 !strncmp(line
, ".el", 3))
782 * If/else - ignore...
785 if (strchr(line
, '{') != NULL
)
788 * Skip whole block...
791 while (fgets(line
, sizeof(line
), infile
))
795 if (strchr(line
, '}') != NULL
)
801 else if (!strncmp(line
, ". ", 4))
808 else if (!strncmp(line
, ".\\\"#", 4))
811 * Anchor for HTML output...
814 strlcpy(anchor
, line
+ 4, sizeof(anchor
));
816 else if (strncmp(line
, ".\\\"", 3))
822 if ((lineptr
= strchr(line
, ' ')) != NULL
)
824 else if ((lineptr
= strchr(line
, '\n')) != NULL
)
827 fprintf(stderr
, "mantohtml: Unknown man page command \'%s\' on line %d.\n", line
, linenum
);
831 * Skip continuation lines...
834 lineptr
= line
+ strlen(line
) - 1;
835 if (lineptr
>= line
&& *lineptr
== '\\')
837 while (fgets(line
, sizeof(line
), infile
))
840 lineptr
= line
+ strlen(line
) - 2;
842 if (lineptr
< line
|| *lineptr
!= '\\')
850 * Process man page text...
853 html_fputs(line
, &font
, outfile
);
858 fputs(post
, outfile
);
864 fprintf(outfile
, "%s\n", end_fonts
[font
]);
869 fprintf(outfile
, "</%s>\n", list
);
874 "</html>\n", outfile
);
883 if (outfile
!= stdout
)
887 * Return with no errors...
895 * 'html_alternate()' - Alternate words between two styles of text.
899 html_alternate(const char *s
, /* I - String */
900 const char *first
, /* I - First style or NULL */
901 const char *second
, /* I - Second style of NULL */
902 FILE *fp
) /* I - File */
904 int i
= 0; /* Which style */
905 int quote
= 0; /* Saw quote? */
906 int dolinks
, /* Do hyperlinks to other man pages? */
907 link
= 0; /* Doing a link now? */
911 * Skip leading whitespace...
914 while (isspace(*s
& 255))
917 dolinks
= first
&& !strcmp(first
, "b") && !second
;
924 * See if we need to make a link to a man page...
927 const char *end
; /* End of current word */
928 const char *next
; /* Start of next word */
930 for (end
= s
; *end
&& !isspace(*end
& 255); end
++);
931 for (next
= end
; isspace(*next
& 255); next
++);
933 if (isalnum(*s
& 255) && *next
== '(')
936 * See if the man file is available locally...
939 char name
[1024], /* Name */
940 manfile
[1024], /* Man page filename */
941 manurl
[1024]; /* Man page URL */
943 strlcpy(name
, s
, sizeof(name
));
944 if ((size_t)(end
- s
) < sizeof(name
))
945 name
[end
- s
] = '\0';
947 snprintf(manfile
, sizeof(manfile
), "%s.man", name
);
948 snprintf(manurl
, sizeof(manurl
), "man-%s.html?TOPIC=Man+Pages", name
);
950 if (!access(manfile
, 0))
953 * Local man page, do a link...
956 fprintf(fp
, "<a href=\"%s\">", manurl
);
963 fprintf(fp
, "<%s>", first
);
964 else if (i
&& second
)
965 fprintf(fp
, "<%s>", second
);
967 while ((!isspace(*s
& 255) || quote
) && *s
)
972 if (*s
== '\\' && s
[1])
982 fprintf(fp
, "</%s>", first
);
983 else if (i
&& second
)
984 fprintf(fp
, "</%s>", second
);
995 * Skip trailing whitespace...
998 while (isspace(*s
& 255))
1006 * 'html_fputs()' - Output a string, quoting as needed HTML entities.
1010 html_fputs(const char *s
, /* I - String */
1011 int *font
, /* IO - Font */
1012 FILE *fp
) /* I - File */
1024 int newfont
; /* New font */
1054 fprintf(stderr
, "mantohtml: Unknown font \"\\f%c\" ignored.\n", s
[-1]);
1059 if (newfont
!= *font
)
1061 fputs(end_fonts
[*font
], fp
);
1063 fputs(start_fonts
[*font
], fp
);
1069 * Substitute macro...
1083 if (!strncmp(s
, "lq", 2))
1084 fputs("“", fp
);
1085 else if (!strncmp(s
, "rq", 2))
1086 fputs("”", fp
);
1087 else if (!strncmp(s
, "Tm", 2))
1088 fputs("<sup>TM</sup>", fp
);
1090 fprintf(stderr
, "mantohtml: Unknown macro \"\\*(%2s\" ignored.\n", s
);
1099 fprintf(stderr
, "mantohtml: Unknown macro \"\\*%c\" ignored.\n", s
[-1]);
1105 if (!strncmp(s
, "(em", 3))
1107 fputs("—", fp
);
1110 else if (!strncmp(s
, "(en", 3))
1112 fputs("–", fp
);
1124 * Substitute escaped character...
1128 if (!strncmp(s
, "co]", 3))
1129 fputs("©", fp
);
1130 else if (!strncmp(s
, "de]", 3))
1132 else if (!strncmp(s
, "rg]", 3))
1134 else if (!strncmp(s
, "tm]", 3))
1135 fputs("<sup>TM</sup>", fp
);
1144 else if (isdigit(s
[0]) && isdigit(s
[1]) &&
1147 fprintf(fp
, "&#%d;", ((s
[0] - '0') * 8 + s
[1] - '0') * 8 + s
[2] - '0');
1152 if (*s
!= '\\' && *s
!= '\"' && *s
!= '\'' && *s
!= '-')
1154 fprintf(stderr
, "mantohtml: Unrecognized escape \"\\%c\" ignored.\n", *s
);
1155 html_putc('\\', fp
);
1158 html_putc(*s
++, fp
);
1161 else if (!strncmp(s
, "http://", 7) || !strncmp(s
, "https://", 8) || !strncmp(s
, "ftp://", 6))
1167 char temp
[1024]; /* Temporary string */
1168 const char *end
= s
+ 6; /* End of URL */
1170 while (*end
&& !isspace(*end
& 255))
1173 if (end
[-1] == ',' || end
[-1] == '.' || end
[-1] == ')')
1176 strlcpy(temp
, s
, sizeof(temp
));
1177 if ((size_t)(end
-s
) < sizeof(temp
))
1178 temp
[end
- s
] = '\0';
1180 fprintf(fp
, "<a href=\"%s\">%s</a>", temp
, temp
);
1184 html_putc(*s
++ & 255, fp
);
1190 * 'html_putc()' - Put a single character, using entities as needed.
1194 html_putc(int ch
, /* I - Character */
1195 FILE *fp
) /* I - File */
1207 * 'strmove()' - Move characters within a string.
1211 strmove(char *d
, /* I - Destination */
1212 const char *s
) /* I - Source */