1 /*
2 * HTML exporting functions for HTMLDOC, a HTML document processing program.
3 *
4 * Copyright 2011-2021 by Michael R Sweet.
5 * Copyright 1997-2010 by Easy Software Products. All rights reserved.
6 *
7 * This program is free software. Distribution and use rights are outlined in
8 * the file "COPYING".
9 */
10
11 /*
12 * Include necessary headers.
13 */
14
15 #include "htmldoc.h"
16 #include "markdown.h"
17 #include <ctype.h>
18
19
20 /*
21 * Named link structure...
22 */
23
24 typedef struct
25 {
26 uchar *filename; /* File for link */
27 uchar name[124]; /* Reference name */
28 } link_t;
29
30
31 /*
32 * Local globals...
33 */
34
35
36 static size_t num_links = 0,
37 alloc_links = 0;
38 static link_t *links;
39
40
41 /*
42 * Local functions...
43 */
44
45 extern "C" {
46 typedef int (*compare_func_t)(const void *, const void *);
47 }
48
49 static void write_header(FILE **out, uchar *filename, uchar *title,
50 uchar *author, uchar *copyright, uchar *docnumber,
51 tree_t *t);
52 static void write_footer(FILE **out, tree_t *t);
53 static void write_title(FILE *out, uchar *title, uchar *author,
54 uchar *copyright, uchar *docnumber);
55 static int write_all(FILE *out, tree_t *t, int col);
56 static int write_node(FILE *out, tree_t *t, int col);
57 static int write_nodeclose(FILE *out, tree_t *t, int col);
58 static int write_toc(FILE *out, tree_t *t, int col);
59 static uchar *get_title(tree_t *doc);
60
61 static void add_link(uchar *name, uchar *filename);
62 static link_t *find_link(uchar *name);
63 static int compare_links(link_t *n1, link_t *n2);
64 static void scan_links(tree_t *t, uchar *filename);
65 static void update_links(tree_t *t, uchar *filename);
66
67
68 /*
69 * 'html_export()' - Export to HTML...
70 */
71
72 int /* O - 0 = success, -1 = failure */
html_export(tree_t * document,tree_t * toc)73 html_export(tree_t *document, /* I - Document to export */
74 tree_t *toc) /* I - Table of contents for document */
75 {
76 uchar *title, /* Title text */
77 *author, /* Author name */
78 *copyright, /* Copyright text */
79 *docnumber; /* Document number */
80 FILE *out; /* Output file */
81
82
83 /*
84 * Copy logo and title images...
85 */
86
87 if (OutputFiles)
88 {
89 if (LogoImage[0])
90 image_copy(LogoImage, file_find(LogoImage, Path), OutputPath);
91
92 for (int hfi = 0; hfi < MAX_HF_IMAGES; hfi ++)
93 if (HFImage[hfi][0])
94 image_copy(HFImage[hfi], file_find(HFImage[hfi], Path), OutputPath);
95 }
96
97 if (OutputFiles && TitleImage[0] && TitlePage &&
98 #ifdef WIN32
99 (stricmp(file_extension(TitleImage), "bmp") == 0 ||
100 stricmp(file_extension(TitleImage), "gif") == 0 ||
101 stricmp(file_extension(TitleImage), "jpg") == 0 ||
102 stricmp(file_extension(TitleImage), "png") == 0))
103 #else
104 (strcmp(file_extension(TitleImage), "bmp") == 0 ||
105 strcmp(file_extension(TitleImage), "gif") == 0 ||
106 strcmp(file_extension(TitleImage), "jpg") == 0 ||
107 strcmp(file_extension(TitleImage), "png") == 0))
108 #endif // WIN32
109 image_copy(TitleImage, file_find(TitleImage, Path), OutputPath);
110
111 /*
112 * Get document strings...
113 */
114
115 title = get_title(document);
116 author = htmlGetMeta(document, (uchar *)"author");
117 copyright = htmlGetMeta(document, (uchar *)"copyright");
118 docnumber = htmlGetMeta(document, (uchar *)"docnumber");
119 if (!docnumber)
120 docnumber = htmlGetMeta(document, (uchar *)"version");
121
122 /*
123 * Scan for all links in the document, and then update them...
124 */
125
126 num_links = 0;
127 alloc_links = 0;
128 links = NULL;
129
130 scan_links(document, NULL);
131 update_links(document, NULL);
132 update_links(toc, NULL);
133
134 /*
135 * Generate title pages and a table of contents...
136 */
137
138 out = NULL;
139 if (TitlePage)
140 {
141 write_header(&out, (uchar *)"index.html", title, author, copyright,
142 docnumber, NULL);
143 if (out != NULL)
144 write_title(out, title, author, copyright, docnumber);
145
146 write_footer(&out, NULL);
147 write_header(&out, (uchar *)"toc.html", title, author, copyright,
148 docnumber, NULL);
149 }
150 else
151 write_header(&out, (uchar *)"index.html", title, author, copyright,
152 docnumber, NULL);
153
154 if (out != NULL)
155 write_toc(out, toc, 0);
156 write_footer(&out, NULL);
157
158 /*
159 * Then write each output file...
160 */
161
162 while (document != NULL)
163 {
164 write_header(&out, htmlGetVariable(document, (uchar *)"_HD_FILENAME"),
165 title, author, copyright, docnumber, document);
166 if (out != NULL)
167 write_all(out, document->child, 0);
168 write_footer(&out, document);
169
170 document = document->next;
171 }
172
173 if (!OutputFiles && out != stdout && out != NULL)
174 {
175 fputs("</BODY>\n", out);
176 fputs("</HTML>\n", out);
177
178 progress_error(HD_ERROR_NONE, "BYTES: %ld", ftell(out));
179
180 fclose(out);
181 }
182
183 if (title != NULL)
184 free(title);
185
186 if (alloc_links)
187 {
188 free(links);
189
190 num_links = 0;
191 alloc_links = 0;
192 links = NULL;
193 }
194
195 return (out == NULL);
196 }
197
198
199 /*
200 * 'write_header()' - Output the standard "header" for a HTML file.
201 */
202
203 static void
write_header(FILE ** out,uchar * filename,uchar * title,uchar * author,uchar * copyright,uchar * docnumber,tree_t * t)204 write_header(FILE **out, /* IO - Output file */
205 uchar *filename, /* I - Output filename */
206 uchar *title, /* I - Title for document */
207 uchar *author, /* I - Author for document */
208 uchar *copyright, /* I - Copyright for document */
209 uchar *docnumber, /* I - ID number for document */
210 tree_t *t) /* I - Current document file */
211 {
212 char realname[1024]; /* Real filename */
213 const char *basename; /* Filename without directory */
214 int newfile; /* Non-zero if this is a new file */
215 static const char *families[] =/* Typeface names */
216 {
217 "monospace",
218 "serif",
219 "sans-serif",
220 "monospace",
221 "serif",
222 "sans-serif",
223 "symbol",
224 "dingbats"
225 };
226
227
228 if (OutputFiles)
229 {
230 newfile = 1;
231 basename = file_basename((char *)filename);
232
233 snprintf(realname, sizeof(realname), "%s/%s", OutputPath, basename);
234
235 *out = fopen(realname, "wb");
236 }
237 else if (OutputPath[0])
238 {
239 if (*out == NULL)
240 {
241 *out = fopen(OutputPath, "wb");
242 newfile = 1;
243 }
244 else
245 newfile = 0;
246 }
247 else
248 {
249 if (*out == NULL)
250 {
251 *out = stdout;
252 newfile = 1;
253 }
254 else
255 newfile = 0;
256 }
257
258 if (*out == NULL)
259 {
260 progress_error(HD_ERROR_WRITE_ERROR,
261 "Unable to create output file \"%s\" - %s.\n",
262 OutputFiles ? realname : OutputPath,
263 strerror(errno));
264 return;
265 }
266
267 if (newfile)
268 {
269 fputs("<!DOCTYPE html>\n", *out);
270 fputs("<HTML>\n", *out);
271 fputs("<HEAD>\n", *out);
272 if (title != NULL)
273 fprintf(*out, "<TITLE>%s</TITLE>\n", title);
274 if (author != NULL)
275 fprintf(*out, "<META NAME=\"author\" CONTENT=\"%s\">\n", author);
276 if (copyright != NULL)
277 fprintf(*out, "<META NAME=\"copyright\" CONTENT=\"%s\">\n", copyright);
278 if (docnumber != NULL)
279 fprintf(*out, "<META NAME=\"docnumber\" CONTENT=\"%s\">\n", docnumber);
280 fprintf(*out, "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; CHARSET=%s\">\n",
281 _htmlCharSet);
282
283 if (OutputFiles)
284 {
285 fputs("<LINK REL=\"Start\" HREF=\"index.html\">\n", *out);
286
287 if (TitlePage)
288 fputs("<LINK REL=\"Contents\" HREF=\"toc.html\">\n", *out);
289 else
290 fputs("<LINK REL=\"Contents\" HREF=\"index.html\">\n", *out);
291
292 if (t)
293 {
294 if (t->prev != NULL)
295 fprintf(*out, "<LINK REL=\"Prev\" HREF=\"%s\">\n",
296 file_basename((char *)htmlGetVariable(t->prev, (uchar *)"_HD_FILENAME")));
297
298 if (t->next != NULL)
299 fprintf(*out, "<LINK REL=\"Next\" HREF=\"%s\">\n",
300 file_basename((char *)htmlGetVariable(t->next, (uchar *)"_HD_FILENAME")));
301 }
302 }
303
304 fputs("<STYLE TYPE=\"text/css\"><!--\n", *out);
305 fprintf(*out, "BODY { font-family: %s; }\n", families[_htmlBodyFont]);
306 fprintf(*out, "H1 { font-family: %s; }\n", families[_htmlHeadingFont]);
307 fprintf(*out, "H2 { font-family: %s; }\n", families[_htmlHeadingFont]);
308 fprintf(*out, "H3 { font-family: %s; }\n", families[_htmlHeadingFont]);
309 fprintf(*out, "H4 { font-family: %s; }\n", families[_htmlHeadingFont]);
310 fprintf(*out, "H5 { font-family: %s; }\n", families[_htmlHeadingFont]);
311 fprintf(*out, "H6 { font-family: %s; }\n", families[_htmlHeadingFont]);
312 fputs("SUB { font-size: smaller; }\n", *out);
313 fputs("SUP { font-size: smaller; }\n", *out);
314 fputs("PRE { font-family: monospace; margin-left: 36pt; }\n", *out);
315
316 if (!LinkStyle)
317 fputs("A { text-decoration: none; }\n", *out);
318
319 fputs("--></STYLE>\n", *out);
320 fputs("</HEAD>\n", *out);
321
322 if (BodyImage[0])
323 fprintf(*out, "<BODY BACKGROUND=\"%s\"", file_basename(BodyImage));
324 else if (BodyColor[0])
325 fprintf(*out, "<BODY BGCOLOR=\"%s\"", BodyColor);
326 else
327 fputs("<BODY", *out);
328
329 if (_htmlTextColor[0])
330 fprintf(*out, " TEXT=\"%s\"", _htmlTextColor);
331
332 if (LinkColor[0])
333 fprintf(*out, " LINK=\"%s\" VLINK=\"%s\" ALINK=\"%s\"", LinkColor,
334 LinkColor, LinkColor);
335
336 fputs(">\n", *out);
337 }
338 else
339 fputs("<HR NOSHADE>\n", *out);
340
341 if (OutputFiles && t != NULL && (t->prev != NULL || t->next != NULL))
342 {
343 if (LogoImage[0])
344 fprintf(*out, "<IMG SRC=\"%s\">\n", file_basename(LogoImage));
345
346 for (int hfi = 0; hfi < MAX_HF_IMAGES; ++hfi)
347 if (HFImage[hfi][0])
348 fprintf(*out, "<IMG SRC=\"%s\">\n", file_basename(HFImage[hfi]));
349
350 if (TitlePage)
351 fputs("<A HREF=\"toc.html\">Contents</A>\n", *out);
352 else
353 fputs("<A HREF=\"index.html\">Contents</A>\n", *out);
354
355 if (t->prev != NULL)
356 fprintf(*out, "<A HREF=\"%s\">Previous</A>\n",
357 file_basename((char *)htmlGetVariable(t->prev, (uchar *)"_HD_FILENAME")));
358
359 if (t->next != NULL)
360 fprintf(*out, "<A HREF=\"%s\">Next</A>\n",
361 file_basename((char *)htmlGetVariable(t->next, (uchar *)"_HD_FILENAME")));
362
363 fputs("<HR NOSHADE>\n", *out);
364 }
365 }
366
367
368 /*
369 * 'write_footer()' - Output the standard "footer" for a HTML file.
370 */
371
372 static void
write_footer(FILE ** out,tree_t * t)373 write_footer(FILE **out, /* IO - Output file pointer */
374 tree_t *t) /* I - Current document file */
375 {
376 if (*out == NULL)
377 return;
378
379 if (OutputFiles && t != NULL && (t->prev != NULL || t->next != NULL))
380 {
381 fputs("<HR NOSHADE>\n", *out);
382
383 if (LogoImage[0])
384 fprintf(*out, "<IMG SRC=\"%s\">\n", file_basename(LogoImage));
385
386 for (int hfi = 0; hfi < MAX_HF_IMAGES; ++hfi)
387 if (HFImage[hfi][0])
388 fprintf(*out, "<IMG SRC=\"%s\">\n", file_basename(HFImage[hfi]));
389
390 if (TitlePage)
391 fputs("<A HREF=\"toc.html\">Contents</A>\n", *out);
392 else
393 fputs("<A HREF=\"index.html\">Contents</A>\n", *out);
394
395
396 if (t->prev != NULL)
397 fprintf(*out, "<A HREF=\"%s\">Previous</A>\n",
398 file_basename((char *)htmlGetVariable(t->prev, (uchar *)"_HD_FILENAME")));
399
400 if (t->next != NULL)
401 fprintf(*out, "<A HREF=\"%s\">Next</A>\n",
402 file_basename((char *)htmlGetVariable(t->next, (uchar *)"_HD_FILENAME")));
403 }
404
405 if (OutputFiles)
406 {
407 fputs("</BODY>\n", *out);
408 fputs("</HTML>\n", *out);
409
410 progress_error(HD_ERROR_NONE, "BYTES: %ld", ftell(*out));
411
412 fclose(*out);
413 *out = NULL;
414 }
415 }
416
417
418 /*
419 * 'write_title()' - Write a title page...
420 */
421
422 static void
write_title(FILE * out,uchar * title,uchar * author,uchar * copyright,uchar * docnumber)423 write_title(FILE *out, /* I - Output file */
424 uchar *title, /* I - Title for document */
425 uchar *author, /* I - Author for document */
426 uchar *copyright, /* I - Copyright for document */
427 uchar *docnumber) /* I - ID number for document */
428 {
429 FILE *fp; /* Title file */
430 const char *title_ext, /* Extension of title file */
431 *title_file; /* Location of title file */
432 tree_t *t; /* Title file document tree */
433
434
435 if (out == NULL)
436 return;
437
438 title_ext = file_extension(TitleImage);
439
440 #ifdef WIN32
441 if (TitleImage[0] &&
442 stricmp(title_ext, "bmp") != 0 &&
443 stricmp(title_ext, "gif") != 0 &&
444 stricmp(title_ext, "jpg") != 0 &&
445 stricmp(title_ext, "png") != 0)
446 #else
447 if (TitleImage[0] &&
448 strcmp(title_ext, "bmp") != 0 &&
449 strcmp(title_ext, "gif") != 0 &&
450 strcmp(title_ext, "jpg") != 0 &&
451 strcmp(title_ext, "png") != 0)
452 #endif // WIN32
453 {
454 // Find the title page file...
455 if ((title_file = file_find(Path, TitleImage)) == NULL)
456 {
457 progress_error(HD_ERROR_FILE_NOT_FOUND,
458 "Unable to find title file \"%s\"!", TitleImage);
459 return;
460 }
461
462 // Write a title page from HTML source...
463 if ((fp = fopen(title_file, "rb")) == NULL)
464 {
465 progress_error(HD_ERROR_FILE_NOT_FOUND,
466 "Unable to open title file \"%s\" - %s!",
467 TitleImage, strerror(errno));
468 return;
469 }
470
471 #ifdef _WIN32
472 if (!stricmp(title_ext, "md"))
473 #else
474 if (!strcmp(title_ext, "md"))
475 #endif // _WIN32
476 t = mdReadFile(NULL, fp, file_directory(TitleImage));
477 else
478 t = htmlReadFile(NULL, fp, file_directory(TitleImage));
479
480 htmlFixLinks(t, t, (uchar *)file_directory(TitleImage));
481 fclose(fp);
482
483 write_all(out, t, 0);
484 htmlDeleteTree(t);
485 }
486 else
487 {
488 // Write a "standard" title page with image...
489 if (OutputFiles)
490 fputs("<CENTER><A HREF=\"toc.html\">", out);
491 else
492 fputs("<CENTER><A HREF=\"#CONTENTS\">", out);
493
494 if (TitleImage[0])
495 {
496 image_t *img = image_load(TitleImage, !OutputColor);
497
498 if (OutputFiles)
499 fprintf(out, "<IMG SRC=\"%s\" BORDER=\"0\" WIDTH=\"%d\" HEIGHT=\"%d\" "
500 "ALT=\"%s\"><BR>\n",
501 file_basename((char *)TitleImage), img->width, img->height,
502 title ? (char *)title : "");
503 else
504 fprintf(out, "<IMG SRC=\"%s\" BORDER=\"0\" WIDTH=\"%d\" HEIGHT=\"%d\" "
505 "ALT=\"%s\"><BR>\n",
506 TitleImage, img->width, img->height,
507 title ? (char *)title : "");
508 }
509
510 if (title != NULL)
511 fprintf(out, "<H1>%s</H1></A><BR>\n", title);
512 else
513 fputs("</A>\n", out);
514
515 if (docnumber != NULL)
516 fprintf(out, "%s<BR>\n", docnumber);
517
518 if (author != NULL)
519 fprintf(out, "%s<BR>\n", author);
520
521 if (copyright != NULL)
522 fprintf(out, "%s<BR>\n", copyright);
523
524 fputs("</CENTER>\n", out);
525 }
526 }
527
528
529 /*
530 * 'write_all()' - Write all markup text for the given tree.
531 */
532
533 static int /* O - Current column */
write_all(FILE * out,tree_t * t,int col)534 write_all(FILE *out, /* I - Output file */
535 tree_t *t, /* I - Document tree */
536 int col) /* I - Current column */
537 {
538 if (out == NULL)
539 return (0);
540
541 while (t != NULL)
542 {
543 col = write_node(out, t, col);
544
545 if (t->markup != MARKUP_HEAD && t->markup != MARKUP_TITLE)
546 col = write_all(out, t->child, col);
547
548 col = write_nodeclose(out, t, col);
549
550 t = t->next;
551 }
552
553 return (col);
554 }
555
556
557 /*
558 * 'write_node()' - Write a single tree node.
559 */
560
561 static int /* O - Current column */
write_node(FILE * out,tree_t * t,int col)562 write_node(FILE *out, /* I - Output file */
563 tree_t *t, /* I - Document tree node */
564 int col) /* I - Current column */
565 {
566 int i; /* Looping var */
567 uchar *ptr, /* Pointer to output string */
568 *entity, /* Entity string */
569 *src, /* Source image */
570 *realsrc, /* Real source image */
571 newsrc[1024]; /* New source image filename */
572
573
574 if (out == NULL)
575 return (0);
576
577 switch (t->markup)
578 {
579 case MARKUP_NONE :
580 if (t->data == NULL)
581 break;
582
583 if (t->preformatted)
584 {
585 for (ptr = t->data; *ptr; ptr ++)
586 fputs((char *)iso8859(*ptr), out);
587
588 if (t->data[0] && t->data[strlen((char *)t->data) - 1] == '\n')
589 col = 0;
590 else
591 col += strlen((char *)t->data);
592 }
593 else
594 {
595 if ((col + (int)strlen((char *)t->data)) > 72 && col > 0)
596 {
597 putc('\n', out);
598 col = 0;
599 }
600
601 for (ptr = t->data; *ptr; ptr ++)
602 fputs((char *)iso8859(*ptr), out);
603
604 col += strlen((char *)t->data);
605
606 if (col > 72)
607 {
608 putc('\n', out);
609 col = 0;
610 }
611 }
612 break;
613
614 case MARKUP_COMMENT :
615 case MARKUP_UNKNOWN :
616 fputs("\n<!--", out);
617 for (ptr = t->data; *ptr; ptr ++)
618 fputs((char *)iso8859(*ptr), out);
619 fputs("-->\n", out);
620 col = 0;
621 break;
622
623 case MARKUP_AREA :
624 case MARKUP_BODY :
625 case MARKUP_DOCTYPE :
626 case MARKUP_ERROR :
627 case MARKUP_FILE :
628 case MARKUP_HEAD :
629 case MARKUP_HTML :
630 case MARKUP_MAP :
631 case MARKUP_META :
632 case MARKUP_TITLE :
633 break;
634
635 case MARKUP_BR :
636 case MARKUP_CENTER :
637 case MARKUP_DD :
638 case MARKUP_DL :
639 case MARKUP_DT :
640 case MARKUP_H1 :
641 case MARKUP_H2 :
642 case MARKUP_H3 :
643 case MARKUP_H4 :
644 case MARKUP_H5 :
645 case MARKUP_H6 :
646 case MARKUP_H7 :
647 case MARKUP_H8 :
648 case MARKUP_H9 :
649 case MARKUP_H10 :
650 case MARKUP_H11 :
651 case MARKUP_H12 :
652 case MARKUP_H13 :
653 case MARKUP_H14 :
654 case MARKUP_H15 :
655 case MARKUP_HR :
656 case MARKUP_LI :
657 case MARKUP_OL :
658 case MARKUP_P :
659 case MARKUP_PRE :
660 case MARKUP_TABLE :
661 case MARKUP_TR :
662 case MARKUP_UL :
663 if (col > 0)
664 {
665 putc('\n', out);
666 col = 0;
667 }
668
669 default :
670 if (t->markup == MARKUP_IMG && OutputFiles &&
671 (src = htmlGetVariable(t, (uchar *)"SRC")) != NULL &&
672 (realsrc = htmlGetVariable(t, (uchar *)"REALSRC")) != NULL)
673 {
674 /*
675 * Update and copy local images...
676 */
677
678 if (file_method((char *)src) == NULL &&
679 src[0] != '/' && src[0] != '\\' &&
680 (!isalpha(src[0]) || src[1] != ':'))
681 {
682 image_copy((char *)src, (char *)realsrc, OutputPath);
683 strlcpy((char *)newsrc, file_basename((char *)src), sizeof(newsrc));
684 htmlSetVariable(t, (uchar *)"SRC", newsrc);
685 }
686 }
687
688 if (t->markup != MARKUP_EMBED)
689 {
690 col += fprintf(out, "<%s", _htmlMarkups[t->markup]);
691 for (i = 0; i < t->nvars; i ++)
692 {
693 if (strcasecmp((char *)t->vars[i].name, "BREAK") == 0 &&
694 t->markup == MARKUP_HR)
695 continue;
696
697 if (strcasecmp((char *)t->vars[i].name, "REALSRC") == 0 &&
698 t->markup == MARKUP_IMG)
699 continue;
700
701 if (strncasecmp((char *)t->vars[i].name, "_HD_", 4) == 0)
702 continue;
703
704 if (col > 72 && !t->preformatted)
705 {
706 putc('\n', out);
707 col = 0;
708 }
709
710 if (col > 0)
711 {
712 putc(' ', out);
713 col ++;
714 }
715
716 if (t->vars[i].value == NULL)
717 col += fprintf(out, "%s", t->vars[i].name);
718 else
719 {
720 col += fprintf(out, "%s=\"", t->vars[i].name);
721 for (ptr = t->vars[i].value; *ptr; ptr ++)
722 {
723 entity = iso8859(*ptr);
724 fputs((char *)entity, out);
725 col += strlen((char *)entity);
726 }
727
728 putc('\"', out);
729 col ++;
730 }
731 }
732
733 putc('>', out);
734 col ++;
735
736 if (col > 72 && !t->preformatted)
737 {
738 putc('\n', out);
739 col = 0;
740 }
741 }
742 break;
743 }
744
745 return (col);
746 }
747
748
749 /*
750 * 'write_nodeclose()' - Close a single tree node.
751 */
752
753 static int /* O - Current column */
write_nodeclose(FILE * out,tree_t * t,int col)754 write_nodeclose(FILE *out, /* I - Output file */
755 tree_t *t, /* I - Document tree node */
756 int col) /* I - Current column */
757 {
758 if (out == NULL)
759 return (0);
760
761 if (t->markup != MARKUP_HEAD && t->markup != MARKUP_TITLE)
762 {
763 if (col > 72 && !t->preformatted)
764 {
765 putc('\n', out);
766 col = 0;
767 }
768
769 switch (t->markup)
770 {
771 case MARKUP_BODY :
772 case MARKUP_ERROR :
773 case MARKUP_FILE :
774 case MARKUP_HEAD :
775 case MARKUP_HTML :
776 case MARKUP_NONE :
777 case MARKUP_TITLE :
778
779 case MARKUP_APPLET :
780 case MARKUP_AREA :
781 case MARKUP_BR :
782 case MARKUP_COMMENT :
783 case MARKUP_DOCTYPE :
784 case MARKUP_EMBED :
785 case MARKUP_HR :
786 case MARKUP_IMG :
787 case MARKUP_INPUT :
788 case MARKUP_ISINDEX :
789 case MARKUP_LINK :
790 case MARKUP_META :
791 case MARKUP_NOBR :
792 case MARKUP_SPACER :
793 case MARKUP_WBR :
794 case MARKUP_UNKNOWN :
795 break;
796
797 case MARKUP_CENTER :
798 case MARKUP_DD :
799 case MARKUP_DL :
800 case MARKUP_DT :
801 case MARKUP_H1 :
802 case MARKUP_H2 :
803 case MARKUP_H3 :
804 case MARKUP_H4 :
805 case MARKUP_H5 :
806 case MARKUP_H6 :
807 case MARKUP_H7 :
808 case MARKUP_H8 :
809 case MARKUP_H9 :
810 case MARKUP_H10 :
811 case MARKUP_H11 :
812 case MARKUP_H12 :
813 case MARKUP_H13 :
814 case MARKUP_H14 :
815 case MARKUP_H15 :
816 case MARKUP_LI :
817 case MARKUP_OL :
818 case MARKUP_P :
819 case MARKUP_PRE :
820 case MARKUP_TABLE :
821 case MARKUP_TR :
822 case MARKUP_UL :
823 fprintf(out, "</%s>\n", _htmlMarkups[t->markup]);
824 col = 0;
825 break;
826
827 default :
828 col += fprintf(out, "</%s>", _htmlMarkups[t->markup]);
829 break;
830 }
831 }
832
833 return (col);
834 }
835
836
837 /*
838 * 'write_toc()' - Write all markup text for the given table-of-contents.
839 */
840
841 static int /* O - Current column */
write_toc(FILE * out,tree_t * t,int col)842 write_toc(FILE *out, /* I - Output file */
843 tree_t *t, /* I - Document tree */
844 int col) /* I - Current column */
845 {
846 if (out == NULL)
847 return (0);
848
849 while (t != NULL)
850 {
851 if (htmlGetVariable(t, (uchar *)"_HD_OMIT_TOC") == NULL)
852 {
853 col = write_node(out, t, col);
854
855 if (t->markup != MARKUP_HEAD && t->markup != MARKUP_TITLE)
856 col = write_toc(out, t->child, col);
857
858 col = write_nodeclose(out, t, col);
859 }
860
861 t = t->next;
862 }
863
864 return (col);
865 }
866
867
868 /*
869 * 'get_title()' - Get the title string for the given document...
870 */
871
872 static uchar * /* O - Title string */
get_title(tree_t * doc)873 get_title(tree_t *doc) /* I - Document tree */
874 {
875 uchar *temp; /* Temporary pointer to title */
876
877
878 while (doc != NULL)
879 {
880 if (doc->markup == MARKUP_TITLE)
881 return (htmlGetText(doc->child));
882 else if (doc->child != NULL)
883 if ((temp = get_title(doc->child)) != NULL)
884 return (temp);
885
886 doc = doc->next;
887 }
888
889 return (NULL);
890 }
891
892
893 /*
894 * 'add_link()' - Add a named link...
895 */
896
897 static void
add_link(uchar * name,uchar * filename)898 add_link(uchar *name, /* I - Name of link */
899 uchar *filename) /* I - File for link */
900 {
901 link_t *temp; /* New name */
902
903
904 if ((temp = find_link(name)) != NULL)
905 temp->filename = filename;
906 else
907 {
908 // See if we need to allocate memory for links...
909 if (num_links >= alloc_links)
910 {
911 // Allocate more links...
912 alloc_links += ALLOC_LINKS;
913
914 if (num_links == 0)
915 temp = (link_t *)malloc(sizeof(link_t) * alloc_links);
916 else
917 temp = (link_t *)realloc(links, sizeof(link_t) * alloc_links);
918
919 if (temp == NULL)
920 {
921 progress_error(HD_ERROR_OUT_OF_MEMORY,
922 "Unable to allocate memory for %d links - %s",
923 (int)alloc_links, strerror(errno));
924 alloc_links -= ALLOC_LINKS;
925 return;
926 }
927
928 links = temp;
929 }
930
931 // Add a new link...
932 temp = links + num_links;
933 num_links ++;
934
935 strlcpy((char *)temp->name, (char *)name, sizeof(temp->name));
936 temp->filename = filename;
937
938 if (num_links > 1)
939 qsort(links, num_links, sizeof(link_t), (compare_func_t)compare_links);
940 }
941 }
942
943
944 /*
945 * 'find_link()' - Find a named link...
946 */
947
948 static link_t *
find_link(uchar * name)949 find_link(uchar *name) /* I - Name to find */
950 {
951 uchar *target; /* Pointer to target name portion */
952 link_t key, /* Search key */
953 *match; /* Matching name entry */
954
955
956 if (name == NULL || num_links == 0)
957 return (NULL);
958
959 if ((target = (uchar *)file_target((char *)name)) == NULL)
960 return (NULL);
961
962 strlcpy((char *)key.name, (char *)target, sizeof(key.name));
963 match = (link_t *)bsearch(&key, links, num_links, sizeof(link_t),
964 (compare_func_t)compare_links);
965
966 return (match);
967 }
968
969
970 /*
971 * 'compare_links()' - Compare two named links.
972 */
973
974 static int /* O - 0 = equal, -1 or 1 = not equal */
compare_links(link_t * n1,link_t * n2)975 compare_links(link_t *n1, /* I - First name */
976 link_t *n2) /* I - Second name */
977 {
978 return (strcasecmp((char *)n1->name, (char *)n2->name));
979 }
980
981
982 /*
983 * 'scan_links()' - Scan a document for link targets, and keep track of
984 * the files they are in...
985 */
986
987 static void
scan_links(tree_t * t,uchar * filename)988 scan_links(tree_t *t, /* I - Document tree */
989 uchar *filename) /* I - Filename */
990 {
991 uchar *name; /* Name of link */
992
993
994 while (t != NULL)
995 {
996 if (t->markup == MARKUP_FILE)
997 scan_links(t->child, (uchar *)file_basename((char *)htmlGetVariable(t, (uchar *)"_HD_FILENAME")));
998 else if (t->markup == MARKUP_A &&
999 (name = htmlGetVariable(t, (uchar *)"NAME")) != NULL)
1000 {
1001 add_link(name, filename);
1002 scan_links(t->child, filename);
1003 }
1004 else if (t->child != NULL)
1005 scan_links(t->child, filename);
1006
1007 t = t->next;
1008 }
1009 }
1010
1011
1012 /*
1013 * 'update_links()' - Update links as needed.
1014 */
1015
1016 static void
update_links(tree_t * t,uchar * filename)1017 update_links(tree_t *t, /* I - Document tree */
1018 uchar *filename) /* I - Current filename */
1019 {
1020 link_t *link; /* Link */
1021 uchar *href; /* Reference name */
1022 uchar newhref[1024]; /* New reference name */
1023
1024
1025 filename = (uchar *)file_basename((char *)filename);
1026
1027 if (OutputFiles)
1028 {
1029 /*
1030 * Need to preserve/add filenames.
1031 */
1032
1033 while (t != NULL)
1034 {
1035 if (t->markup == MARKUP_A &&
1036 (href = htmlGetVariable(t, (uchar *)"HREF")) != NULL)
1037 {
1038 /*
1039 * Update this link as needed...
1040 */
1041
1042 if (href[0] == '#' &&
1043 (link = find_link(href)) != NULL)
1044 {
1045 #if defined(WIN32) || defined(__EMX__)
1046 if (filename == NULL ||
1047 strcasecmp((char *)filename, (char *)link->filename) != 0)
1048 #else
1049 if (filename == NULL ||
1050 strcmp((char *)filename, (char *)link->filename) != 0)
1051 #endif /* WIN32 || __EMX__ */
1052 {
1053 snprintf((char *)newhref, sizeof(newhref), "%s%s",
1054 link->filename, href);
1055 htmlSetVariable(t, (uchar *)"HREF", newhref);
1056 }
1057 }
1058 }
1059
1060 if (t->child != NULL)
1061 {
1062 if (t->markup == MARKUP_FILE)
1063 update_links(t->child, htmlGetVariable(t, (uchar *)"_HD_FILENAME"));
1064 else
1065 update_links(t->child, filename);
1066 }
1067
1068 t = t->next;
1069 }
1070 }
1071 else
1072 {
1073 /*
1074 * Need to strip filenames.
1075 */
1076
1077 while (t != NULL)
1078 {
1079 if (t->markup == MARKUP_A &&
1080 (href = htmlGetVariable(t, (uchar *)"HREF")) != NULL)
1081 {
1082 /*
1083 * Update this link as needed...
1084 */
1085
1086 if (href[0] != '#' && file_method((char *)href) == NULL &&
1087 (link = find_link(href)) != NULL)
1088 {
1089 snprintf((char *)newhref, sizeof(newhref), "#%s", link->name);
1090 htmlSetVariable(t, (uchar *)"HREF", newhref);
1091 }
1092 }
1093
1094 if (t->child != NULL)
1095 update_links(t->child, filename);
1096
1097 t = t->next;
1098 }
1099 }
1100 }
1101