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