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