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