]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
bc44d920 | 2 | * "$Id: mantohtml.c 6649 2007-07-11 21:46:42Z mike $" |
ef416fc2 | 3 | * |
4 | * Man page to HTML conversion program. | |
5 | * | |
bc44d920 | 6 | * Copyright 2007 by Apple Inc. |
bd7854cb | 7 | * Copyright 2004-2006 by Easy Software Products. |
ef416fc2 | 8 | * |
9 | * These coded instructions, statements, and computer programs are the | |
bc44d920 | 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/". | |
ef416fc2 | 14 | * |
15 | * Contents: | |
16 | * | |
17 | * main() - Convert a man page to HTML. | |
18 | * putc_entity() - Put a single character, using entities as needed. | |
19 | * strmove() - Move characters within a string. | |
20 | */ | |
21 | ||
22 | /* | |
23 | * Include necessary headers. | |
24 | */ | |
25 | ||
26 | #include <cups/string.h> | |
27 | #include <stdlib.h> | |
ecdc0628 | 28 | #include <unistd.h> |
ef416fc2 | 29 | |
30 | ||
31 | /* | |
32 | * Local functions... | |
33 | */ | |
34 | ||
35 | static void putc_entity(int ch, FILE *fp); | |
36 | static void strmove(char *d, const char *s); | |
37 | ||
38 | ||
39 | /* | |
40 | * 'main()' - Convert a man page to HTML. | |
41 | */ | |
42 | ||
43 | int /* O - Exit status */ | |
44 | main(int argc, /* I - Number of command-line args */ | |
45 | char *argv[]) /* I - Command-line arguments */ | |
46 | { | |
47 | FILE *infile, /* Input file */ | |
48 | *outfile; /* Output file */ | |
49 | char line[1024], /* Line from file */ | |
50 | *lineptr, /* Pointer into line */ | |
bd7854cb | 51 | *endptr, /* Pointer to end of current */ |
52 | endchar, /* End character */ | |
53 | *paren, /* Pointer to parenthesis */ | |
ef416fc2 | 54 | name[1024]; /* Man page name */ |
55 | int section, /* Man page section */ | |
56 | pre, /* Preformatted */ | |
57 | font, /* Current font */ | |
58 | blist, /* In a bullet list? */ | |
59 | list, /* In a list? */ | |
60 | linenum; /* Current line number */ | |
61 | const char *post; /* Text to add after the current line */ | |
62 | static const char /* Start/end tags for fonts */ | |
63 | * const start_fonts[] = { "", "<b>", "<i>" }, | |
64 | * const end_fonts[] = { "", "</b>", "</i>" }; | |
65 | ||
66 | /* | |
67 | * Check arguments... | |
68 | */ | |
69 | ||
70 | if (argc > 3) | |
71 | { | |
72 | fputs("Usage: mantohtml [filename.man [filename.html]]\n", stderr); | |
73 | return (1); | |
74 | } | |
75 | ||
76 | /* | |
77 | * Open files as needed... | |
78 | */ | |
79 | ||
80 | if (argc > 1) | |
81 | { | |
82 | if ((infile = fopen(argv[1], "r")) == NULL) | |
83 | { | |
84 | perror(argv[1]); | |
85 | return (1); | |
86 | } | |
87 | } | |
88 | else | |
89 | infile = stdin; | |
90 | ||
91 | if (argc > 2) | |
92 | { | |
93 | if ((outfile = fopen(argv[2], "w")) == NULL) | |
94 | { | |
95 | perror(argv[2]); | |
96 | fclose(infile); | |
97 | return (1); | |
98 | } | |
99 | } | |
100 | else | |
101 | outfile = stdout; | |
102 | ||
103 | /* | |
104 | * Read from input and write the output... | |
105 | */ | |
106 | ||
107 | fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" " | |
108 | "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n" | |
109 | "<html>\n" | |
110 | "<!-- SECTION: Man Pages -->\n" | |
111 | "<head>\n" | |
112 | "\t<style type='text/css'><!--\n" | |
113 | "\th1, h2, h3, p { font-family: sans-serif; text-align: justify; }\n" | |
114 | "\ttt, pre a:link, pre a:visited, tt a:link, tt a:visited { font-weight: bold; color: #7f0000; }\n" | |
115 | "\tpre { font-weight: bold; color: #7f0000; margin-left: 2em; }\n" | |
116 | "\th1.title, h2.title, h3.title { border-bottom: solid 2px #000000; }\n" | |
117 | "\t--></style>\n", outfile); | |
118 | ||
119 | blist = 0; | |
120 | font = 0; | |
121 | list = 0; | |
122 | linenum = 0; | |
123 | pre = 0; | |
124 | post = NULL; | |
125 | section = -1; | |
126 | ||
127 | while (fgets(line, sizeof(line), infile)) | |
128 | { | |
129 | linenum ++; | |
130 | ||
131 | if (line[0] == '.') | |
132 | { | |
133 | /* | |
134 | * Strip leading whitespace... | |
135 | */ | |
136 | ||
137 | while (line[1] == ' ' || line[1] == '\t') | |
138 | strmove(line + 1, line + 2); | |
139 | ||
140 | /* | |
141 | * Process man page commands... | |
142 | */ | |
143 | ||
144 | if (!strncmp(line, ".TH ", 4) && section < 0) | |
145 | { | |
146 | /* | |
147 | * Grab man page title... | |
148 | */ | |
149 | ||
150 | sscanf(line + 4, "%s%d", name, §ion); | |
151 | ||
152 | fprintf(outfile, | |
bd7854cb | 153 | "\t<title>%s(%d)</title>\n" |
ef416fc2 | 154 | "</head>\n" |
155 | "<body>\n" | |
ef416fc2 | 156 | "%s", |
bd7854cb | 157 | name, section, start_fonts[font]); |
ef416fc2 | 158 | } |
159 | else if (section < 0) | |
160 | continue; | |
dd1abb6b | 161 | else if (!strncmp(line, ".SH ", 4) || !strncmp(line, ".SS ", 4)) |
ef416fc2 | 162 | { |
163 | /* | |
164 | * Grab heading... | |
165 | */ | |
166 | ||
167 | int first = 1; | |
168 | ||
169 | fputs(end_fonts[font], outfile); | |
170 | ||
171 | if (blist) | |
172 | { | |
173 | fputs("</li>\n</ul>\n", outfile); | |
174 | blist = 0; | |
175 | } | |
176 | ||
177 | if (list) | |
178 | { | |
179 | if (list == 1) | |
180 | fputs("</dt>\n", outfile); | |
181 | else if (list) | |
182 | fputs("</dd>\n", outfile); | |
183 | ||
184 | fputs("</dl>\n", outfile); | |
185 | list = 0; | |
186 | } | |
187 | ||
bd7854cb | 188 | line[strlen(line) - 1] = '\0'; /* Strip LF */ |
189 | ||
ef416fc2 | 190 | if (line[2] == 'H') |
bd7854cb | 191 | fputs("<h2><a name='", outfile); |
ef416fc2 | 192 | else |
bd7854cb | 193 | fputs("<h3><a name='", outfile); |
194 | ||
195 | for (lineptr = line + 4; *lineptr; lineptr ++) | |
196 | if (*lineptr == '\"') | |
197 | continue; | |
198 | else if (*lineptr == ' ') | |
199 | putc_entity('_', outfile); | |
200 | else | |
201 | putc_entity(*lineptr, outfile); | |
202 | ||
203 | fputs("'>", outfile); | |
ef416fc2 | 204 | |
205 | for (lineptr = line + 4; *lineptr; lineptr ++) | |
206 | if (*lineptr == '\"') | |
207 | continue; | |
208 | else if (*lineptr == ' ') | |
209 | { | |
210 | putc_entity(' ', outfile); | |
211 | ||
212 | first = 1; | |
213 | } | |
bd7854cb | 214 | else |
ef416fc2 | 215 | { |
216 | if (first) | |
bd7854cb | 217 | putc_entity(*lineptr, outfile); |
ef416fc2 | 218 | else |
219 | putc_entity(tolower(*lineptr), outfile); | |
220 | ||
221 | first = 0; | |
222 | } | |
223 | ||
224 | if (line[2] == 'H') | |
bd7854cb | 225 | fprintf(outfile, "</a></h2>\n%s", start_fonts[font]); |
ef416fc2 | 226 | else |
bd7854cb | 227 | fprintf(outfile, "</a></h3>\n%s", start_fonts[font]); |
ef416fc2 | 228 | } |
229 | else if (!strncmp(line, ".LP", 3) || !strncmp(line, ".PP", 3)) | |
230 | { | |
231 | /* | |
232 | * New paragraph... | |
233 | */ | |
234 | ||
235 | fputs(end_fonts[font], outfile); | |
236 | ||
237 | if (blist) | |
238 | { | |
239 | fputs("</li>\n</ul>\n", outfile); | |
240 | blist = 0; | |
241 | } | |
242 | ||
243 | if (list) | |
244 | { | |
245 | if (list == 1) | |
246 | fputs("</dt>\n", outfile); | |
247 | else if (list) | |
248 | fputs("</dd>\n", outfile); | |
249 | ||
250 | fputs("</dl>\n", outfile); | |
251 | list = 0; | |
252 | } | |
253 | ||
254 | fputs("<p>", outfile); | |
255 | font = 0; | |
256 | } | |
257 | else if (!strncmp(line, ".TP ", 4)) | |
258 | { | |
259 | /* | |
260 | * Grab list... | |
261 | */ | |
262 | ||
263 | fputs(end_fonts[font], outfile); | |
264 | ||
265 | if (blist) | |
266 | { | |
267 | fputs("</li>\n</ul>\n", outfile); | |
268 | blist = 0; | |
269 | } | |
270 | ||
271 | if (!list) | |
272 | fputs("<dl>\n", outfile); | |
273 | else if (list == 1) | |
274 | fputs("</dt>\n", outfile); | |
275 | else if (list) | |
276 | fputs("</dd>\n", outfile); | |
277 | ||
278 | fputs("<dt>", outfile); | |
279 | list = 1; | |
280 | font = 0; | |
281 | } | |
282 | else if (!strncmp(line, ".br", 3)) | |
283 | { | |
284 | /* | |
285 | * Grab line break... | |
286 | */ | |
287 | ||
288 | if (list == 1) | |
289 | { | |
290 | fputs("</dt>\n<dd>", outfile); | |
291 | list = 2; | |
292 | } | |
293 | else if (list) | |
294 | fputs("</dd>\n<dd>", outfile); | |
295 | else | |
296 | fputs("<br>\n", outfile); | |
297 | } | |
298 | else if (!strncmp(line, ".de ", 4)) | |
299 | { | |
300 | /* | |
301 | * Define macro - ignore... | |
302 | */ | |
303 | ||
304 | while (fgets(line, sizeof(line), infile)) | |
305 | { | |
306 | linenum ++; | |
307 | ||
308 | if (!strncmp(line, "..", 2)) | |
309 | break; | |
310 | } | |
311 | } | |
312 | else if (!strncmp(line, ".RS", 3)) | |
313 | { | |
314 | /* | |
315 | * Indent... | |
316 | */ | |
317 | ||
318 | fputs("<div style='margin-left: 3em;'>\n", outfile); | |
319 | } | |
320 | else if (!strncmp(line, ".RE", 3)) | |
321 | { | |
322 | /* | |
323 | * Unindent... | |
324 | */ | |
325 | ||
326 | fputs("</div>\n", outfile); | |
327 | } | |
328 | else if (!strncmp(line, ".ds ", 4) || !strncmp(line, ".rm ", 4) || | |
329 | !strncmp(line, ".tr ", 4) || !strncmp(line, ".hy ", 4) || | |
330 | !strncmp(line, ".IX ", 4) || !strncmp(line, ".PD", 3) || | |
331 | !strncmp(line, ".Sp", 3)) | |
332 | { | |
333 | /* | |
334 | * Ignore unused commands... | |
335 | */ | |
336 | } | |
337 | else if (!strncmp(line, ".Vb", 3) || !strncmp(line, ".nf", 3)) | |
338 | { | |
339 | /* | |
340 | * Start preformatted... | |
341 | */ | |
342 | ||
343 | pre = 1; | |
344 | fputs("<pre>\n", outfile); | |
345 | } | |
346 | else if (!strncmp(line, ".Ve", 3) || !strncmp(line, ".fi", 3)) | |
347 | { | |
348 | /* | |
349 | * End preformatted... | |
350 | */ | |
351 | ||
352 | if (pre) | |
353 | { | |
354 | pre = 0; | |
355 | fputs("</pre>\n", outfile); | |
356 | } | |
357 | } | |
358 | else if (!strncmp(line, ".IP \\(bu", 8)) | |
359 | { | |
360 | /* | |
361 | * Bullet list... | |
362 | */ | |
363 | ||
364 | if (blist) | |
365 | fputs("</li>\n", outfile); | |
366 | else | |
367 | { | |
368 | fputs("<ul>\n", outfile); | |
369 | blist = 1; | |
370 | } | |
371 | ||
372 | fputs("<li>", outfile); | |
373 | } | |
374 | else if (!strncmp(line, ".IP ", 4)) | |
375 | { | |
376 | /* | |
377 | * Indented paragraph... | |
378 | */ | |
379 | ||
380 | if (blist) | |
381 | { | |
382 | fputs("</li>\n</ul>\n", outfile); | |
383 | blist = 0; | |
384 | } | |
385 | ||
386 | fputs("<p style='margin-left: 3em;'>", outfile); | |
387 | ||
388 | for (lineptr = line + 4; isspace(*lineptr); lineptr ++); | |
389 | ||
390 | if (*lineptr == '\"') | |
391 | { | |
392 | strmove(line, lineptr + 1); | |
393 | ||
394 | if ((lineptr = strchr(line, '\"')) != NULL) | |
395 | *lineptr = '\0'; | |
396 | } | |
397 | else | |
398 | { | |
399 | strmove(line, lineptr); | |
400 | ||
401 | if ((lineptr = strchr(line, ' ')) != NULL) | |
402 | *lineptr = '\0'; | |
403 | } | |
404 | ||
405 | /* | |
406 | * Process the text as if it was in-line... | |
407 | */ | |
408 | ||
409 | post = "\n<br />\n<br />"; | |
410 | goto process_text; | |
411 | } | |
412 | else if (!strncmp(line, ".\\}", 3)) | |
413 | { | |
414 | /* | |
415 | * Ignore close block... | |
416 | */ | |
417 | } | |
418 | else if (!strncmp(line, ".ie", 3) || !strncmp(line, ".if", 3) || | |
419 | !strncmp(line, ".el", 3)) | |
420 | { | |
421 | /* | |
422 | * If/else - ignore... | |
423 | */ | |
424 | ||
425 | if (strchr(line, '{') != NULL) | |
426 | { | |
427 | /* | |
428 | * Skip whole block... | |
429 | */ | |
430 | ||
431 | while (fgets(line, sizeof(line), infile)) | |
432 | { | |
433 | linenum ++; | |
434 | ||
435 | if (strchr(line, '}') != NULL) | |
436 | break; | |
437 | } | |
438 | } | |
439 | } | |
440 | #if 0 | |
441 | else if (!strncmp(line, ". ", 4)) | |
442 | { | |
443 | /* | |
444 | * Grab ... | |
445 | */ | |
446 | } | |
447 | #endif /* 0 */ | |
448 | else if (!strncmp(line, ".B ", 3)) | |
449 | { | |
450 | /* | |
451 | * Grab bold text... | |
452 | */ | |
453 | ||
454 | fprintf(outfile, "%s<b>%s</b>%s", end_fonts[font], line + 3, | |
455 | start_fonts[font]); | |
456 | } | |
457 | else if (!strncmp(line, ".I ", 3)) | |
458 | { | |
459 | /* | |
460 | * Grab italic text... | |
461 | */ | |
462 | ||
463 | fprintf(outfile, "%s<i>%s</i>%s", end_fonts[font], line + 3, | |
464 | start_fonts[font]); | |
465 | } | |
466 | else if (strncmp(line, ".\\\"", 3)) | |
467 | { | |
468 | /* | |
469 | * Unknown... | |
470 | */ | |
471 | ||
472 | if ((lineptr = strchr(line, ' ')) != NULL) | |
473 | *lineptr = '\0'; | |
474 | else if ((lineptr = strchr(line, '\n')) != NULL) | |
475 | *lineptr = '\0'; | |
476 | ||
477 | fprintf(stderr, "mantohtml: Unknown man page command \'%s\' on line %d!\n", | |
478 | line, linenum); | |
479 | } | |
480 | ||
481 | /* | |
482 | * Skip continuation lines... | |
483 | */ | |
484 | ||
485 | lineptr = line + strlen(line) - 2; | |
486 | if (lineptr >= line && *lineptr == '\\') | |
487 | { | |
488 | while (fgets(line, sizeof(line), infile)) | |
489 | { | |
490 | linenum ++; | |
491 | lineptr = line + strlen(line) - 2; | |
492 | ||
493 | if (lineptr < line || *lineptr != '\\') | |
494 | break; | |
495 | } | |
496 | } | |
497 | } | |
498 | else | |
499 | { | |
500 | /* | |
501 | * Process man page text... | |
502 | */ | |
503 | ||
504 | process_text: | |
505 | ||
506 | for (lineptr = line; *lineptr; lineptr ++) | |
507 | { | |
bd7854cb | 508 | if (!strncmp(lineptr, "http://", 7)) |
509 | { | |
510 | /* | |
511 | * Embed URL... | |
512 | */ | |
513 | ||
514 | for (endptr = lineptr + 7; | |
515 | *endptr && !isspace(*endptr & 255); | |
516 | endptr ++); | |
517 | ||
518 | endchar = *endptr; | |
519 | *endptr = '\0'; | |
520 | ||
521 | fprintf(outfile, "<a href='%s'>%s</a>", lineptr, lineptr); | |
522 | *endptr = endchar; | |
523 | lineptr = endptr - 1; | |
524 | } | |
525 | else if (!strncmp(lineptr, "\\fI", 3) && | |
526 | (endptr = strstr(lineptr, "\\fR")) != NULL && | |
527 | (paren = strchr(lineptr, '(')) != NULL && | |
528 | paren < endptr) | |
529 | { | |
530 | /* | |
531 | * Link to man page? | |
532 | */ | |
533 | ||
534 | char manfile[1024], /* Man page filename */ | |
535 | manurl[1024]; /* Man page URL */ | |
536 | ||
537 | ||
538 | /* | |
539 | * See if the man file is available locally... | |
540 | */ | |
541 | ||
542 | lineptr += 3; | |
543 | endchar = *paren; | |
544 | *paren = '\0'; | |
545 | ||
546 | snprintf(manfile, sizeof(manfile), "%s.man", lineptr); | |
547 | snprintf(manurl, sizeof(manurl), "man-%s.html?TOPIC=Man+Pages", | |
548 | lineptr); | |
549 | ||
550 | *paren = endchar; | |
551 | endchar = *endptr; | |
552 | *endptr = '\0'; | |
553 | ||
554 | if (access(manfile, 0)) | |
555 | { | |
556 | /* | |
557 | * Not a local man page, just do it italic... | |
558 | */ | |
559 | ||
560 | fputs("<i>", outfile); | |
561 | while (*lineptr) | |
562 | putc_entity(*lineptr++, outfile); | |
563 | fputs("</i>", outfile); | |
564 | } | |
565 | else | |
566 | { | |
567 | /* | |
568 | * Local man page, do a link... | |
569 | */ | |
570 | ||
571 | fprintf(outfile, "<a href='%s'>", manurl); | |
572 | while (*lineptr) | |
573 | putc_entity(*lineptr++, outfile); | |
574 | fputs("</a>", outfile); | |
575 | } | |
576 | ||
577 | *endptr = endchar; | |
578 | lineptr = endptr + 2; | |
579 | } | |
580 | else if (*lineptr == '\\') | |
ef416fc2 | 581 | { |
582 | lineptr ++; | |
583 | if (!*lineptr) | |
584 | break; | |
585 | else if (isdigit(lineptr[0]) && isdigit(lineptr[1]) && | |
586 | isdigit(lineptr[2])) | |
587 | { | |
588 | fprintf(outfile, "&#%d;", ((lineptr[0] - '0') * 8 + | |
589 | lineptr[1] - '0') * 8 + | |
590 | lineptr[2] - '0'); | |
591 | lineptr += 2; | |
592 | } | |
593 | else if (*lineptr == '&') | |
594 | continue; | |
595 | else if (*lineptr == 's') | |
596 | { | |
597 | while (lineptr[1] == '-' || isdigit(lineptr[1])) | |
598 | lineptr ++; | |
599 | } | |
600 | else if (*lineptr == '*') | |
601 | { | |
602 | lineptr += 2; | |
603 | } | |
604 | else if (*lineptr != 'f') | |
605 | putc_entity(*lineptr, outfile); | |
606 | else | |
607 | { | |
608 | lineptr ++; | |
609 | if (!*lineptr) | |
610 | break; | |
611 | else | |
612 | { | |
613 | fputs(end_fonts[font], outfile); | |
614 | ||
615 | switch (*lineptr) | |
616 | { | |
617 | default : /* Regular */ | |
618 | font = 0; | |
619 | break; | |
620 | case 'B' : /* Bold */ | |
621 | case 'b' : | |
622 | font = 1; | |
623 | break; | |
624 | case 'I' : /* Italic */ | |
625 | case 'i' : | |
626 | font = 2; | |
627 | break; | |
628 | } | |
629 | ||
630 | fputs(start_fonts[font], outfile); | |
631 | } | |
632 | } | |
633 | } | |
634 | else | |
635 | putc_entity(*lineptr, outfile); | |
636 | } | |
637 | ||
638 | if (post) | |
639 | { | |
640 | fputs(post, outfile); | |
641 | post = NULL; | |
642 | } | |
643 | } | |
644 | } | |
645 | ||
646 | fprintf(outfile, "%s\n", end_fonts[font]); | |
647 | ||
648 | if (blist) | |
649 | { | |
650 | fputs("</li>\n</ul>\n", outfile); | |
651 | blist = 0; | |
652 | } | |
653 | ||
654 | if (list) | |
655 | { | |
656 | if (list == 1) | |
657 | fputs("</dt>\n", outfile); | |
658 | else if (list) | |
659 | fputs("</dd>\n", outfile); | |
660 | ||
661 | fputs("</dl>\n", outfile); | |
662 | list = 0; | |
663 | } | |
664 | ||
665 | fputs("</body>\n" | |
666 | "</html>\n", outfile); | |
667 | ||
668 | /* | |
669 | * Close files... | |
670 | */ | |
671 | ||
672 | if (infile != stdin) | |
673 | fclose(infile); | |
674 | ||
675 | if (outfile != stdout) | |
676 | fclose(outfile); | |
677 | ||
678 | /* | |
679 | * Return with no errors... | |
680 | */ | |
681 | ||
682 | return (0); | |
683 | } | |
684 | ||
685 | ||
686 | /* | |
687 | * 'putc_entity()' - Put a single character, using entities as needed. | |
688 | */ | |
689 | ||
690 | static void | |
691 | putc_entity(int ch, /* I - Character */ | |
692 | FILE *fp) /* I - File */ | |
693 | { | |
694 | if (ch == '&') | |
695 | fputs("&", fp); | |
696 | else if (ch == '<') | |
697 | fputs("<", fp); | |
698 | else | |
699 | putc(ch, fp); | |
700 | } | |
701 | ||
702 | ||
703 | /* | |
704 | * 'strmove()' - Move characters within a string. | |
705 | */ | |
706 | ||
707 | static void | |
708 | strmove(char *d, /* I - Destination */ | |
709 | const char *s) /* I - Source */ | |
710 | { | |
711 | while (*s) | |
712 | *d++ = *s++; | |
713 | ||
714 | *d = '\0'; | |
715 | } | |
716 | ||
717 | ||
718 | /* | |
bc44d920 | 719 | * End of "$Id: mantohtml.c 6649 2007-07-11 21:46:42Z mike $". |
ef416fc2 | 720 | */ |