]> git.ipfire.org Git - thirdparty/cups.git/blame - man/mantohtml.c
Update .gitignore to ignore compressed man pages.
[thirdparty/cups.git] / man / mantohtml.c
CommitLineData
ef416fc2 1/*
c277f98c 2 * Man page to HTML conversion program.
ef416fc2 3 *
845913bd 4 * Copyright 2007-2017 by Apple Inc.
c277f98c 5 * Copyright 2004-2006 by Easy Software Products.
ef416fc2 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
ef416fc2 8 */
9
10/*
11 * Include necessary headers.
12 */
13
71e16022 14#include <cups/string-private.h>
c277f98c 15#include <cups/array-private.h>
ecdc0628 16#include <unistd.h>
ef416fc2 17
18
c277f98c
MS
19/*
20 * Local globals...
21 */
22
23static const char /* Start/end tags for fonts */
24 * const start_fonts[] = { "", "<b>", "<i>" },
25 * const end_fonts[] = { "", "</b>", "</i>" };
26
27
ef416fc2 28/*
29 * Local functions...
30 */
31
c277f98c
MS
32static void html_alternate(const char *s, const char *first, const char *second, FILE *fp);
33static void html_fputs(const char *s, int *font, FILE *fp);
34static void html_putc(int ch, FILE *fp);
ef416fc2 35static void strmove(char *d, const char *s);
36
37
38/*
39 * 'main()' - Convert a man page to HTML.
40 */
41
42int /* O - Exit status */
43main(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 */
37d9646b 50 anchor[1024], /* Anchor */
2909c66c
MS
51 name[1024], /* Man page name */
52 ddpost[256]; /* Tagged list post markup */
c277f98c
MS
53 int section = -1, /* Man page section */
54 pre = 0, /* Preformatted */
55 font = 0, /* Current font */
56 linenum = 0; /* Current line number */
c390f212
MS
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 */
c277f98c
MS
61 const char *post = NULL; /* Text to add after the current line */
62
ef416fc2 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
c277f98c 105 fputs("<!DOCTYPE HTML>\n"
ef416fc2 106 "<html>\n"
107 "<!-- SECTION: Man Pages -->\n"
108 "<head>\n"
178cb736
MS
109 "\t<link rel=\"stylesheet\" type=\"text/css\" "
110 "href=\"../cups-printable.css\">\n", outfile);
ef416fc2 111
37d9646b
MS
112 anchor[0] = '\0';
113
ef416fc2 114 while (fgets(line, sizeof(line), infile))
115 {
c277f98c
MS
116 size_t linelen = strlen(line); /* Length of line */
117
118 if (linelen > 0 && line[linelen - 1] == '\n')
119 line[linelen - 1] = '\0';
120
ef416fc2 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,
bd7854cb 145 "\t<title>%s(%d)</title>\n"
ef416fc2 146 "</head>\n"
147 "<body>\n"
178cb736 148 "<h1 class=\"title\">%s(%d)</h1>\n"
ef416fc2 149 "%s",
178cb736 150 name, section, name, section, start_fonts[font]);
ef416fc2 151 }
152 else if (section < 0)
153 continue;
dd1abb6b 154 else if (!strncmp(line, ".SH ", 4) || !strncmp(line, ".SS ", 4))
ef416fc2 155 {
156 /*
157 * Grab heading...
158 */
159
160 int first = 1;
161
162 fputs(end_fonts[font], outfile);
c277f98c 163 font = 0;
ef416fc2 164
165 if (list)
166 {
c277f98c
MS
167 fprintf(outfile, "</%s>\n", list);
168 list = NULL;
ef416fc2 169 }
170
171 if (line[2] == 'H')
178cb736 172 fputs("<h2 class=\"title\"><a name=\"", outfile);
ef416fc2 173 else
178cb736 174 fputs("<h3><a name=\"", outfile);
bd7854cb 175
37d9646b
MS
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 }
bd7854cb 191
178cb736 192 fputs("\">", outfile);
ef416fc2 193
194 for (lineptr = line + 4; *lineptr; lineptr ++)
c277f98c 195 {
ef416fc2 196 if (*lineptr == '\"')
197 continue;
198 else if (*lineptr == ' ')
199 {
c277f98c 200 html_putc(' ', outfile);
ef416fc2 201
202 first = 1;
203 }
bd7854cb 204 else
ef416fc2 205 {
206 if (first)
c277f98c 207 html_putc(*lineptr, outfile);
ef416fc2 208 else
c277f98c 209 html_putc(tolower(*lineptr & 255), outfile);
ef416fc2 210
211 first = 0;
212 }
c277f98c 213 }
ef416fc2 214
215 if (line[2] == 'H')
c277f98c 216 fputs("</a></h2>\n", outfile);
ef416fc2 217 else
c277f98c 218 fputs("</a></h3>\n", outfile);
ef416fc2 219 }
c277f98c 220 else if (!strncmp(line, ".B ", 3))
ef416fc2 221 {
222 /*
c277f98c 223 * Grab bold text...
ef416fc2 224 */
225
226 fputs(end_fonts[font], outfile);
c277f98c 227 font = 0;
ef416fc2 228
37d9646b
MS
229 if (anchor[0])
230 fprintf(outfile, "<a name=\"%s\">", anchor);
231
c277f98c 232 html_alternate(line + 3, "b", "b", outfile);
2909c66c 233
37d9646b
MS
234 if (anchor[0])
235 {
236 fputs("</a>", outfile);
237 anchor[0] = '\0';
238 }
239
2909c66c
MS
240 if (post)
241 {
242 fputs(post, outfile);
243 post = NULL;
244 }
c277f98c
MS
245 }
246 else if (!strncmp(line, ".I ", 3))
247 {
248 /*
249 * Grab italic text...
250 */
ef416fc2 251
c277f98c
MS
252 fputs(end_fonts[font], outfile);
253 font = 0;
ef416fc2 254
37d9646b
MS
255 if (anchor[0])
256 fprintf(outfile, "<a name=\"%s\">", anchor);
257
c277f98c 258 html_alternate(line + 3, "i", "i", outfile);
2909c66c 259
37d9646b
MS
260 if (anchor[0])
261 {
262 fputs("</a>", outfile);
263 anchor[0] = '\0';
264 }
265
2909c66c
MS
266 if (post)
267 {
268 fputs(post, outfile);
269 post = NULL;
270 }
c277f98c
MS
271 }
272 else if (!strncmp(line, ".BI ", 4))
273 {
274 /*
275 * Alternating bold and italic text...
276 */
ef416fc2 277
c277f98c 278 fputs(end_fonts[font], outfile);
ef416fc2 279 font = 0;
c277f98c 280
37d9646b
MS
281 if (anchor[0])
282 fprintf(outfile, "<a name=\"%s\">", anchor);
283
c277f98c 284 html_alternate(line + 4, "b", "i", outfile);
2909c66c 285
37d9646b
MS
286 if (anchor[0])
287 {
288 fputs("</a>", outfile);
289 anchor[0] = '\0';
290 }
291
2909c66c
MS
292 if (post)
293 {
294 fputs(post, outfile);
295 post = NULL;
296 }
ef416fc2 297 }
c277f98c 298 else if (!strncmp(line, ".BR ", 4))
ef416fc2 299 {
300 /*
c277f98c
MS
301 * Alternating bold and roman (plain) text...
302 */
ef416fc2 303
304 fputs(end_fonts[font], outfile);
c277f98c 305 font = 0;
ef416fc2 306
37d9646b
MS
307 if (anchor[0])
308 fprintf(outfile, "<a name=\"%s\">", anchor);
309
c277f98c 310 html_alternate(line + 4, "b", NULL, outfile);
2909c66c 311
37d9646b
MS
312 if (anchor[0])
313 {
314 fputs("</a>", outfile);
315 anchor[0] = '\0';
316 }
317
2909c66c
MS
318 if (post)
319 {
320 fputs(post, outfile);
321 post = NULL;
322 }
c277f98c
MS
323 }
324 else if (!strncmp(line, ".IB ", 4))
325 {
326 /*
327 * Alternating italic and bold text...
328 */
ef416fc2 329
c277f98c
MS
330 fputs(end_fonts[font], outfile);
331 font = 0;
ef416fc2 332
37d9646b
MS
333 if (anchor[0])
334 fprintf(outfile, "<a name=\"%s\">", anchor);
335
c277f98c 336 html_alternate(line + 4, "i", "b", outfile);
2909c66c 337
37d9646b
MS
338 if (anchor[0])
339 {
340 fputs("</a>", outfile);
341 anchor[0] = '\0';
342 }
343
2909c66c
MS
344 if (post)
345 {
346 fputs(post, outfile);
347 post = NULL;
348 }
c277f98c
MS
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);
ef416fc2 357 font = 0;
c277f98c 358
37d9646b
MS
359 if (anchor[0])
360 fprintf(outfile, "<a name=\"%s\">", anchor);
361
c277f98c 362 html_alternate(line + 4, "i", NULL, outfile);
2909c66c 363
37d9646b
MS
364 if (anchor[0])
365 {
366 fputs("</a>", outfile);
367 anchor[0] = '\0';
368 }
369
2909c66c
MS
370 if (post)
371 {
372 fputs(post, outfile);
373 post = NULL;
374 }
ef416fc2 375 }
c277f98c 376 else if (!strncmp(line, ".RB ", 4))
ef416fc2 377 {
378 /*
c277f98c
MS
379 * Alternating roman (plain) and bold text...
380 */
ef416fc2 381
c277f98c
MS
382 fputs(end_fonts[font], outfile);
383 font = 0;
384
37d9646b
MS
385 if (anchor[0])
386 fprintf(outfile, "<a name=\"%s\">", anchor);
387
c277f98c 388 html_alternate(line + 4, NULL, "b", outfile);
2909c66c 389
37d9646b
MS
390 if (anchor[0])
391 {
392 fputs("</a>", outfile);
393 anchor[0] = '\0';
394 }
395
2909c66c
MS
396 if (post)
397 {
398 fputs(post, outfile);
399 post = NULL;
400 }
ef416fc2 401 }
c277f98c 402 else if (!strncmp(line, ".RI ", 4))
ef416fc2 403 {
404 /*
c277f98c
MS
405 * Alternating roman (plain) and italic text...
406 */
ef416fc2 407
c277f98c
MS
408 fputs(end_fonts[font], outfile);
409 font = 0;
ef416fc2 410
37d9646b
MS
411 if (anchor[0])
412 fprintf(outfile, "<a name=\"%s\">", anchor);
413
c277f98c 414 html_alternate(line + 4, NULL, "i", outfile);
2909c66c 415
37d9646b
MS
416 if (anchor[0])
417 {
418 fputs("</a>", outfile);
419 anchor[0] = '\0';
420 }
421
2909c66c
MS
422 if (post)
423 {
424 fputs(post, outfile);
425 post = NULL;
426 }
ef416fc2 427 }
c277f98c 428 else if (!strncmp(line, ".SB ", 4))
ef416fc2 429 {
430 /*
c277f98c
MS
431 * Alternating small and bold text...
432 */
433
434 fputs(end_fonts[font], outfile);
435 font = 0;
ef416fc2 436
37d9646b
MS
437 if (anchor[0])
438 fprintf(outfile, "<a name=\"%s\">", anchor);
439
c277f98c 440 html_alternate(line + 4, "small", "b", outfile);
2909c66c 441
37d9646b
MS
442 if (anchor[0])
443 {
444 fputs("</a>", outfile);
445 anchor[0] = '\0';
446 }
447
2909c66c
MS
448 if (post)
449 {
450 fputs(post, outfile);
451 post = NULL;
452 }
ef416fc2 453 }
c277f98c 454 else if (!strncmp(line, ".SM ", 4))
ef416fc2 455 {
456 /*
c277f98c
MS
457 * Small text...
458 */
ef416fc2 459
c277f98c
MS
460 fputs(end_fonts[font], outfile);
461 font = 0;
462
37d9646b
MS
463 if (anchor[0])
464 fprintf(outfile, "<a name=\"%s\">", anchor);
465
c277f98c 466 html_alternate(line + 4, "small", "small", outfile);
2909c66c 467
37d9646b
MS
468 if (anchor[0])
469 {
470 fputs("</a>", outfile);
471 anchor[0] = '\0';
472 }
473
2909c66c
MS
474 if (post)
475 {
476 fputs(post, outfile);
477 post = NULL;
478 }
ef416fc2 479 }
c277f98c 480 else if (!strcmp(line, ".LP") || !strcmp(line, ".PP") || !strcmp(line, ".P"))
ef416fc2 481 {
482 /*
c277f98c 483 * New paragraph...
ef416fc2 484 */
c277f98c
MS
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);
37d9646b
MS
496
497 if (anchor[0])
498 {
499 fprintf(outfile, "<a name=\"%s\"></a>", anchor);
500 anchor[0] = '\0';
501 }
ef416fc2 502 }
c277f98c 503 else if (!strcmp(line, ".RS") || !strncmp(line, ".RS ", 4))
ef416fc2 504 {
505 /*
c277f98c 506 * Indent...
ef416fc2 507 */
508
c277f98c
MS
509 float amount = 3.0; /* Indentation */
510
511 if (line[3])
6b4bf984 512 amount = (float)atof(line + 4);
c277f98c
MS
513
514 fputs(end_fonts[font], outfile);
515 font = 0;
516
517 if (list)
518 {
c390f212
MS
519 nested = list;
520 list = NULL;
521 nested_indent = list_indent;
522 list_indent = 0.0f;
c277f98c
MS
523 }
524
c390f212 525 fprintf(outfile, "<div style=\"margin-left: %.1fem;\">\n", amount - nested_indent);
ef416fc2 526 }
c277f98c 527 else if (!strcmp(line, ".RE"))
ef416fc2 528 {
529 /*
c277f98c 530 * Unindent...
ef416fc2 531 */
532
c277f98c
MS
533 fputs(end_fonts[font], outfile);
534 font = 0;
535
536 fputs("</div>\n", outfile);
c390f212
MS
537
538 if (nested)
539 {
540 list = nested;
541 nested = NULL;
542
543 list_indent = nested_indent;
544 nested_indent = 0.0f;
545 }
ef416fc2 546 }
2909c66c 547 else if (!strcmp(line, ".HP") || !strncmp(line, ".HP ", 4))
ef416fc2 548 {
549 /*
2909c66c 550 * Hanging paragraph...
c277f98c
MS
551 *
552 * .HP i
ef416fc2 553 */
554
c277f98c
MS
555 float amount = 3.0; /* Indentation */
556
557 if (line[3])
6b4bf984 558 amount = (float)atof(line + 4);
ef416fc2 559
c277f98c
MS
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
37d9646b
MS
571 if (anchor[0])
572 {
573 fprintf(outfile, "<a name=\"%s\"></a>", anchor);
574 anchor[0] = '\0';
575 }
576
c277f98c
MS
577 if (line[1] == 'T')
578 post = "<br>\n";
ef416fc2 579 }
2909c66c
MS
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])
6b4bf984 591 amount = (float)atof(line + 4);
2909c66c
MS
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);
c390f212
MS
605 list = "dl";
606 list_indent = amount;
2909c66c
MS
607 }
608
609 fputs("<dt>", outfile);
610 snprintf(ddpost, sizeof(ddpost), "<dd style=\"margin-left: %.1fem\">", amount);
611 post = ddpost;
37d9646b
MS
612
613 if (anchor[0])
614 {
615 fprintf(outfile, "<a name=\"%s\"></a>", anchor);
616 anchor[0] = '\0';
617 }
2909c66c 618 }
ef416fc2 619 else if (!strncmp(line, ".IP ", 4))
620 {
621 /*
622 * Indented paragraph...
c277f98c
MS
623 *
624 * .IP x i
ef416fc2 625 */
626
c277f98c
MS
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))
ef416fc2 639 {
c277f98c
MS
640 /*
641 * Bullet list...
642 */
ef416fc2 643
c277f98c
MS
644 newlist = "ul";
645 }
646 else if (isdigit(*lineptr & 255))
647 {
648 /*
649 * Numbered list...
650 */
ef416fc2 651
c277f98c
MS
652 newlist = "ol";
653 }
654 else if (islower(*lineptr & 255))
655 {
656 /*
657 * Lowercase alpha list...
658 */
ef416fc2 659
c277f98c
MS
660 newlist = "ol";
661 newtype = "a";
662 }
663 else if (isupper(*lineptr & 255))
ef416fc2 664 {
c277f98c
MS
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))
6b4bf984 679 amount = (float)atof(lineptr);
ef416fc2 680
c277f98c
MS
681 if (newlist && list && strcmp(newlist, list))
682 {
683 fprintf(outfile, "</%s>\n", list);
684 list = NULL;
ef416fc2 685 }
ef416fc2 686
c277f98c
MS
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;
ef416fc2 695 }
696
c277f98c
MS
697 if (list)
698 fprintf(outfile, "<li style=\"margin-left: %.1fem;\">", amount);
699 else
700 fprintf(outfile, "<p style=\"margin-left: %.1fem;\">", amount);
37d9646b
MS
701
702 if (anchor[0])
703 {
704 fprintf(outfile, "<a name=\"%s\"></a>", anchor);
705 anchor[0] = '\0';
706 }
c277f98c
MS
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 }
2909c66c 739 else if (!strncmp(line, ".Vb", 3) || !strncmp(line, ".nf", 3) || !strncmp(line, ".EX", 3))
c277f98c
MS
740 {
741 /*
742 * Start preformatted...
743 */
744
745 fputs(end_fonts[font], outfile);
746 font = 0;
747
c390f212
MS
748// if (list)
749// {
750// fprintf(outfile, "</%s>\n", list);
751// list = NULL;
752// }
c277f98c
MS
753
754 pre = 1;
2909c66c 755 fputs("<pre class=\"man\">\n", outfile);
c277f98c 756 }
2909c66c 757 else if (!strncmp(line, ".Ve", 3) || !strncmp(line, ".fi", 3) || !strncmp(line, ".EE", 3))
c277f98c 758 {
ef416fc2 759 /*
c277f98c 760 * End preformatted...
ef416fc2 761 */
762
c277f98c
MS
763 fputs(end_fonts[font], outfile);
764 font = 0;
765
766 if (pre)
767 {
768 pre = 0;
769 fputs("</pre>\n", outfile);
770 }
ef416fc2 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 */
37d9646b
MS
808 else if (!strncmp(line, ".\\\"#", 4))
809 {
810 /*
811 * Anchor for HTML output...
812 */
813
814 strlcpy(anchor, line + 4, sizeof(anchor));
815 }
ef416fc2 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
c277f98c 827 fprintf(stderr, "mantohtml: Unknown man page command \'%s\' on line %d.\n", line, linenum);
ef416fc2 828 }
829
830 /*
831 * Skip continuation lines...
832 */
833
c277f98c 834 lineptr = line + strlen(line) - 1;
ef416fc2 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
c277f98c
MS
853 html_fputs(line, &font, outfile);
854 putc('\n', outfile);
ef416fc2 855
856 if (post)
857 {
858 fputs(post, outfile);
859 post = NULL;
860 }
861 }
862 }
863
864 fprintf(outfile, "%s\n", end_fonts[font]);
c277f98c 865 font = 0;
ef416fc2 866
867 if (list)
868 {
c277f98c
MS
869 fprintf(outfile, "</%s>\n", list);
870 list = NULL;
ef416fc2 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/*
c277f98c
MS
895 * 'html_alternate()' - Alternate words between two styles of text.
896 */
897
898static void
899html_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;
c7d89253
MS
971
972 if (*s == '\\' && s[1])
c277f98c
MS
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 ++;
c277f98c
MS
1000 }
1001
1002 putc('\n', fp);
1003}
1004
1005/*
1006 * 'html_fputs()' - Output a string, quoting as needed HTML entities.
1007 */
1008
1009static void
1010html_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 }
2909c66c
MS
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 }
c277f98c
MS
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 {
845913bd
MS
1152 if (*s != '\\' && *s != '\"' && *s != '\'' && *s != '-')
1153 {
c277f98c 1154 fprintf(stderr, "mantohtml: Unrecognized escape \"\\%c\" ignored.\n", *s);
845913bd
MS
1155 html_putc('\\', fp);
1156 }
c277f98c
MS
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
969af935 1167 char temp[1024]; /* Temporary string */
c277f98c
MS
1168 const char *end = s + 6; /* End of URL */
1169
1170 while (*end && !isspace(*end & 255))
1171 end ++;
1172
a99f5c6a 1173 if (end[-1] == ',' || end[-1] == '.' || end[-1] == ')')
969af935
MS
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);
c277f98c
MS
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.
ef416fc2 1191 */
1192
1193static void
c277f98c
MS
1194html_putc(int ch, /* I - Character */
1195 FILE *fp) /* I - File */
ef416fc2 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
1210static void
1211strmove(char *d, /* I - Destination */
1212 const char *s) /* I - Source */
1213{
1214 while (*s)
1215 *d++ = *s++;
1216
1217 *d = '\0';
1218}