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