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, &section);
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("&reg;", fp);
1080               break;
1081 
1082           case '(' :
1083 	      if (!strncmp(s, "lq", 2))
1084 		fputs("&ldquo;", fp);
1085 	      else if (!strncmp(s, "rq", 2))
1086 		fputs("&rdquo;", 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("&mdash;", fp);
1108           s += 3;
1109         }
1110         else if (!strncmp(s, "(en", 3))
1111         {
1112           fputs("&ndash;", 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("&copy;", fp);
1130 	else if (!strncmp(s, "de]", 3))
1131 	  fputs("&deg;", fp);
1132         else if (!strncmp(s, "rg]", 3))
1133 	  fputs("&reg;", 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("&amp;", fp);
1199   else if (ch == '<')
1200     fputs("&lt;", 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