]> git.ipfire.org Git - thirdparty/cups.git/blob - man/mantohtml.c
0f46848311f73750d978809ddc4c12b7f2a3a771
[thirdparty/cups.git] / man / mantohtml.c
1 /*
2 * Man page to HTML conversion program.
3 *
4 * Copyright 2007-2010, 2014 by Apple Inc.
5 * Copyright 2004-2006 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * file is missing or damaged, see the license at "http://www.cups.org/".
12 */
13
14 /*
15 * Include necessary headers.
16 */
17
18 #include <cups/string-private.h>
19 #include <cups/array-private.h>
20 #include <unistd.h>
21
22
23 /*
24 * Local globals...
25 */
26
27 static const char /* Start/end tags for fonts */
28 * const start_fonts[] = { "", "<b>", "<i>" },
29 * const end_fonts[] = { "", "</b>", "</i>" };
30
31
32 /*
33 * Local functions...
34 */
35
36 static void html_alternate(const char *s, const char *first, const char *second, FILE *fp);
37 static void html_fputs(const char *s, int *font, FILE *fp);
38 static void html_putc(int ch, FILE *fp);
39 static void strmove(char *d, const char *s);
40
41
42 /*
43 * 'main()' - Convert a man page to HTML.
44 */
45
46 int /* O - Exit status */
47 main(int argc, /* I - Number of command-line args */
48 char *argv[]) /* I - Command-line arguments */
49 {
50 FILE *infile, /* Input file */
51 *outfile; /* Output file */
52 char line[1024], /* Line from file */
53 *lineptr, /* Pointer into line */
54 anchor[1024], /* Anchor */
55 name[1024], /* Man page name */
56 ddpost[256]; /* Tagged list post markup */
57 int section = -1, /* Man page section */
58 pre = 0, /* Preformatted */
59 font = 0, /* Current font */
60 linenum = 0; /* Current line number */
61 float list_indent = 0.0f, /* Current list indentation */
62 nested_indent = 0.0f; /* Nested list indentation, if any */
63 const char *list = NULL, /* Current list, if any */
64 *nested = NULL; /* Nested list, if any */
65 const char *post = NULL; /* Text to add after the current line */
66
67
68 /*
69 * Check arguments...
70 */
71
72 if (argc > 3)
73 {
74 fputs("Usage: mantohtml [filename.man [filename.html]]\n", stderr);
75 return (1);
76 }
77
78 /*
79 * Open files as needed...
80 */
81
82 if (argc > 1)
83 {
84 if ((infile = fopen(argv[1], "r")) == NULL)
85 {
86 perror(argv[1]);
87 return (1);
88 }
89 }
90 else
91 infile = stdin;
92
93 if (argc > 2)
94 {
95 if ((outfile = fopen(argv[2], "w")) == NULL)
96 {
97 perror(argv[2]);
98 fclose(infile);
99 return (1);
100 }
101 }
102 else
103 outfile = stdout;
104
105 /*
106 * Read from input and write the output...
107 */
108
109 fputs("<!DOCTYPE HTML>\n"
110 "<html>\n"
111 "<!-- SECTION: Man Pages -->\n"
112 "<head>\n"
113 "\t<link rel=\"stylesheet\" type=\"text/css\" "
114 "href=\"../cups-printable.css\">\n", outfile);
115
116 anchor[0] = '\0';
117
118 while (fgets(line, sizeof(line), infile))
119 {
120 size_t linelen = strlen(line); /* Length of line */
121
122 if (linelen > 0 && line[linelen - 1] == '\n')
123 line[linelen - 1] = '\0';
124
125 linenum ++;
126
127 if (line[0] == '.')
128 {
129 /*
130 * Strip leading whitespace...
131 */
132
133 while (line[1] == ' ' || line[1] == '\t')
134 strmove(line + 1, line + 2);
135
136 /*
137 * Process man page commands...
138 */
139
140 if (!strncmp(line, ".TH ", 4) && section < 0)
141 {
142 /*
143 * Grab man page title...
144 */
145
146 sscanf(line + 4, "%s%d", name, &section);
147
148 fprintf(outfile,
149 "\t<title>%s(%d)</title>\n"
150 "</head>\n"
151 "<body>\n"
152 "<h1 class=\"title\">%s(%d)</h1>\n"
153 "%s",
154 name, section, name, section, start_fonts[font]);
155 }
156 else if (section < 0)
157 continue;
158 else if (!strncmp(line, ".SH ", 4) || !strncmp(line, ".SS ", 4))
159 {
160 /*
161 * Grab heading...
162 */
163
164 int first = 1;
165
166 fputs(end_fonts[font], outfile);
167 font = 0;
168
169 if (list)
170 {
171 fprintf(outfile, "</%s>\n", list);
172 list = NULL;
173 }
174
175 if (line[2] == 'H')
176 fputs("<h2 class=\"title\"><a name=\"", outfile);
177 else
178 fputs("<h3><a name=\"", outfile);
179
180 if (anchor[0])
181 {
182 fputs(anchor, outfile);
183 anchor[0] = '\0';
184 }
185 else
186 {
187 for (lineptr = line + 4; *lineptr; lineptr ++)
188 if (*lineptr == '\"')
189 continue;
190 else if (isalnum(*lineptr & 255))
191 html_putc(*lineptr, outfile);
192 else
193 html_putc('_', outfile);
194 }
195
196 fputs("\">", outfile);
197
198 for (lineptr = line + 4; *lineptr; lineptr ++)
199 {
200 if (*lineptr == '\"')
201 continue;
202 else if (*lineptr == ' ')
203 {
204 html_putc(' ', outfile);
205
206 first = 1;
207 }
208 else
209 {
210 if (first)
211 html_putc(*lineptr, outfile);
212 else
213 html_putc(tolower(*lineptr & 255), outfile);
214
215 first = 0;
216 }
217 }
218
219 if (line[2] == 'H')
220 fputs("</a></h2>\n", outfile);
221 else
222 fputs("</a></h3>\n", outfile);
223 }
224 else if (!strncmp(line, ".B ", 3))
225 {
226 /*
227 * Grab bold text...
228 */
229
230 fputs(end_fonts[font], outfile);
231 font = 0;
232
233 if (anchor[0])
234 fprintf(outfile, "<a name=\"%s\">", anchor);
235
236 html_alternate(line + 3, "b", "b", outfile);
237
238 if (anchor[0])
239 {
240 fputs("</a>", outfile);
241 anchor[0] = '\0';
242 }
243
244 if (post)
245 {
246 fputs(post, outfile);
247 post = NULL;
248 }
249 }
250 else if (!strncmp(line, ".I ", 3))
251 {
252 /*
253 * Grab italic text...
254 */
255
256 fputs(end_fonts[font], outfile);
257 font = 0;
258
259 if (anchor[0])
260 fprintf(outfile, "<a name=\"%s\">", anchor);
261
262 html_alternate(line + 3, "i", "i", outfile);
263
264 if (anchor[0])
265 {
266 fputs("</a>", outfile);
267 anchor[0] = '\0';
268 }
269
270 if (post)
271 {
272 fputs(post, outfile);
273 post = NULL;
274 }
275 }
276 else if (!strncmp(line, ".BI ", 4))
277 {
278 /*
279 * Alternating bold and italic text...
280 */
281
282 fputs(end_fonts[font], outfile);
283 font = 0;
284
285 if (anchor[0])
286 fprintf(outfile, "<a name=\"%s\">", anchor);
287
288 html_alternate(line + 4, "b", "i", outfile);
289
290 if (anchor[0])
291 {
292 fputs("</a>", outfile);
293 anchor[0] = '\0';
294 }
295
296 if (post)
297 {
298 fputs(post, outfile);
299 post = NULL;
300 }
301 }
302 else if (!strncmp(line, ".BR ", 4))
303 {
304 /*
305 * Alternating bold and roman (plain) text...
306 */
307
308 fputs(end_fonts[font], outfile);
309 font = 0;
310
311 if (anchor[0])
312 fprintf(outfile, "<a name=\"%s\">", anchor);
313
314 html_alternate(line + 4, "b", NULL, outfile);
315
316 if (anchor[0])
317 {
318 fputs("</a>", outfile);
319 anchor[0] = '\0';
320 }
321
322 if (post)
323 {
324 fputs(post, outfile);
325 post = NULL;
326 }
327 }
328 else if (!strncmp(line, ".IB ", 4))
329 {
330 /*
331 * Alternating italic and bold text...
332 */
333
334 fputs(end_fonts[font], outfile);
335 font = 0;
336
337 if (anchor[0])
338 fprintf(outfile, "<a name=\"%s\">", anchor);
339
340 html_alternate(line + 4, "i", "b", outfile);
341
342 if (anchor[0])
343 {
344 fputs("</a>", outfile);
345 anchor[0] = '\0';
346 }
347
348 if (post)
349 {
350 fputs(post, outfile);
351 post = NULL;
352 }
353 }
354 else if (!strncmp(line, ".IR ", 4))
355 {
356 /*
357 * Alternating italic and roman (plain) text...
358 */
359
360 fputs(end_fonts[font], outfile);
361 font = 0;
362
363 if (anchor[0])
364 fprintf(outfile, "<a name=\"%s\">", anchor);
365
366 html_alternate(line + 4, "i", NULL, outfile);
367
368 if (anchor[0])
369 {
370 fputs("</a>", outfile);
371 anchor[0] = '\0';
372 }
373
374 if (post)
375 {
376 fputs(post, outfile);
377 post = NULL;
378 }
379 }
380 else if (!strncmp(line, ".RB ", 4))
381 {
382 /*
383 * Alternating roman (plain) and bold text...
384 */
385
386 fputs(end_fonts[font], outfile);
387 font = 0;
388
389 if (anchor[0])
390 fprintf(outfile, "<a name=\"%s\">", anchor);
391
392 html_alternate(line + 4, NULL, "b", outfile);
393
394 if (anchor[0])
395 {
396 fputs("</a>", outfile);
397 anchor[0] = '\0';
398 }
399
400 if (post)
401 {
402 fputs(post, outfile);
403 post = NULL;
404 }
405 }
406 else if (!strncmp(line, ".RI ", 4))
407 {
408 /*
409 * Alternating roman (plain) and italic text...
410 */
411
412 fputs(end_fonts[font], outfile);
413 font = 0;
414
415 if (anchor[0])
416 fprintf(outfile, "<a name=\"%s\">", anchor);
417
418 html_alternate(line + 4, NULL, "i", outfile);
419
420 if (anchor[0])
421 {
422 fputs("</a>", outfile);
423 anchor[0] = '\0';
424 }
425
426 if (post)
427 {
428 fputs(post, outfile);
429 post = NULL;
430 }
431 }
432 else if (!strncmp(line, ".SB ", 4))
433 {
434 /*
435 * Alternating small and bold text...
436 */
437
438 fputs(end_fonts[font], outfile);
439 font = 0;
440
441 if (anchor[0])
442 fprintf(outfile, "<a name=\"%s\">", anchor);
443
444 html_alternate(line + 4, "small", "b", outfile);
445
446 if (anchor[0])
447 {
448 fputs("</a>", outfile);
449 anchor[0] = '\0';
450 }
451
452 if (post)
453 {
454 fputs(post, outfile);
455 post = NULL;
456 }
457 }
458 else if (!strncmp(line, ".SM ", 4))
459 {
460 /*
461 * Small text...
462 */
463
464 fputs(end_fonts[font], outfile);
465 font = 0;
466
467 if (anchor[0])
468 fprintf(outfile, "<a name=\"%s\">", anchor);
469
470 html_alternate(line + 4, "small", "small", outfile);
471
472 if (anchor[0])
473 {
474 fputs("</a>", outfile);
475 anchor[0] = '\0';
476 }
477
478 if (post)
479 {
480 fputs(post, outfile);
481 post = NULL;
482 }
483 }
484 else if (!strcmp(line, ".LP") || !strcmp(line, ".PP") || !strcmp(line, ".P"))
485 {
486 /*
487 * New paragraph...
488 */
489
490 fputs(end_fonts[font], outfile);
491 font = 0;
492
493 if (list)
494 {
495 fprintf(outfile, "</%s>\n", list);
496 list = NULL;
497 }
498
499 fputs("<p>", outfile);
500
501 if (anchor[0])
502 {
503 fprintf(outfile, "<a name=\"%s\"></a>", anchor);
504 anchor[0] = '\0';
505 }
506 }
507 else if (!strcmp(line, ".RS") || !strncmp(line, ".RS ", 4))
508 {
509 /*
510 * Indent...
511 */
512
513 float amount = 3.0; /* Indentation */
514
515 if (line[3])
516 amount = (float)atof(line + 4);
517
518 fputs(end_fonts[font], outfile);
519 font = 0;
520
521 if (list)
522 {
523 nested = list;
524 list = NULL;
525 nested_indent = list_indent;
526 list_indent = 0.0f;
527 }
528
529 fprintf(outfile, "<div style=\"margin-left: %.1fem;\">\n", amount - nested_indent);
530 }
531 else if (!strcmp(line, ".RE"))
532 {
533 /*
534 * Unindent...
535 */
536
537 fputs(end_fonts[font], outfile);
538 font = 0;
539
540 fputs("</div>\n", outfile);
541
542 if (nested)
543 {
544 list = nested;
545 nested = NULL;
546
547 list_indent = nested_indent;
548 nested_indent = 0.0f;
549 }
550 }
551 else if (!strcmp(line, ".HP") || !strncmp(line, ".HP ", 4))
552 {
553 /*
554 * Hanging paragraph...
555 *
556 * .HP i
557 */
558
559 float amount = 3.0; /* Indentation */
560
561 if (line[3])
562 amount = (float)atof(line + 4);
563
564 fputs(end_fonts[font], outfile);
565 font = 0;
566
567 if (list)
568 {
569 fprintf(outfile, "</%s>\n", list);
570 list = NULL;
571 }
572
573 fprintf(outfile, "<p style=\"margin-left: %.1fem; text-indent: %.1fem\">", amount, -amount);
574
575 if (anchor[0])
576 {
577 fprintf(outfile, "<a name=\"%s\"></a>", anchor);
578 anchor[0] = '\0';
579 }
580
581 if (line[1] == 'T')
582 post = "<br>\n";
583 }
584 else if (!strcmp(line, ".TP") || !strncmp(line, ".TP ", 4))
585 {
586 /*
587 * Tagged list...
588 *
589 * .TP i
590 */
591
592 float amount = 3.0; /* Indentation */
593
594 if (line[3])
595 amount = (float)atof(line + 4);
596
597 fputs(end_fonts[font], outfile);
598 font = 0;
599
600 if (list && strcmp(list, "dl"))
601 {
602 fprintf(outfile, "</%s>\n", list);
603 list = NULL;
604 }
605
606 if (!list)
607 {
608 fputs("<dl class=\"man\">\n", outfile);
609 list = "dl";
610 list_indent = amount;
611 }
612
613 fputs("<dt>", outfile);
614 snprintf(ddpost, sizeof(ddpost), "<dd style=\"margin-left: %.1fem\">", amount);
615 post = ddpost;
616
617 if (anchor[0])
618 {
619 fprintf(outfile, "<a name=\"%s\"></a>", anchor);
620 anchor[0] = '\0';
621 }
622 }
623 else if (!strncmp(line, ".IP ", 4))
624 {
625 /*
626 * Indented paragraph...
627 *
628 * .IP x i
629 */
630
631 float amount = 3.0; /* Indentation */
632 const char *newlist = NULL; /* New list style */
633 const char *newtype = NULL; /* New list numbering type */
634
635 fputs(end_fonts[font], outfile);
636 font = 0;
637
638 lineptr = line + 4;
639 while (isspace(*lineptr & 255))
640 lineptr ++;
641
642 if (!strncmp(lineptr, "\\(bu", 4) || !strncmp(lineptr, "\\(em", 4))
643 {
644 /*
645 * Bullet list...
646 */
647
648 newlist = "ul";
649 }
650 else if (isdigit(*lineptr & 255))
651 {
652 /*
653 * Numbered list...
654 */
655
656 newlist = "ol";
657 }
658 else if (islower(*lineptr & 255))
659 {
660 /*
661 * Lowercase alpha list...
662 */
663
664 newlist = "ol";
665 newtype = "a";
666 }
667 else if (isupper(*lineptr & 255))
668 {
669 /*
670 * Lowercase alpha list...
671 */
672
673 newlist = "ol";
674 newtype = "A";
675 }
676
677 while (!isspace(*lineptr & 255))
678 lineptr ++;
679 while (isspace(*lineptr & 255))
680 lineptr ++;
681
682 if (isdigit(*lineptr & 255))
683 amount = (float)atof(lineptr);
684
685 if (newlist && list && strcmp(newlist, list))
686 {
687 fprintf(outfile, "</%s>\n", list);
688 list = NULL;
689 }
690
691 if (newlist && !list)
692 {
693 if (newtype)
694 fprintf(outfile, "<%s type=\"%s\">\n", newlist, newtype);
695 else
696 fprintf(outfile, "<%s>\n", newlist);
697
698 list = newlist;
699 }
700
701 if (list)
702 fprintf(outfile, "<li style=\"margin-left: %.1fem;\">", amount);
703 else
704 fprintf(outfile, "<p style=\"margin-left: %.1fem;\">", amount);
705
706 if (anchor[0])
707 {
708 fprintf(outfile, "<a name=\"%s\"></a>", anchor);
709 anchor[0] = '\0';
710 }
711 }
712 else if (!strncmp(line, ".br", 3))
713 {
714 /*
715 * Grab line break...
716 */
717
718 fputs("<br>\n", outfile);
719 }
720 else if (!strncmp(line, ".de ", 4))
721 {
722 /*
723 * Define macro - ignore...
724 */
725
726 while (fgets(line, sizeof(line), infile))
727 {
728 linenum ++;
729
730 if (!strncmp(line, "..", 2))
731 break;
732 }
733 }
734 else if (!strncmp(line, ".ds ", 4) || !strncmp(line, ".rm ", 4) ||
735 !strncmp(line, ".tr ", 4) || !strncmp(line, ".hy ", 4) ||
736 !strncmp(line, ".IX ", 4) || !strncmp(line, ".PD", 3) ||
737 !strncmp(line, ".Sp", 3))
738 {
739 /*
740 * Ignore unused commands...
741 */
742 }
743 else if (!strncmp(line, ".Vb", 3) || !strncmp(line, ".nf", 3) || !strncmp(line, ".EX", 3))
744 {
745 /*
746 * Start preformatted...
747 */
748
749 fputs(end_fonts[font], outfile);
750 font = 0;
751
752 // if (list)
753 // {
754 // fprintf(outfile, "</%s>\n", list);
755 // list = NULL;
756 // }
757
758 pre = 1;
759 fputs("<pre class=\"man\">\n", outfile);
760 }
761 else if (!strncmp(line, ".Ve", 3) || !strncmp(line, ".fi", 3) || !strncmp(line, ".EE", 3))
762 {
763 /*
764 * End preformatted...
765 */
766
767 fputs(end_fonts[font], outfile);
768 font = 0;
769
770 if (pre)
771 {
772 pre = 0;
773 fputs("</pre>\n", outfile);
774 }
775 }
776 else if (!strncmp(line, ".\\}", 3))
777 {
778 /*
779 * Ignore close block...
780 */
781 }
782 else if (!strncmp(line, ".ie", 3) || !strncmp(line, ".if", 3) ||
783 !strncmp(line, ".el", 3))
784 {
785 /*
786 * If/else - ignore...
787 */
788
789 if (strchr(line, '{') != NULL)
790 {
791 /*
792 * Skip whole block...
793 */
794
795 while (fgets(line, sizeof(line), infile))
796 {
797 linenum ++;
798
799 if (strchr(line, '}') != NULL)
800 break;
801 }
802 }
803 }
804 #if 0
805 else if (!strncmp(line, ". ", 4))
806 {
807 /*
808 * Grab ...
809 */
810 }
811 #endif /* 0 */
812 else if (!strncmp(line, ".\\\"#", 4))
813 {
814 /*
815 * Anchor for HTML output...
816 */
817
818 strlcpy(anchor, line + 4, sizeof(anchor));
819 }
820 else if (strncmp(line, ".\\\"", 3))
821 {
822 /*
823 * Unknown...
824 */
825
826 if ((lineptr = strchr(line, ' ')) != NULL)
827 *lineptr = '\0';
828 else if ((lineptr = strchr(line, '\n')) != NULL)
829 *lineptr = '\0';
830
831 fprintf(stderr, "mantohtml: Unknown man page command \'%s\' on line %d.\n", line, linenum);
832 }
833
834 /*
835 * Skip continuation lines...
836 */
837
838 lineptr = line + strlen(line) - 1;
839 if (lineptr >= line && *lineptr == '\\')
840 {
841 while (fgets(line, sizeof(line), infile))
842 {
843 linenum ++;
844 lineptr = line + strlen(line) - 2;
845
846 if (lineptr < line || *lineptr != '\\')
847 break;
848 }
849 }
850 }
851 else
852 {
853 /*
854 * Process man page text...
855 */
856
857 html_fputs(line, &font, outfile);
858 putc('\n', outfile);
859
860 if (post)
861 {
862 fputs(post, outfile);
863 post = NULL;
864 }
865 }
866 }
867
868 fprintf(outfile, "%s\n", end_fonts[font]);
869 font = 0;
870
871 if (list)
872 {
873 fprintf(outfile, "</%s>\n", list);
874 list = NULL;
875 }
876
877 fputs("</body>\n"
878 "</html>\n", outfile);
879
880 /*
881 * Close files...
882 */
883
884 if (infile != stdin)
885 fclose(infile);
886
887 if (outfile != stdout)
888 fclose(outfile);
889
890 /*
891 * Return with no errors...
892 */
893
894 return (0);
895 }
896
897
898 /*
899 * 'html_alternate()' - Alternate words between two styles of text.
900 */
901
902 static void
903 html_alternate(const char *s, /* I - String */
904 const char *first, /* I - First style or NULL */
905 const char *second, /* I - Second style of NULL */
906 FILE *fp) /* I - File */
907 {
908 int i = 0; /* Which style */
909 int quote = 0; /* Saw quote? */
910 int dolinks, /* Do hyperlinks to other man pages? */
911 link = 0; /* Doing a link now? */
912
913
914 /*
915 * Skip leading whitespace...
916 */
917
918 while (isspace(*s & 255))
919 s ++;
920
921 dolinks = first && !strcmp(first, "b") && !second;
922
923 while (*s)
924 {
925 if (!i && dolinks)
926 {
927 /*
928 * See if we need to make a link to a man page...
929 */
930
931 const char *end; /* End of current word */
932 const char *next; /* Start of next word */
933
934 for (end = s; *end && !isspace(*end & 255); end ++);
935 for (next = end; isspace(*next & 255); next ++);
936
937 if (isalnum(*s & 255) && *next == '(')
938 {
939 /*
940 * See if the man file is available locally...
941 */
942
943 char name[1024], /* Name */
944 manfile[1024], /* Man page filename */
945 manurl[1024]; /* Man page URL */
946
947 strlcpy(name, s, sizeof(name));
948 if ((size_t)(end - s) < sizeof(name))
949 name[end - s] = '\0';
950
951 snprintf(manfile, sizeof(manfile), "%s.man", name);
952 snprintf(manurl, sizeof(manurl), "man-%s.html?TOPIC=Man+Pages", name);
953
954 if (!access(manfile, 0))
955 {
956 /*
957 * Local man page, do a link...
958 */
959
960 fprintf(fp, "<a href=\"%s\">", manurl);
961 link = 1;
962 }
963 }
964 }
965
966 if (!i && first)
967 fprintf(fp, "<%s>", first);
968 else if (i && second)
969 fprintf(fp, "<%s>", second);
970
971 while ((!isspace(*s & 255) || quote) && *s)
972 {
973 if (*s == '\"')
974 quote = !quote;
975
976 if (*s == '\\' && s[1])
977 {
978 s ++;
979 html_putc(*s++, fp);
980 }
981 else
982 html_putc(*s++, fp);
983 }
984
985 if (!i && first)
986 fprintf(fp, "</%s>", first);
987 else if (i && second)
988 fprintf(fp, "</%s>", second);
989
990 if (i && link)
991 {
992 fputs("</a>", fp);
993 link = 0;
994 }
995
996 i = 1 - i;
997
998 /*
999 * Skip trailing whitespace...
1000 */
1001
1002 while (isspace(*s & 255))
1003 s ++;
1004 }
1005
1006 putc('\n', fp);
1007 }
1008
1009 /*
1010 * 'html_fputs()' - Output a string, quoting as needed HTML entities.
1011 */
1012
1013 static void
1014 html_fputs(const char *s, /* I - String */
1015 int *font, /* IO - Font */
1016 FILE *fp) /* I - File */
1017 {
1018 while (*s)
1019 {
1020 if (*s == '\\')
1021 {
1022 s ++;
1023 if (!*s)
1024 break;
1025
1026 if (*s == 'f')
1027 {
1028 int newfont; /* New font */
1029
1030 s ++;
1031 if (!*s)
1032 break;
1033
1034 if (!font)
1035 {
1036 s ++;
1037 continue;
1038 }
1039
1040 switch (*s++)
1041 {
1042 case 'R' :
1043 case 'P' :
1044 newfont = 0;
1045 break;
1046
1047 case 'b' :
1048 case 'B' :
1049 newfont = 1;
1050 break;
1051
1052 case 'i' :
1053 case 'I' :
1054 newfont = 2;
1055 break;
1056
1057 default :
1058 fprintf(stderr, "mantohtml: Unknown font \"\\f%c\" ignored.\n", s[-1]);
1059 newfont = *font;
1060 break;
1061 }
1062
1063 if (newfont != *font)
1064 {
1065 fputs(end_fonts[*font], fp);
1066 *font = newfont;
1067 fputs(start_fonts[*font], fp);
1068 }
1069 }
1070 else if (*s == '*')
1071 {
1072 /*
1073 * Substitute macro...
1074 */
1075
1076 s ++;
1077 if (!*s)
1078 break;
1079
1080 switch (*s++)
1081 {
1082 case 'R' :
1083 fputs("&reg;", fp);
1084 break;
1085
1086 case '(' :
1087 if (!strncmp(s, "lq", 2))
1088 fputs("&ldquo;", fp);
1089 else if (!strncmp(s, "rq", 2))
1090 fputs("&rdquo;", fp);
1091 else if (!strncmp(s, "Tm", 2))
1092 fputs("<sup>TM</sup>", fp);
1093 else
1094 fprintf(stderr, "mantohtml: Unknown macro \"\\*(%2s\" ignored.\n", s);
1095
1096 if (*s)
1097 s ++;
1098 if (*s)
1099 s ++;
1100 break;
1101
1102 default :
1103 fprintf(stderr, "mantohtml: Unknown macro \"\\*%c\" ignored.\n", s[-1]);
1104 break;
1105 }
1106 }
1107 else if (*s == '(')
1108 {
1109 if (!strncmp(s, "(em", 3))
1110 {
1111 fputs("&mdash;", fp);
1112 s += 3;
1113 }
1114 else if (!strncmp(s, "(en", 3))
1115 {
1116 fputs("&ndash;", fp);
1117 s += 3;
1118 }
1119 else
1120 {
1121 putc(*s, fp);
1122 s ++;
1123 }
1124 }
1125 else if (*s == '[')
1126 {
1127 /*
1128 * Substitute escaped character...
1129 */
1130
1131 s ++;
1132 if (!strncmp(s, "co]", 3))
1133 fputs("&copy;", fp);
1134 else if (!strncmp(s, "de]", 3))
1135 fputs("&deg;", fp);
1136 else if (!strncmp(s, "rg]", 3))
1137 fputs("&reg;", fp);
1138 else if (!strncmp(s, "tm]", 3))
1139 fputs("<sup>TM</sup>", fp);
1140
1141 if (*s)
1142 s ++;
1143 if (*s)
1144 s ++;
1145 if (*s)
1146 s ++;
1147 }
1148 else if (isdigit(s[0]) && isdigit(s[1]) &&
1149 isdigit(s[2]))
1150 {
1151 fprintf(fp, "&#%d;", ((s[0] - '0') * 8 + s[1] - '0') * 8 + s[2] - '0');
1152 s += 3;
1153 }
1154 else
1155 {
1156 if (*s != '\\' && *s == '\"' && *s == '\'' && *s == '-')
1157 fprintf(stderr, "mantohtml: Unrecognized escape \"\\%c\" ignored.\n", *s);
1158
1159 html_putc(*s++, fp);
1160 }
1161 }
1162 else if (!strncmp(s, "http://", 7) || !strncmp(s, "https://", 8) || !strncmp(s, "ftp://", 6))
1163 {
1164 /*
1165 * Embed URL...
1166 */
1167
1168 char temp[1024]; /* Temporary string */
1169 const char *end = s + 6; /* End of URL */
1170
1171 while (*end && !isspace(*end & 255))
1172 end ++;
1173
1174 if (end[-1] == ',' || end[-1] == '.' || end[-1] == ')')
1175 end --;
1176
1177 strlcpy(temp, s, sizeof(temp));
1178 if ((size_t)(end -s) < sizeof(temp))
1179 temp[end - s] = '\0';
1180
1181 fprintf(fp, "<a href=\"%s\">%s</a>", temp, temp);
1182 s = end;
1183 }
1184 else
1185 html_putc(*s++ & 255, fp);
1186 }
1187 }
1188
1189
1190 /*
1191 * 'html_putc()' - Put a single character, using entities as needed.
1192 */
1193
1194 static void
1195 html_putc(int ch, /* I - Character */
1196 FILE *fp) /* I - File */
1197 {
1198 if (ch == '&')
1199 fputs("&amp;", fp);
1200 else if (ch == '<')
1201 fputs("&lt;", fp);
1202 else
1203 putc(ch, fp);
1204 }
1205
1206
1207 /*
1208 * 'strmove()' - Move characters within a string.
1209 */
1210
1211 static void
1212 strmove(char *d, /* I - Destination */
1213 const char *s) /* I - Source */
1214 {
1215 while (*s)
1216 *d++ = *s++;
1217
1218 *d = '\0';
1219 }