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