]>
git.ipfire.org Git - thirdparty/cups.git/blob - man/mantohtml.c
4 * Man page to HTML conversion program.
6 * Copyright 2007-2010, 2014 by Apple Inc.
7 * Copyright 2004-2006 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
17 * Include necessary headers.
20 #include <cups/string-private.h>
21 #include <cups/array-private.h>
29 static const char /* Start/end tags for fonts */
30 * const start_fonts
[] = { "", "<b>", "<i>" },
31 * const end_fonts
[] = { "", "</b>", "</i>" };
38 static void html_alternate(const char *s
, const char *first
, const char *second
, FILE *fp
);
39 static void html_fputs(const char *s
, int *font
, FILE *fp
);
40 static void html_putc(int ch
, FILE *fp
);
41 static void strmove(char *d
, const char *s
);
45 * 'main()' - Convert a man page to HTML.
48 int /* O - Exit status */
49 main(int argc
, /* I - Number of command-line args */
50 char *argv
[]) /* I - Command-line arguments */
52 FILE *infile
, /* Input file */
53 *outfile
; /* Output file */
54 char line
[1024], /* Line from file */
55 *lineptr
, /* Pointer into line */
56 name
[1024], /* Man page name */
57 ddpost
[256]; /* Tagged list post markup */
58 int section
= -1, /* Man page section */
59 pre
= 0, /* Preformatted */
60 font
= 0, /* Current font */
61 linenum
= 0; /* Current line number */
62 float list_indent
= 0.0f
, /* Current list indentation */
63 nested_indent
= 0.0f
; /* Nested list indentation, if any */
64 const char *list
= NULL
, /* Current list, if any */
65 *nested
= NULL
; /* Nested list, if any */
66 const char *post
= NULL
; /* Text to add after the current line */
75 fputs("Usage: mantohtml [filename.man [filename.html]]\n", stderr
);
80 * Open files as needed...
85 if ((infile
= fopen(argv
[1], "r")) == NULL
)
96 if ((outfile
= fopen(argv
[2], "w")) == NULL
)
107 * Read from input and write the output...
110 fputs("<!DOCTYPE HTML>\n"
112 "<!-- SECTION: Man Pages -->\n"
114 "\t<link rel=\"stylesheet\" type=\"text/css\" "
115 "href=\"../cups-printable.css\">\n", outfile
);
117 while (fgets(line
, sizeof(line
), infile
))
119 size_t linelen
= strlen(line
); /* Length of line */
121 if (linelen
> 0 && line
[linelen
- 1] == '\n')
122 line
[linelen
- 1] = '\0';
129 * Strip leading whitespace...
132 while (line
[1] == ' ' || line
[1] == '\t')
133 strmove(line
+ 1, line
+ 2);
136 * Process man page commands...
139 if (!strncmp(line
, ".TH ", 4) && section
< 0)
142 * Grab man page title...
145 sscanf(line
+ 4, "%s%d", name
, §ion
);
148 "\t<title>%s(%d)</title>\n"
151 "<h1 class=\"title\">%s(%d)</h1>\n"
153 name
, section
, name
, section
, start_fonts
[font
]);
155 else if (section
< 0)
157 else if (!strncmp(line
, ".SH ", 4) || !strncmp(line
, ".SS ", 4))
165 fputs(end_fonts
[font
], outfile
);
170 fprintf(outfile
, "</%s>\n", list
);
175 fputs("<h2 class=\"title\"><a name=\"", outfile
);
177 fputs("<h3><a name=\"", outfile
);
179 for (lineptr
= line
+ 4; *lineptr
; lineptr
++)
180 if (*lineptr
== '\"')
182 else if (isalnum(*lineptr
& 255))
183 html_putc(*lineptr
, outfile
);
185 html_putc('_', outfile
);
187 fputs("\">", outfile
);
189 for (lineptr
= line
+ 4; *lineptr
; lineptr
++)
191 if (*lineptr
== '\"')
193 else if (*lineptr
== ' ')
195 html_putc(' ', outfile
);
202 html_putc(*lineptr
, outfile
);
204 html_putc(tolower(*lineptr
& 255), outfile
);
211 fputs("</a></h2>\n", outfile
);
213 fputs("</a></h3>\n", outfile
);
215 else if (!strncmp(line
, ".B ", 3))
221 fputs(end_fonts
[font
], outfile
);
224 html_alternate(line
+ 3, "b", "b", outfile
);
228 fputs(post
, outfile
);
232 else if (!strncmp(line
, ".I ", 3))
235 * Grab italic text...
238 fputs(end_fonts
[font
], outfile
);
241 html_alternate(line
+ 3, "i", "i", outfile
);
245 fputs(post
, outfile
);
249 else if (!strncmp(line
, ".BI ", 4))
252 * Alternating bold and italic text...
255 fputs(end_fonts
[font
], outfile
);
258 html_alternate(line
+ 4, "b", "i", outfile
);
262 fputs(post
, outfile
);
266 else if (!strncmp(line
, ".BR ", 4))
269 * Alternating bold and roman (plain) text...
272 fputs(end_fonts
[font
], outfile
);
275 html_alternate(line
+ 4, "b", NULL
, outfile
);
279 fputs(post
, outfile
);
283 else if (!strncmp(line
, ".IB ", 4))
286 * Alternating italic and bold text...
289 fputs(end_fonts
[font
], outfile
);
292 html_alternate(line
+ 4, "i", "b", outfile
);
296 fputs(post
, outfile
);
300 else if (!strncmp(line
, ".IR ", 4))
303 * Alternating italic and roman (plain) text...
306 fputs(end_fonts
[font
], outfile
);
309 html_alternate(line
+ 4, "i", NULL
, outfile
);
313 fputs(post
, outfile
);
317 else if (!strncmp(line
, ".RB ", 4))
320 * Alternating roman (plain) and bold text...
323 fputs(end_fonts
[font
], outfile
);
326 html_alternate(line
+ 4, NULL
, "b", outfile
);
330 fputs(post
, outfile
);
334 else if (!strncmp(line
, ".RI ", 4))
337 * Alternating roman (plain) and italic text...
340 fputs(end_fonts
[font
], outfile
);
343 html_alternate(line
+ 4, NULL
, "i", outfile
);
347 fputs(post
, outfile
);
351 else if (!strncmp(line
, ".SB ", 4))
354 * Alternating small and bold text...
357 fputs(end_fonts
[font
], outfile
);
360 html_alternate(line
+ 4, "small", "b", outfile
);
364 fputs(post
, outfile
);
368 else if (!strncmp(line
, ".SM ", 4))
374 fputs(end_fonts
[font
], outfile
);
377 html_alternate(line
+ 4, "small", "small", outfile
);
381 fputs(post
, outfile
);
385 else if (!strcmp(line
, ".LP") || !strcmp(line
, ".PP") || !strcmp(line
, ".P"))
391 fputs(end_fonts
[font
], outfile
);
396 fprintf(outfile
, "</%s>\n", list
);
400 fputs("<p>", outfile
);
402 else if (!strcmp(line
, ".RS") || !strncmp(line
, ".RS ", 4))
408 float amount
= 3.0; /* Indentation */
411 amount
= atof(line
+ 4);
413 fputs(end_fonts
[font
], outfile
);
420 nested_indent
= list_indent
;
424 fprintf(outfile
, "<div style=\"margin-left: %.1fem;\">\n", amount
- nested_indent
);
426 else if (!strcmp(line
, ".RE"))
432 fputs(end_fonts
[font
], outfile
);
435 fputs("</div>\n", outfile
);
442 list_indent
= nested_indent
;
443 nested_indent
= 0.0f
;
446 else if (!strcmp(line
, ".HP") || !strncmp(line
, ".HP ", 4))
449 * Hanging paragraph...
454 float amount
= 3.0; /* Indentation */
457 amount
= atof(line
+ 4);
459 fputs(end_fonts
[font
], outfile
);
464 fprintf(outfile
, "</%s>\n", list
);
468 fprintf(outfile
, "<p style=\"margin-left: %.1fem; text-indent: %.1fem\">", amount
, -amount
);
473 else if (!strcmp(line
, ".TP") || !strncmp(line
, ".TP ", 4))
481 float amount
= 3.0; /* Indentation */
484 amount
= atof(line
+ 4);
486 fputs(end_fonts
[font
], outfile
);
489 if (list
&& strcmp(list
, "dl"))
491 fprintf(outfile
, "</%s>\n", list
);
497 fputs("<dl class=\"man\">\n", outfile
);
499 list_indent
= amount
;
502 fputs("<dt>", outfile
);
503 snprintf(ddpost
, sizeof(ddpost
), "<dd style=\"margin-left: %.1fem\">", amount
);
506 else if (!strncmp(line
, ".IP ", 4))
509 * Indented paragraph...
514 float amount
= 3.0; /* Indentation */
515 const char *newlist
= NULL
; /* New list style */
516 const char *newtype
= NULL
; /* New list numbering type */
518 fputs(end_fonts
[font
], outfile
);
522 while (isspace(*lineptr
& 255))
525 if (!strncmp(lineptr
, "\\(bu", 4) || !strncmp(lineptr
, "\\(em", 4))
533 else if (isdigit(*lineptr
& 255))
541 else if (islower(*lineptr
& 255))
544 * Lowercase alpha list...
550 else if (isupper(*lineptr
& 255))
553 * Lowercase alpha list...
560 while (!isspace(*lineptr
& 255))
562 while (isspace(*lineptr
& 255))
565 if (isdigit(*lineptr
& 255))
566 amount
= atof(lineptr
);
568 if (newlist
&& list
&& strcmp(newlist
, list
))
570 fprintf(outfile
, "</%s>\n", list
);
574 if (newlist
&& !list
)
577 fprintf(outfile
, "<%s type=\"%s\">\n", newlist
, newtype
);
579 fprintf(outfile
, "<%s>\n", newlist
);
585 fprintf(outfile
, "<li style=\"margin-left: %.1fem;\">", amount
);
587 fprintf(outfile
, "<p style=\"margin-left: %.1fem;\">", amount
);
589 else if (!strncmp(line
, ".br", 3))
595 fputs("<br>\n", outfile
);
597 else if (!strncmp(line
, ".de ", 4))
600 * Define macro - ignore...
603 while (fgets(line
, sizeof(line
), infile
))
607 if (!strncmp(line
, "..", 2))
611 else if (!strncmp(line
, ".ds ", 4) || !strncmp(line
, ".rm ", 4) ||
612 !strncmp(line
, ".tr ", 4) || !strncmp(line
, ".hy ", 4) ||
613 !strncmp(line
, ".IX ", 4) || !strncmp(line
, ".PD", 3) ||
614 !strncmp(line
, ".Sp", 3))
617 * Ignore unused commands...
620 else if (!strncmp(line
, ".Vb", 3) || !strncmp(line
, ".nf", 3) || !strncmp(line
, ".EX", 3))
623 * Start preformatted...
626 fputs(end_fonts
[font
], outfile
);
631 // fprintf(outfile, "</%s>\n", list);
636 fputs("<pre class=\"man\">\n", outfile
);
638 else if (!strncmp(line
, ".Ve", 3) || !strncmp(line
, ".fi", 3) || !strncmp(line
, ".EE", 3))
641 * End preformatted...
644 fputs(end_fonts
[font
], outfile
);
650 fputs("</pre>\n", outfile
);
653 else if (!strncmp(line
, ".\\}", 3))
656 * Ignore close block...
659 else if (!strncmp(line
, ".ie", 3) || !strncmp(line
, ".if", 3) ||
660 !strncmp(line
, ".el", 3))
663 * If/else - ignore...
666 if (strchr(line
, '{') != NULL
)
669 * Skip whole block...
672 while (fgets(line
, sizeof(line
), infile
))
676 if (strchr(line
, '}') != NULL
)
682 else if (!strncmp(line
, ". ", 4))
689 else if (strncmp(line
, ".\\\"", 3))
695 if ((lineptr
= strchr(line
, ' ')) != NULL
)
697 else if ((lineptr
= strchr(line
, '\n')) != NULL
)
700 fprintf(stderr
, "mantohtml: Unknown man page command \'%s\' on line %d.\n", line
, linenum
);
704 * Skip continuation lines...
707 lineptr
= line
+ strlen(line
) - 1;
708 if (lineptr
>= line
&& *lineptr
== '\\')
710 while (fgets(line
, sizeof(line
), infile
))
713 lineptr
= line
+ strlen(line
) - 2;
715 if (lineptr
< line
|| *lineptr
!= '\\')
723 * Process man page text...
726 html_fputs(line
, &font
, outfile
);
731 fputs(post
, outfile
);
737 fprintf(outfile
, "%s\n", end_fonts
[font
]);
742 fprintf(outfile
, "</%s>\n", list
);
747 "</html>\n", outfile
);
756 if (outfile
!= stdout
)
760 * Return with no errors...
768 * 'html_alternate()' - Alternate words between two styles of text.
772 html_alternate(const char *s
, /* I - String */
773 const char *first
, /* I - First style or NULL */
774 const char *second
, /* I - Second style of NULL */
775 FILE *fp
) /* I - File */
777 int i
= 0; /* Which style */
778 int quote
= 0; /* Saw quote? */
779 int dolinks
, /* Do hyperlinks to other man pages? */
780 link
= 0; /* Doing a link now? */
784 * Skip leading whitespace...
787 while (isspace(*s
& 255))
790 dolinks
= first
&& !strcmp(first
, "b") && !second
;
797 * See if we need to make a link to a man page...
800 const char *end
; /* End of current word */
801 const char *next
; /* Start of next word */
803 for (end
= s
; *end
&& !isspace(*end
& 255); end
++);
804 for (next
= end
; isspace(*next
& 255); next
++);
806 if (isalnum(*s
& 255) && *next
== '(')
809 * See if the man file is available locally...
812 char name
[1024], /* Name */
813 manfile
[1024], /* Man page filename */
814 manurl
[1024]; /* Man page URL */
816 strlcpy(name
, s
, sizeof(name
));
817 if ((size_t)(end
- s
) < sizeof(name
))
818 name
[end
- s
] = '\0';
820 snprintf(manfile
, sizeof(manfile
), "%s.man", name
);
821 snprintf(manurl
, sizeof(manurl
), "man-%s.html?TOPIC=Man+Pages", name
);
823 if (!access(manfile
, 0))
826 * Local man page, do a link...
829 fprintf(fp
, "<a href=\"%s\">", manurl
);
836 fprintf(fp
, "<%s>", first
);
837 else if (i
&& second
)
838 fprintf(fp
, "<%s>", second
);
840 while ((!isspace(*s
& 255) || quote
) && *s
)
844 else if (*s
== '\\' && s
[1])
854 fprintf(fp
, "</%s>", first
);
855 else if (i
&& second
)
856 fprintf(fp
, "</%s>", second
);
867 * Skip trailing whitespace...
870 while (isspace(*s
& 255))
878 * 'html_fputs()' - Output a string, quoting as needed HTML entities.
882 html_fputs(const char *s
, /* I - String */
883 int *font
, /* IO - Font */
884 FILE *fp
) /* I - File */
896 int newfont
; /* New font */
926 fprintf(stderr
, "mantohtml: Unknown font \"\\f%c\" ignored.\n", s
[-1]);
931 if (newfont
!= *font
)
933 fputs(end_fonts
[*font
], fp
);
935 fputs(start_fonts
[*font
], fp
);
941 * Substitute macro...
955 if (!strncmp(s
, "lq", 2))
956 fputs("“", fp
);
957 else if (!strncmp(s
, "rq", 2))
958 fputs("”", fp
);
959 else if (!strncmp(s
, "Tm", 2))
960 fputs("<sup>TM</sup>", fp
);
962 fprintf(stderr
, "mantohtml: Unknown macro \"\\*(%2s\" ignored.\n", s
);
971 fprintf(stderr
, "mantohtml: Unknown macro \"\\*%c\" ignored.\n", s
[-1]);
977 if (!strncmp(s
, "(em", 3))
979 fputs("—", fp
);
982 else if (!strncmp(s
, "(en", 3))
984 fputs("–", fp
);
996 * Substitute escaped character...
1000 if (!strncmp(s
, "co]", 3))
1001 fputs("©", fp
);
1002 else if (!strncmp(s
, "de]", 3))
1004 else if (!strncmp(s
, "rg]", 3))
1006 else if (!strncmp(s
, "tm]", 3))
1007 fputs("<sup>TM</sup>", fp
);
1016 else if (isdigit(s
[0]) && isdigit(s
[1]) &&
1019 fprintf(fp
, "&#%d;", ((s
[0] - '0') * 8 + s
[1] - '0') * 8 + s
[2] - '0');
1024 if (*s
!= '\\' && *s
== '\"' && *s
== '\'' && *s
== '-')
1025 fprintf(stderr
, "mantohtml: Unrecognized escape \"\\%c\" ignored.\n", *s
);
1027 html_putc(*s
++, fp
);
1030 else if (!strncmp(s
, "http://", 7) || !strncmp(s
, "https://", 8) || !strncmp(s
, "ftp://", 6))
1036 char temp
[1024]; /* Temporary string */
1037 const char *end
= s
+ 6; /* End of URL */
1039 while (*end
&& !isspace(*end
& 255))
1042 if (end
[-1] == ',' || end
[-1] == '.' || end
[-1] == ')')
1045 strlcpy(temp
, s
, sizeof(temp
));
1046 if ((size_t)(end
-s
) < sizeof(temp
))
1047 temp
[end
- s
] = '\0';
1049 fprintf(fp
, "<a href=\"%s\">%s</a>", temp
, temp
);
1053 html_putc(*s
++ & 255, fp
);
1059 * 'html_putc()' - Put a single character, using entities as needed.
1063 html_putc(int ch
, /* I - Character */
1064 FILE *fp
) /* I - File */
1076 * 'strmove()' - Move characters within a string.
1080 strmove(char *d
, /* I - Destination */
1081 const char *s
) /* I - Source */