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