+ * Before we start outputting the HTML Help files, check
+ * whether there's even going to _be_ an index file: we omit it
+ * if the index contains nothing.
+ */
+ hhk_filename = conf.hhk_filename;
+ if (hhk_filename) {
+ int ok = FALSE;
+ int i;
+ indexentry *entry;
+
+ for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++) {
+ htmlindex *hi = (htmlindex *)entry->backend_data;
+
+ if (hi->nrefs > 0) {
+ ok = TRUE; /* found an index entry */
+ break;
+ }
+ }
+
+ if (!ok)
+ hhk_filename = NULL;
+ }
+
+ /*
+ * Output the MS HTML Help supporting files, if requested.
+ *
+ * A good unofficial reference for these is <http://chmspec.nongnu.org/>.
+ */
+ if (conf.hhp_filename) {
+ htmlfile *f;
+ htmloutput ho;
+
+ ho.charset = CS_CP1252; /* as far as I know, HHP files are */
+ ho.restrict_charset = CS_CP1252; /* hardwired to this charset */
+ ho.cstate = charset_init_state;
+ ho.ver = HTML_4; /* *shrug* */
+ ho.state = HO_NEUTRAL;
+ ho.contents_level = 0;
+ ho.hackflags = HO_HACK_QUOTENOTHING;
+
+ ho.fp = fopen(conf.hhp_filename, "w");
+ if (!ho.fp)
+ error(err_cantopenw, conf.hhp_filename);
+
+ fprintf(ho.fp,
+ "[OPTIONS]\n"
+ /* Binary TOC required for Next/Previous nav to work */
+ "Binary TOC=Yes\n"
+ "Compatibility=1.1 or later\n"
+ "Compiled file=%s\n"
+ "Default Window=main\n"
+ "Default topic=%s\n"
+ "Display compile progress=Yes\n"
+ "Full-text search=Yes\n"
+ "Title=", conf.chm_filename, files.head->filename);
+
+ ho.hacklimit = 255;
+ html_words(&ho, topsect->title->words, NOTHING,
+ NULL, keywords, &conf);
+
+ fprintf(ho.fp, "\n");
+
+ /*
+ * These two entries don't seem to be remotely necessary
+ * for a successful run of the help _compiler_, but
+ * omitting them causes the GUI Help Workshop to behave
+ * rather strangely if you try to load the help project
+ * into that and edit it.
+ */
+ if (conf.hhc_filename)
+ fprintf(ho.fp, "Contents file=%s\n", conf.hhc_filename);
+ if (hhk_filename)
+ fprintf(ho.fp, "Index file=%s\n", hhk_filename);
+
+ fprintf(ho.fp, "\n[WINDOWS]\nmain=\"");
+
+ ho.hackflags |= HO_HACK_OMITQUOTES;
+ ho.hacklimit = 255;
+ html_words(&ho, topsect->title->words, NOTHING,
+ NULL, keywords, &conf);
+
+ fprintf(ho.fp, "\",\"%s\",\"%s\",\"%s\",,,,,,"
+ /* This first magic number is fsWinProperties, controlling
+ * Navigation Pane options and the like.
+ * Constants HHWIN_PROP_* in htmlhelp.h. */
+ "0x62520,,"
+ /* This second number is fsToolBarFlags, mainly controlling
+ * toolbar buttons. Constants HHWIN_BUTTON_*.
+ * NOTE: there are two pairs of bits for Next/Previous
+ * buttons: 7/8 (which do nothing useful), and 21/22
+ * (which work). (Neither of these are exposed in the HHW
+ * UI, but they work fine in HH.) We use the latter. */
+ "0x60304e,,,,,,,,0\n",
+ conf.hhc_filename ? conf.hhc_filename : "",
+ hhk_filename ? hhk_filename : "",
+ files.head->filename);
+
+ /*
+ * The [FILES] section is also not necessary for
+ * compilation (hhc appears to build up a list of needed
+ * files just by following links from the given starting
+ * points), but useful for loading the project into HHW.
+ */
+ fprintf(ho.fp, "\n[FILES]\n");
+ for (f = files.head; f; f = f->next)
+ fprintf(ho.fp, "%s\n", f->filename);
+
+ fclose(ho.fp);
+ }
+ if (conf.hhc_filename) {
+ htmlfile *f;
+ htmlsect *s, *a;
+ htmloutput ho;
+ int currdepth = 0;
+
+ ho.fp = fopen(conf.hhc_filename, "w");
+ if (!ho.fp)
+ error(err_cantopenw, conf.hhc_filename);
+
+ ho.charset = CS_CP1252; /* as far as I know, HHC files are */
+ ho.restrict_charset = CS_CP1252; /* hardwired to this charset */
+ ho.cstate = charset_init_state;
+ ho.ver = HTML_4; /* *shrug* */
+ ho.state = HO_NEUTRAL;
+ ho.contents_level = 0;
+ ho.hackflags = HO_HACK_QUOTEQUOTES;
+
+ /*
+ * Magic DOCTYPE which seems to work for .HHC files. I'm
+ * wary of trying to change it!
+ */
+ fprintf(ho.fp, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"
+ "<HTML><HEAD>\n"
+ "<META HTTP-EQUIV=\"Content-Type\" "
+ "CONTENT=\"text/html; charset=%s\">\n"
+ "</HEAD><BODY><UL>\n",
+ charset_to_mimeenc(conf.output_charset));
+
+ for (f = files.head; f; f = f->next) {
+ /*
+ * For each HTML file, write out a contents entry.
+ */
+ int depth, leaf = TRUE;
+
+ /*
+ * Determine the depth of this file in the contents
+ * tree.
+ *
+ * If the file contains no sections, it is assumed to
+ * have depth zero.
+ */
+ depth = 0;
+ if (f->first)
+ for (a = f->first->parent; a && a->type != TOP; a = a->parent)
+ depth++;
+
+ /*
+ * Determine if this file is a leaf file, by
+ * trawling the section list to see if there's any
+ * section with an ancestor in this file but which
+ * is not itself in this file.
+ *
+ * Special case: for contents purposes, the TOP
+ * file is not considered to be the parent of the
+ * chapter files, so it's always a leaf.
+ *
+ * A file with no sections in it is also a leaf.
+ */
+ if (f->first && f->first->type != TOP) {
+ for (s = f->first; s; s = s->next) {
+ htmlsect *a;
+
+ if (leaf && s->file != f) {
+ for (a = s; a; a = a->parent)
+ if (a->file == f) {
+ leaf = FALSE;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Now write out our contents entry.
+ */
+ while (currdepth < depth) {
+ fprintf(ho.fp, "<UL>\n");
+ currdepth++;
+ }
+ while (currdepth > depth) {
+ fprintf(ho.fp, "</UL>\n");
+ currdepth--;
+ }
+ /* fprintf(ho.fp, "<!-- depth=%d -->", depth); */
+ fprintf(ho.fp, "<LI><OBJECT TYPE=\"text/sitemap\">"
+ "<PARAM NAME=\"Name\" VALUE=\"");
+ ho.hacklimit = 255;
+ if (f->first->title)
+ html_words(&ho, f->first->title->words, NOTHING,
+ NULL, keywords, &conf);
+ else if (f->first->type == INDEX)
+ html_text(&ho, conf.index_text);
+ fprintf(ho.fp, "\"><PARAM NAME=\"Local\" VALUE=\"%s\">"
+ "<PARAM NAME=\"ImageNumber\" VALUE=\"%d\"></OBJECT>\n",
+ f->filename, leaf ? 11 : 1);
+ }
+
+ while (currdepth > 0) {
+ fprintf(ho.fp, "</UL>\n");
+ currdepth--;
+ }
+
+ fprintf(ho.fp, "</UL></BODY></HTML>\n");
+
+ cleanup(&ho);
+ }
+ if (hhk_filename) {
+ htmlfile *f;
+ htmloutput ho;
+ indexentry *entry;
+ int i;
+
+ /*
+ * First make a pass over all HTML files and set their
+ * `temp' fields to zero, because we're about to use them.
+ */
+ for (f = files.head; f; f = f->next)
+ f->temp = 0;
+
+ ho.fp = fopen(hhk_filename, "w");
+ if (!ho.fp)
+ error(err_cantopenw, hhk_filename);
+
+ ho.charset = CS_CP1252; /* as far as I know, HHK files are */
+ ho.restrict_charset = CS_CP1252; /* hardwired to this charset */
+ ho.cstate = charset_init_state;
+ ho.ver = HTML_4; /* *shrug* */
+ ho.state = HO_NEUTRAL;
+ ho.contents_level = 0;
+ ho.hackflags = HO_HACK_QUOTEQUOTES;
+
+ /*
+ * Magic DOCTYPE which seems to work for .HHK files. I'm
+ * wary of trying to change it!
+ */
+ fprintf(ho.fp, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"
+ "<HTML><HEAD>\n"
+ "<META HTTP-EQUIV=\"Content-Type\" "
+ "CONTENT=\"text/html; charset=%s\">\n"
+ "</HEAD><BODY><UL>\n",
+ charset_to_mimeenc(conf.output_charset));
+
+ /*
+ * Go through the index terms and output each one.
+ */
+ for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++) {
+ htmlindex *hi = (htmlindex *)entry->backend_data;
+ int j;
+
+ if (hi->nrefs > 0) {
+ fprintf(ho.fp, "<LI><OBJECT TYPE=\"text/sitemap\">\n"
+ "<PARAM NAME=\"Name\" VALUE=\"");
+ ho.hacklimit = 255;
+ html_words(&ho, entry->text, NOTHING,
+ NULL, keywords, &conf);
+ fprintf(ho.fp, "\">\n");
+
+ for (j = 0; j < hi->nrefs; j++) {
+ htmlindexref *hr =
+ (htmlindexref *)hi->refs[j]->private_data;
+
+ /*
+ * Use the temp field to ensure we don't
+ * reference the same file more than once.
+ */
+ if (!hr->section->file->temp) {
+ fprintf(ho.fp, "<PARAM NAME=\"Local\" VALUE=\"%s\">\n",
+ hr->section->file->filename);
+ hr->section->file->temp = 1;
+ }
+
+ hr->referenced = TRUE;
+ }
+
+ fprintf(ho.fp, "</OBJECT>\n");
+
+ /*
+ * Now go through those files and re-clear the temp
+ * fields ready for the _next_ index term.
+ */
+ for (j = 0; j < hi->nrefs; j++) {
+ htmlindexref *hr =
+ (htmlindexref *)hi->refs[j]->private_data;
+ hr->section->file->temp = 0;
+ }
+ }
+ }
+
+ fprintf(ho.fp, "</UL></BODY></HTML>\n");
+ cleanup(&ho);
+ }
+
+ /*
+ * Go through and check that no index fragments were referenced
+ * without being generated, or indeed vice versa.
+ *
+ * (When I actually get round to freeing everything, this can
+ * probably be the freeing loop as well.)
+ */
+ for (p = sourceform; p; p = p->next) {
+ word *w;
+ for (w = p->words; w; w = w->next)
+ if (w->type == word_IndexRef) {
+ htmlindexref *hr = (htmlindexref *)w->private_data;
+
+ assert(!hr->referenced == !hr->generated);
+ }
+ }
+
+ /*
+ * Free all the working data.
+ */
+ {
+ htmlfragment *frag;
+ while ( (frag = (htmlfragment *)delpos234(files.frags, 0)) != NULL ) {
+ /*
+ * frag->fragment is dynamically allocated, but will be
+ * freed when we process the htmlsect structure which
+ * it is attached to.
+ */
+ sfree(frag);
+ }
+ freetree234(files.frags);
+ }
+ /*
+ * The strings in files.files are all owned by their containing
+ * htmlfile structures, so there's no need to free them here.