]>
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 const char *list
= NULL
; /* Current list, if any */
63 const char *post
= NULL
; /* Text to add after the current line */
72 fputs("Usage: mantohtml [filename.man [filename.html]]\n", stderr
);
77 * Open files as needed...
82 if ((infile
= fopen(argv
[1], "r")) == NULL
)
93 if ((outfile
= fopen(argv
[2], "w")) == NULL
)
104 * Read from input and write the output...
107 fputs("<!DOCTYPE HTML>\n"
109 "<!-- SECTION: Man Pages -->\n"
111 "\t<link rel=\"stylesheet\" type=\"text/css\" "
112 "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
);
176 for (lineptr
= line
+ 4; *lineptr
; lineptr
++)
177 if (*lineptr
== '\"')
179 else if (isalnum(*lineptr
& 255))
180 html_putc(*lineptr
, outfile
);
182 html_putc('_', outfile
);
184 fputs("\">", outfile
);
186 for (lineptr
= line
+ 4; *lineptr
; lineptr
++)
188 if (*lineptr
== '\"')
190 else if (*lineptr
== ' ')
192 html_putc(' ', outfile
);
199 html_putc(*lineptr
, outfile
);
201 html_putc(tolower(*lineptr
& 255), outfile
);
208 fputs("</a></h2>\n", outfile
);
210 fputs("</a></h3>\n", outfile
);
212 else if (!strncmp(line
, ".B ", 3))
218 fputs(end_fonts
[font
], outfile
);
221 html_alternate(line
+ 3, "b", "b", outfile
);
225 fputs(post
, outfile
);
229 else if (!strncmp(line
, ".I ", 3))
232 * Grab italic text...
235 fputs(end_fonts
[font
], outfile
);
238 html_alternate(line
+ 3, "i", "i", outfile
);
242 fputs(post
, outfile
);
246 else if (!strncmp(line
, ".BI ", 4))
249 * Alternating bold and italic text...
252 fputs(end_fonts
[font
], outfile
);
255 html_alternate(line
+ 4, "b", "i", outfile
);
259 fputs(post
, outfile
);
263 else if (!strncmp(line
, ".BR ", 4))
266 * Alternating bold and roman (plain) text...
269 fputs(end_fonts
[font
], outfile
);
272 html_alternate(line
+ 4, "b", NULL
, outfile
);
276 fputs(post
, outfile
);
280 else if (!strncmp(line
, ".IB ", 4))
283 * Alternating italic and bold text...
286 fputs(end_fonts
[font
], outfile
);
289 html_alternate(line
+ 4, "i", "b", outfile
);
293 fputs(post
, outfile
);
297 else if (!strncmp(line
, ".IR ", 4))
300 * Alternating italic and roman (plain) text...
303 fputs(end_fonts
[font
], outfile
);
306 html_alternate(line
+ 4, "i", NULL
, outfile
);
310 fputs(post
, outfile
);
314 else if (!strncmp(line
, ".RB ", 4))
317 * Alternating roman (plain) and bold text...
320 fputs(end_fonts
[font
], outfile
);
323 html_alternate(line
+ 4, NULL
, "b", outfile
);
327 fputs(post
, outfile
);
331 else if (!strncmp(line
, ".RI ", 4))
334 * Alternating roman (plain) and italic text...
337 fputs(end_fonts
[font
], outfile
);
340 html_alternate(line
+ 4, NULL
, "i", outfile
);
344 fputs(post
, outfile
);
348 else if (!strncmp(line
, ".SB ", 4))
351 * Alternating small and bold text...
354 fputs(end_fonts
[font
], outfile
);
357 html_alternate(line
+ 4, "small", "b", outfile
);
361 fputs(post
, outfile
);
365 else if (!strncmp(line
, ".SM ", 4))
371 fputs(end_fonts
[font
], outfile
);
374 html_alternate(line
+ 4, "small", "small", outfile
);
378 fputs(post
, outfile
);
382 else if (!strcmp(line
, ".LP") || !strcmp(line
, ".PP") || !strcmp(line
, ".P"))
388 fputs(end_fonts
[font
], outfile
);
393 fprintf(outfile
, "</%s>\n", list
);
397 fputs("<p>", outfile
);
399 else if (!strcmp(line
, ".RS") || !strncmp(line
, ".RS ", 4))
405 float amount
= 3.0; /* Indentation */
408 amount
= atof(line
+ 4);
410 fputs(end_fonts
[font
], outfile
);
415 fprintf(outfile
, "</%s>\n", list
);
419 fprintf(outfile
, "<div style=\"margin-left: %.1fem;\">\n", amount
);
421 else if (!strcmp(line
, ".RE"))
427 fputs(end_fonts
[font
], outfile
);
430 fputs("</div>\n", outfile
);
432 else if (!strcmp(line
, ".HP") || !strncmp(line
, ".HP ", 4))
435 * Hanging paragraph...
440 float amount
= 3.0; /* Indentation */
443 amount
= atof(line
+ 4);
445 fputs(end_fonts
[font
], outfile
);
450 fprintf(outfile
, "</%s>\n", list
);
454 fprintf(outfile
, "<p style=\"margin-left: %.1fem; text-indent: %.1fem\">", amount
, -amount
);
459 else if (!strcmp(line
, ".TP") || !strncmp(line
, ".TP ", 4))
467 float amount
= 3.0; /* Indentation */
470 amount
= atof(line
+ 4);
472 fputs(end_fonts
[font
], outfile
);
475 if (list
&& strcmp(list
, "dl"))
477 fprintf(outfile
, "</%s>\n", list
);
483 fputs("<dl class=\"man\">\n", outfile
);
487 fputs("<dt>", outfile
);
488 snprintf(ddpost
, sizeof(ddpost
), "<dd style=\"margin-left: %.1fem\">", amount
);
491 else if (!strncmp(line
, ".IP ", 4))
494 * Indented paragraph...
499 float amount
= 3.0; /* Indentation */
500 const char *newlist
= NULL
; /* New list style */
501 const char *newtype
= NULL
; /* New list numbering type */
503 fputs(end_fonts
[font
], outfile
);
507 while (isspace(*lineptr
& 255))
510 if (!strncmp(lineptr
, "\\(bu", 4) || !strncmp(lineptr
, "\\(em", 4))
518 else if (isdigit(*lineptr
& 255))
526 else if (islower(*lineptr
& 255))
529 * Lowercase alpha list...
535 else if (isupper(*lineptr
& 255))
538 * Lowercase alpha list...
545 while (!isspace(*lineptr
& 255))
547 while (isspace(*lineptr
& 255))
550 if (isdigit(*lineptr
& 255))
551 amount
= atof(lineptr
);
553 if (newlist
&& list
&& strcmp(newlist
, list
))
555 fprintf(outfile
, "</%s>\n", list
);
559 if (newlist
&& !list
)
562 fprintf(outfile
, "<%s type=\"%s\">\n", newlist
, newtype
);
564 fprintf(outfile
, "<%s>\n", newlist
);
570 fprintf(outfile
, "<li style=\"margin-left: %.1fem;\">", amount
);
572 fprintf(outfile
, "<p style=\"margin-left: %.1fem;\">", amount
);
574 else if (!strncmp(line
, ".br", 3))
580 fputs("<br>\n", outfile
);
582 else if (!strncmp(line
, ".de ", 4))
585 * Define macro - ignore...
588 while (fgets(line
, sizeof(line
), infile
))
592 if (!strncmp(line
, "..", 2))
596 else if (!strncmp(line
, ".ds ", 4) || !strncmp(line
, ".rm ", 4) ||
597 !strncmp(line
, ".tr ", 4) || !strncmp(line
, ".hy ", 4) ||
598 !strncmp(line
, ".IX ", 4) || !strncmp(line
, ".PD", 3) ||
599 !strncmp(line
, ".Sp", 3))
602 * Ignore unused commands...
605 else if (!strncmp(line
, ".Vb", 3) || !strncmp(line
, ".nf", 3) || !strncmp(line
, ".EX", 3))
608 * Start preformatted...
611 fputs(end_fonts
[font
], outfile
);
616 fprintf(outfile
, "</%s>\n", list
);
621 fputs("<pre class=\"man\">\n", outfile
);
623 else if (!strncmp(line
, ".Ve", 3) || !strncmp(line
, ".fi", 3) || !strncmp(line
, ".EE", 3))
626 * End preformatted...
629 fputs(end_fonts
[font
], outfile
);
635 fputs("</pre>\n", outfile
);
638 else if (!strncmp(line
, ".\\}", 3))
641 * Ignore close block...
644 else if (!strncmp(line
, ".ie", 3) || !strncmp(line
, ".if", 3) ||
645 !strncmp(line
, ".el", 3))
648 * If/else - ignore...
651 if (strchr(line
, '{') != NULL
)
654 * Skip whole block...
657 while (fgets(line
, sizeof(line
), infile
))
661 if (strchr(line
, '}') != NULL
)
667 else if (!strncmp(line
, ". ", 4))
674 else if (strncmp(line
, ".\\\"", 3))
680 if ((lineptr
= strchr(line
, ' ')) != NULL
)
682 else if ((lineptr
= strchr(line
, '\n')) != NULL
)
685 fprintf(stderr
, "mantohtml: Unknown man page command \'%s\' on line %d.\n", line
, linenum
);
689 * Skip continuation lines...
692 lineptr
= line
+ strlen(line
) - 1;
693 if (lineptr
>= line
&& *lineptr
== '\\')
695 while (fgets(line
, sizeof(line
), infile
))
698 lineptr
= line
+ strlen(line
) - 2;
700 if (lineptr
< line
|| *lineptr
!= '\\')
708 * Process man page text...
711 html_fputs(line
, &font
, outfile
);
716 fputs(post
, outfile
);
722 fprintf(outfile
, "%s\n", end_fonts
[font
]);
727 fprintf(outfile
, "</%s>\n", list
);
732 "</html>\n", outfile
);
741 if (outfile
!= stdout
)
745 * Return with no errors...
753 * 'html_alternate()' - Alternate words between two styles of text.
757 html_alternate(const char *s
, /* I - String */
758 const char *first
, /* I - First style or NULL */
759 const char *second
, /* I - Second style of NULL */
760 FILE *fp
) /* I - File */
762 int i
= 0; /* Which style */
763 int quote
= 0; /* Saw quote? */
764 int dolinks
, /* Do hyperlinks to other man pages? */
765 link
= 0; /* Doing a link now? */
769 * Skip leading whitespace...
772 while (isspace(*s
& 255))
775 dolinks
= first
&& !strcmp(first
, "b") && !second
;
782 * See if we need to make a link to a man page...
785 const char *end
; /* End of current word */
786 const char *next
; /* Start of next word */
788 for (end
= s
; *end
&& !isspace(*end
& 255); end
++);
789 for (next
= end
; isspace(*next
& 255); next
++);
791 if (isalnum(*s
& 255) && *next
== '(')
794 * See if the man file is available locally...
797 char name
[1024], /* Name */
798 manfile
[1024], /* Man page filename */
799 manurl
[1024]; /* Man page URL */
801 strlcpy(name
, s
, sizeof(name
));
802 if ((size_t)(end
- s
) < sizeof(name
))
803 name
[end
- s
] = '\0';
805 snprintf(manfile
, sizeof(manfile
), "%s.man", name
);
806 snprintf(manurl
, sizeof(manurl
), "man-%s.html?TOPIC=Man+Pages", name
);
808 if (!access(manfile
, 0))
811 * Local man page, do a link...
814 fprintf(fp
, "<a href=\"%s\">", manurl
);
821 fprintf(fp
, "<%s>", first
);
822 else if (i
&& second
)
823 fprintf(fp
, "<%s>", second
);
825 while ((!isspace(*s
& 255) || quote
) && *s
)
829 else if (*s
== '\\' && s
[1])
839 fprintf(fp
, "</%s>", first
);
840 else if (i
&& second
)
841 fprintf(fp
, "</%s>", second
);
852 * Skip trailing whitespace...
855 while (isspace(*s
& 255))
863 * 'html_fputs()' - Output a string, quoting as needed HTML entities.
867 html_fputs(const char *s
, /* I - String */
868 int *font
, /* IO - Font */
869 FILE *fp
) /* I - File */
881 int newfont
; /* New font */
911 fprintf(stderr
, "mantohtml: Unknown font \"\\f%c\" ignored.\n", s
[-1]);
916 if (newfont
!= *font
)
918 fputs(end_fonts
[*font
], fp
);
920 fputs(start_fonts
[*font
], fp
);
926 * Substitute macro...
940 if (!strncmp(s
, "lq", 2))
941 fputs("“", fp
);
942 else if (!strncmp(s
, "rq", 2))
943 fputs("”", fp
);
944 else if (!strncmp(s
, "Tm", 2))
945 fputs("<sup>TM</sup>", fp
);
947 fprintf(stderr
, "mantohtml: Unknown macro \"\\*(%2s\" ignored.\n", s
);
956 fprintf(stderr
, "mantohtml: Unknown macro \"\\*%c\" ignored.\n", s
[-1]);
962 if (!strncmp(s
, "(em", 3))
964 fputs("—", fp
);
967 else if (!strncmp(s
, "(en", 3))
969 fputs("–", fp
);
981 * Substitute escaped character...
985 if (!strncmp(s
, "co]", 3))
987 else if (!strncmp(s
, "de]", 3))
989 else if (!strncmp(s
, "rg]", 3))
991 else if (!strncmp(s
, "tm]", 3))
992 fputs("<sup>TM</sup>", fp
);
1001 else if (isdigit(s
[0]) && isdigit(s
[1]) &&
1004 fprintf(fp
, "&#%d;", ((s
[0] - '0') * 8 + s
[1] - '0') * 8 + s
[2] - '0');
1009 if (*s
!= '\\' && *s
== '\"' && *s
== '\'' && *s
== '-')
1010 fprintf(stderr
, "mantohtml: Unrecognized escape \"\\%c\" ignored.\n", *s
);
1012 html_putc(*s
++, fp
);
1015 else if (!strncmp(s
, "http://", 7) || !strncmp(s
, "https://", 8) || !strncmp(s
, "ftp://", 6))
1021 char temp
[1024]; /* Temporary string */
1022 const char *end
= s
+ 6; /* End of URL */
1024 while (*end
&& !isspace(*end
& 255))
1027 if (end
[-1] == ',' || end
[-1] == '.' || end
[-1] == ')')
1030 strlcpy(temp
, s
, sizeof(temp
));
1031 if ((size_t)(end
-s
) < sizeof(temp
))
1032 temp
[end
- s
] = '\0';
1034 fprintf(fp
, "<a href=\"%s\">%s</a>", temp
, temp
);
1038 html_putc(*s
++ & 255, fp
);
1044 * 'html_putc()' - Put a single character, using entities as needed.
1048 html_putc(int ch
, /* I - Character */
1049 FILE *fp
) /* I - File */
1061 * 'strmove()' - Move characters within a string.
1065 strmove(char *d
, /* I - Destination */
1066 const char *s
) /* I - Source */