]> git.ipfire.org Git - thirdparty/cups.git/blob - man/mantohtml.c
Merge changes from CUPS 1.4svn-r8628.
[thirdparty/cups.git] / man / mantohtml.c
1 /*
2 * "$Id: mantohtml.c 7720 2008-07-11 22:46:21Z mike $"
3 *
4 * Man page to HTML conversion program.
5 *
6 * Copyright 2007-2009 by Apple Inc.
7 * Copyright 2004-2006 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * Contents:
16 *
17 * main() - Convert a man page to HTML.
18 * putc_entity() - Put a single character, using entities as needed.
19 * strmove() - Move characters within a string.
20 */
21
22 /*
23 * Include necessary headers.
24 */
25
26 #include <cups/string.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29
30
31 /*
32 * Local functions...
33 */
34
35 static void putc_entity(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 */
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 *endptr, /* Pointer to end of current */
52 endchar, /* End character */
53 *paren, /* Pointer to parenthesis */
54 name[1024]; /* Man page name */
55 int section, /* Man page section */
56 pre, /* Preformatted */
57 font, /* Current font */
58 blist, /* In a bullet list? */
59 list, /* In a list? */
60 linenum; /* Current line number */
61 const char *post; /* Text to add after the current line */
62 static const char /* Start/end tags for fonts */
63 * const start_fonts[] = { "", "<b>", "<i>" },
64 * const end_fonts[] = { "", "</b>", "</i>" };
65
66 /*
67 * Check arguments...
68 */
69
70 if (argc > 3)
71 {
72 fputs("Usage: mantohtml [filename.man [filename.html]]\n", stderr);
73 return (1);
74 }
75
76 /*
77 * Open files as needed...
78 */
79
80 if (argc > 1)
81 {
82 if ((infile = fopen(argv[1], "r")) == NULL)
83 {
84 perror(argv[1]);
85 return (1);
86 }
87 }
88 else
89 infile = stdin;
90
91 if (argc > 2)
92 {
93 if ((outfile = fopen(argv[2], "w")) == NULL)
94 {
95 perror(argv[2]);
96 fclose(infile);
97 return (1);
98 }
99 }
100 else
101 outfile = stdout;
102
103 /*
104 * Read from input and write the output...
105 */
106
107 fputs("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
108 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
109 "<html>\n"
110 "<!-- SECTION: Man Pages -->\n"
111 "<head>\n"
112 "\t<link rel=\"stylesheet\" type=\"text/css\" "
113 "href=\"../cups-printable.css\">\n", outfile);
114
115 blist = 0;
116 font = 0;
117 list = 0;
118 linenum = 0;
119 pre = 0;
120 post = NULL;
121 section = -1;
122
123 while (fgets(line, sizeof(line), infile))
124 {
125 linenum ++;
126
127 if (line[0] == '.')
128 {
129 /*
130 * Strip leading whitespace...
131 */
132
133 while (line[1] == ' ' || line[1] == '\t')
134 strmove(line + 1, line + 2);
135
136 /*
137 * Process man page commands...
138 */
139
140 if (!strncmp(line, ".TH ", 4) && section < 0)
141 {
142 /*
143 * Grab man page title...
144 */
145
146 sscanf(line + 4, "%s%d", name, &section);
147
148 fprintf(outfile,
149 "\t<title>%s(%d)</title>\n"
150 "</head>\n"
151 "<body>\n"
152 "<h1 class=\"title\">%s(%d)</h1>\n"
153 "%s",
154 name, section, name, section, start_fonts[font]);
155 }
156 else if (section < 0)
157 continue;
158 else if (!strncmp(line, ".SH ", 4) || !strncmp(line, ".SS ", 4))
159 {
160 /*
161 * Grab heading...
162 */
163
164 int first = 1;
165
166 fputs(end_fonts[font], outfile);
167
168 if (blist)
169 {
170 fputs("</li>\n</ul>\n", outfile);
171 blist = 0;
172 }
173
174 if (list)
175 {
176 if (list == 1)
177 fputs("</dt>\n", outfile);
178 else if (list)
179 fputs("</dd>\n", outfile);
180
181 fputs("</dl>\n", outfile);
182 list = 0;
183 }
184
185 line[strlen(line) - 1] = '\0'; /* Strip LF */
186
187 if (line[2] == 'H')
188 fputs("<h2 class=\"title\"><a name=\"", outfile);
189 else
190 fputs("<h3><a name=\"", outfile);
191
192 for (lineptr = line + 4; *lineptr; lineptr ++)
193 if (*lineptr == '\"')
194 continue;
195 else if (*lineptr == ' ')
196 putc_entity('_', outfile);
197 else
198 putc_entity(*lineptr, outfile);
199
200 fputs("\">", outfile);
201
202 for (lineptr = line + 4; *lineptr; lineptr ++)
203 if (*lineptr == '\"')
204 continue;
205 else if (*lineptr == ' ')
206 {
207 putc_entity(' ', outfile);
208
209 first = 1;
210 }
211 else
212 {
213 if (first)
214 putc_entity(*lineptr, outfile);
215 else
216 putc_entity(tolower(*lineptr), outfile);
217
218 first = 0;
219 }
220
221 if (line[2] == 'H')
222 fprintf(outfile, "</a></h2>\n%s", start_fonts[font]);
223 else
224 fprintf(outfile, "</a></h3>\n%s", start_fonts[font]);
225 }
226 else if (!strncmp(line, ".LP", 3) || !strncmp(line, ".PP", 3))
227 {
228 /*
229 * New paragraph...
230 */
231
232 fputs(end_fonts[font], outfile);
233
234 if (blist)
235 {
236 fputs("</li>\n</ul>\n", outfile);
237 blist = 0;
238 }
239
240 if (list)
241 {
242 if (list == 1)
243 fputs("</dt>\n", outfile);
244 else if (list)
245 fputs("</dd>\n", outfile);
246
247 fputs("</dl>\n", outfile);
248 list = 0;
249 }
250
251 fputs("<p>", outfile);
252 font = 0;
253 }
254 else if (!strncmp(line, ".TP ", 4))
255 {
256 /*
257 * Grab list...
258 */
259
260 fputs(end_fonts[font], outfile);
261
262 if (blist)
263 {
264 fputs("</li>\n</ul>\n", outfile);
265 blist = 0;
266 }
267
268 if (!list)
269 fputs("<dl>\n", outfile);
270 else if (list == 1)
271 fputs("</dt>\n", outfile);
272 else if (list)
273 fputs("</dd>\n", outfile);
274
275 fputs("<dt>", outfile);
276 list = 1;
277 font = 0;
278 }
279 else if (!strncmp(line, ".br", 3))
280 {
281 /*
282 * Grab line break...
283 */
284
285 if (list == 1)
286 {
287 fputs("</dt>\n<dd>", outfile);
288 list = 2;
289 }
290 else if (list)
291 fputs("</dd>\n<dd>", outfile);
292 else
293 fputs("<br>\n", outfile);
294 }
295 else if (!strncmp(line, ".de ", 4))
296 {
297 /*
298 * Define macro - ignore...
299 */
300
301 while (fgets(line, sizeof(line), infile))
302 {
303 linenum ++;
304
305 if (!strncmp(line, "..", 2))
306 break;
307 }
308 }
309 else if (!strncmp(line, ".RS", 3))
310 {
311 /*
312 * Indent...
313 */
314
315 fputs("<div style='margin-left: 3em;'>\n", outfile);
316 }
317 else if (!strncmp(line, ".RE", 3))
318 {
319 /*
320 * Unindent...
321 */
322
323 fputs("</div>\n", outfile);
324 }
325 else if (!strncmp(line, ".ds ", 4) || !strncmp(line, ".rm ", 4) ||
326 !strncmp(line, ".tr ", 4) || !strncmp(line, ".hy ", 4) ||
327 !strncmp(line, ".IX ", 4) || !strncmp(line, ".PD", 3) ||
328 !strncmp(line, ".Sp", 3))
329 {
330 /*
331 * Ignore unused commands...
332 */
333 }
334 else if (!strncmp(line, ".Vb", 3) || !strncmp(line, ".nf", 3))
335 {
336 /*
337 * Start preformatted...
338 */
339
340 pre = 1;
341 fputs("<pre>\n", outfile);
342 }
343 else if (!strncmp(line, ".Ve", 3) || !strncmp(line, ".fi", 3))
344 {
345 /*
346 * End preformatted...
347 */
348
349 if (pre)
350 {
351 pre = 0;
352 fputs("</pre>\n", outfile);
353 }
354 }
355 else if (!strncmp(line, ".IP \\(bu", 8))
356 {
357 /*
358 * Bullet list...
359 */
360
361 if (blist)
362 fputs("</li>\n", outfile);
363 else
364 {
365 fputs("<ul>\n", outfile);
366 blist = 1;
367 }
368
369 fputs("<li>", outfile);
370 }
371 else if (!strncmp(line, ".IP ", 4))
372 {
373 /*
374 * Indented paragraph...
375 */
376
377 if (blist)
378 {
379 fputs("</li>\n</ul>\n", outfile);
380 blist = 0;
381 }
382
383 fputs("<p style='margin-left: 3em;'>", outfile);
384
385 for (lineptr = line + 4; isspace(*lineptr); lineptr ++);
386
387 if (*lineptr == '\"')
388 {
389 strmove(line, lineptr + 1);
390
391 if ((lineptr = strchr(line, '\"')) != NULL)
392 *lineptr = '\0';
393 }
394 else
395 {
396 strmove(line, lineptr);
397
398 if ((lineptr = strchr(line, ' ')) != NULL)
399 *lineptr = '\0';
400 }
401
402 /*
403 * Process the text as if it was in-line...
404 */
405
406 post = "\n<br />\n<br />";
407 goto process_text;
408 }
409 else if (!strncmp(line, ".\\}", 3))
410 {
411 /*
412 * Ignore close block...
413 */
414 }
415 else if (!strncmp(line, ".ie", 3) || !strncmp(line, ".if", 3) ||
416 !strncmp(line, ".el", 3))
417 {
418 /*
419 * If/else - ignore...
420 */
421
422 if (strchr(line, '{') != NULL)
423 {
424 /*
425 * Skip whole block...
426 */
427
428 while (fgets(line, sizeof(line), infile))
429 {
430 linenum ++;
431
432 if (strchr(line, '}') != NULL)
433 break;
434 }
435 }
436 }
437 #if 0
438 else if (!strncmp(line, ". ", 4))
439 {
440 /*
441 * Grab ...
442 */
443 }
444 #endif /* 0 */
445 else if (!strncmp(line, ".B ", 3))
446 {
447 /*
448 * Grab bold text...
449 */
450
451 fprintf(outfile, "%s<b>%s</b>%s", end_fonts[font], line + 3,
452 start_fonts[font]);
453 }
454 else if (!strncmp(line, ".I ", 3))
455 {
456 /*
457 * Grab italic text...
458 */
459
460 fprintf(outfile, "%s<i>%s</i>%s", end_fonts[font], line + 3,
461 start_fonts[font]);
462 }
463 else if (strncmp(line, ".\\\"", 3))
464 {
465 /*
466 * Unknown...
467 */
468
469 if ((lineptr = strchr(line, ' ')) != NULL)
470 *lineptr = '\0';
471 else if ((lineptr = strchr(line, '\n')) != NULL)
472 *lineptr = '\0';
473
474 fprintf(stderr, "mantohtml: Unknown man page command \'%s\' on line %d!\n",
475 line, linenum);
476 }
477
478 /*
479 * Skip continuation lines...
480 */
481
482 lineptr = line + strlen(line) - 2;
483 if (lineptr >= line && *lineptr == '\\')
484 {
485 while (fgets(line, sizeof(line), infile))
486 {
487 linenum ++;
488 lineptr = line + strlen(line) - 2;
489
490 if (lineptr < line || *lineptr != '\\')
491 break;
492 }
493 }
494 }
495 else
496 {
497 /*
498 * Process man page text...
499 */
500
501 process_text:
502
503 for (lineptr = line; *lineptr; lineptr ++)
504 {
505 if (!strncmp(lineptr, "http://", 7))
506 {
507 /*
508 * Embed URL...
509 */
510
511 for (endptr = lineptr + 7;
512 *endptr && !isspace(*endptr & 255);
513 endptr ++);
514
515 endchar = *endptr;
516 *endptr = '\0';
517
518 fprintf(outfile, "<a href='%s'>%s</a>", lineptr, lineptr);
519 *endptr = endchar;
520 lineptr = endptr - 1;
521 }
522 else if (!strncmp(lineptr, "\\fI", 3) &&
523 (endptr = strstr(lineptr, "\\fR")) != NULL &&
524 (paren = strchr(lineptr, '(')) != NULL &&
525 paren < endptr)
526 {
527 /*
528 * Link to man page?
529 */
530
531 char manfile[1024], /* Man page filename */
532 manurl[1024]; /* Man page URL */
533
534
535 /*
536 * See if the man file is available locally...
537 */
538
539 lineptr += 3;
540 endchar = *paren;
541 *paren = '\0';
542
543 snprintf(manfile, sizeof(manfile), "%s.man", lineptr);
544 snprintf(manurl, sizeof(manurl), "man-%s.html?TOPIC=Man+Pages",
545 lineptr);
546
547 *paren = endchar;
548 endchar = *endptr;
549 *endptr = '\0';
550
551 if (access(manfile, 0))
552 {
553 /*
554 * Not a local man page, just do it italic...
555 */
556
557 fputs("<i>", outfile);
558 while (*lineptr)
559 putc_entity(*lineptr++, outfile);
560 fputs("</i>", outfile);
561 }
562 else
563 {
564 /*
565 * Local man page, do a link...
566 */
567
568 fprintf(outfile, "<a href='%s'>", manurl);
569 while (*lineptr)
570 putc_entity(*lineptr++, outfile);
571 fputs("</a>", outfile);
572 }
573
574 *endptr = endchar;
575 lineptr = endptr + 2;
576 }
577 else if (*lineptr == '\\')
578 {
579 lineptr ++;
580 if (!*lineptr)
581 break;
582 else if (isdigit(lineptr[0]) && isdigit(lineptr[1]) &&
583 isdigit(lineptr[2]))
584 {
585 fprintf(outfile, "&#%d;", ((lineptr[0] - '0') * 8 +
586 lineptr[1] - '0') * 8 +
587 lineptr[2] - '0');
588 lineptr += 2;
589 }
590 else if (*lineptr == '&')
591 continue;
592 else if (*lineptr == 's')
593 {
594 while (lineptr[1] == '-' || isdigit(lineptr[1]))
595 lineptr ++;
596 }
597 else if (*lineptr == '*')
598 {
599 lineptr += 2;
600 }
601 else if (*lineptr != 'f')
602 putc_entity(*lineptr, outfile);
603 else
604 {
605 lineptr ++;
606 if (!*lineptr)
607 break;
608 else
609 {
610 fputs(end_fonts[font], outfile);
611
612 switch (*lineptr)
613 {
614 default : /* Regular */
615 font = 0;
616 break;
617 case 'B' : /* Bold */
618 case 'b' :
619 font = 1;
620 break;
621 case 'I' : /* Italic */
622 case 'i' :
623 font = 2;
624 break;
625 }
626
627 fputs(start_fonts[font], outfile);
628 }
629 }
630 }
631 else
632 putc_entity(*lineptr, outfile);
633 }
634
635 if (post)
636 {
637 fputs(post, outfile);
638 post = NULL;
639 }
640 }
641 }
642
643 fprintf(outfile, "%s\n", end_fonts[font]);
644
645 if (blist)
646 {
647 fputs("</li>\n</ul>\n", outfile);
648 }
649
650 if (list)
651 {
652 if (list == 1)
653 fputs("</dt>\n", outfile);
654 else if (list)
655 fputs("</dd>\n", outfile);
656
657 fputs("</dl>\n", outfile);
658 }
659
660 fputs("</body>\n"
661 "</html>\n", outfile);
662
663 /*
664 * Close files...
665 */
666
667 if (infile != stdin)
668 fclose(infile);
669
670 if (outfile != stdout)
671 fclose(outfile);
672
673 /*
674 * Return with no errors...
675 */
676
677 return (0);
678 }
679
680
681 /*
682 * 'putc_entity()' - Put a single character, using entities as needed.
683 */
684
685 static void
686 putc_entity(int ch, /* I - Character */
687 FILE *fp) /* I - File */
688 {
689 if (ch == '&')
690 fputs("&amp;", fp);
691 else if (ch == '<')
692 fputs("&lt;", fp);
693 else
694 putc(ch, fp);
695 }
696
697
698 /*
699 * 'strmove()' - Move characters within a string.
700 */
701
702 static void
703 strmove(char *d, /* I - Destination */
704 const char *s) /* I - Source */
705 {
706 while (*s)
707 *d++ = *s++;
708
709 *d = '\0';
710 }
711
712
713 /*
714 * End of "$Id: mantohtml.c 7720 2008-07-11 22:46:21Z mike $".
715 */