/* * xhtml backend for Halibut * (initial implementation by James Aylett) * * Still to do: * * +++ doesn't handle non-breaking hyphens. Not sure how to yet. * +++ entity names (from a file -- ideally supply normal SGML files) * +++ configuration directive to file split where the current layout * code wouldn't. Needs changes to _ponder_layout() and _do_paras(), * perhaps others. * * Limitations: * * +++ biblio/index references target the nearest section marker, rather * than having a dedicated target themselves. In large bibliographies * this will cause problems. (The solution is to fake up a response * from xhtml_find_section(), probably linking it into the sections * chain just in case we need it again, and to make freeing it up * easier.) docsrc.pl used to work as we do, however, and SGT agrees that * this is acceptable for now. * +++ can't cope with leaf-level == 0. It's all to do with the * top-level file not being normal, probably not even having a valid * section level, and stuff like that. I question whether this is an * issue, frankly; small manuals that fit on one page should probably * not be written in halibut at all. */ #include #include #include #include #include "halibut.h" enum outputtype { OT_XHTML = 0x01, OT_CHM = 0x02, OT_HTML4 = 0x04, OT_HTML5 = 0x08 }; int g_outputtype = OT_XHTML; #define is_chm() ( (g_outputtype & OT_CHM) == OT_CHM ) #define is_xhtml() ( (g_outputtype & OT_XHTML) == OT_XHTML ) #define is_html5() ( (g_outputtype & OT_HTML5) == OT_HTML5 ) #define gettagtxt_br() ( is_xhtml() ? "
" : "
" ) #define gettagtxt_hr() ( is_xhtml() ? "
" : "
" ) struct xhtmlsection_Struct { struct xhtmlsection_Struct *next; /* next sibling (NULL if split across files) */ struct xhtmlsection_Struct *child; /* NULL if split across files */ struct xhtmlsection_Struct *parent; /* NULL if split across files */ struct xhtmlsection_Struct *chain; /* single structure independent of weird trees */ paragraph *para; struct xhtmlfile_Struct *file; /* which file is this a part of? */ char *fragment; /* fragment id within the file */ int level; }; struct xhtmlfile_Struct { struct xhtmlfile_Struct *next; struct xhtmlfile_Struct *child; struct xhtmlfile_Struct *parent; char *filename; struct xhtmlsection_Struct *sections; /* sections within this file (only one for non-leaf) */ int is_leaf; /* is this file a leaf file, ie does it not have any children? */ }; typedef struct xhtmlsection_Struct xhtmlsection; typedef struct xhtmlfile_Struct xhtmlfile; typedef struct xhtmlindex_Struct xhtmlindex; struct xhtmlindex_Struct { int nsection; int size; xhtmlsection **sections; }; typedef struct { int just_numbers; wchar_t *number_suffix; } xhtmlheadfmt; typedef struct { int contents_depth[6]; int leaf_contains_contents; int leaf_level; int leaf_smallest_contents; int include_version_id; wchar_t *author, *description; wchar_t *html_lang, *meta_charset; wchar_t *head_start, *head_middle, *head_end, *body, *body_start, *body_end; ustr_slist *meta_append; wchar_t *address_start, *address_end, *nav_attrs; wchar_t *rlink_prefix, *rlink_suffix; wchar_t *chm_toc_file, *chm_ind_file; int suppress_address; xhtmlheadfmt fchapter, *fsect; int nfsect; int keywordfragments; } xhtmlconfig; /*static void xhtml_level(paragraph *, int); static void xhtml_level_0(paragraph *); static void xhtml_docontents(FILE *, paragraph *, int); static void xhtml_dosections(FILE *, paragraph *, int); static void xhtml_dobody(FILE *, paragraph *, int);*/ static void xhtml_doheader(FILE *, word *); static void xhtml_dofooter(FILE *); static void xhtml_versionid(FILE *, word *, int); static void xhtml_utostr(wchar_t *, char **); static int xhtml_para_level(paragraph *); static int xhtml_reservedchar(int); static int xhtml_convert(wchar_t *, char **, int); static void xhtml_rdaddwc(rdstringc *, word *, word *); static void xhtml_para(FILE *, word *); static void xhtml_codepara(FILE *, word *); static void xhtml_heading(FILE *, paragraph *); static void chm_doheader(FILE *, word *); static void chm_dofooter(FILE *); /* File-global variables are much easier than passing these things * all over the place. Evil, but easier. We can replace this with a single * structure at some point. */ static xhtmlconfig conf; static keywordlist *keywords; static indexdata *idx; static xhtmlfile *topfile; static xhtmlsection *topsection; static paragraph *sourceparas; static xhtmlfile *lastfile; static xhtmlfile *xhtml_last_file = NULL; static int last_level = -1; static xhtmlsection *currentsection; static FILE* chm_toc = NULL; static FILE* chm_ind = NULL; static const wchar_t* normalizehtmlkeywordprefix(const wchar_t*s) // [x]html... --> html... { return s && utolower(s[0]) == 'x' && utolower(s[1]) == 'h' && utolower(s[2]) == 't' ? s + 1 : s; } static const wchar_t* ishtmlkeyword(const wchar_t*a, const wchar_t*b) { return !ustricmp(normalizehtmlkeywordprefix(a), normalizehtmlkeywordprefix(b)) ? a : 0; } static wchar_t* configurekeyword(wchar_t**dst, const wchar_t*keyword, const paragraph*source) { if (ishtmlkeyword(source->keyword, keyword)) return *dst = uadv(source->keyword); else return 0; } static xhtmlconfig xhtml_configure(paragraph * source) { xhtmlconfig ret; /* * Defaults. */ ret.contents_depth[0] = 2; ret.contents_depth[1] = 3; ret.contents_depth[2] = 4; ret.contents_depth[3] = 5; ret.contents_depth[4] = 6; ret.contents_depth[5] = 7; ret.leaf_level = 2; ret.leaf_smallest_contents = 4; ret.leaf_contains_contents = FALSE; ret.include_version_id = TRUE; ret.author = NULL; ret.description = NULL; ret.html_lang = NULL; ret.meta_charset = NULL; ret.head_start = ret.head_middle = ret.head_end = NULL; ret.body = NULL; ret.body_start = NULL; ret.body_end = NULL; ret.meta_append = NULL; ret.address_start = NULL; ret.address_end = NULL; ret.nav_attrs = NULL; ret.suppress_address = FALSE; ret.chm_toc_file = NULL; ret.chm_ind_file = NULL; chm_toc = NULL; chm_ind = NULL; ret.fchapter.just_numbers = FALSE; ret.fchapter.number_suffix = ustrdup(L": "); ret.nfsect = 2; ret.fsect = mknewa(xhtmlheadfmt, ret.nfsect); ret.fsect[0].just_numbers = FALSE; ret.fsect[0].number_suffix = ustrdup(L": "); ret.fsect[1].just_numbers = TRUE; ret.fsect[1].number_suffix = ustrdup(L" "); ret.rlink_prefix = NULL; ret.rlink_suffix = NULL; ret.keywordfragments = TRUE; for (; source; source = source->next) { if (source->type == para_Config) { if (ishtmlkeyword(source->keyword, L"html-version")) { const wchar_t* v = uadv(source->keyword); if (!ustricmp(v, L"html4")) g_outputtype = OT_HTML4; // Note: Upstream treats this as "W3C HTML 4.01 Strict" else if (!ustricmp(v, L"html5")) g_outputtype = OT_HTML5; else if (!ustricmp(v, L"xhtml1.0transitional")) g_outputtype = OT_XHTML; else error(err_whatever, "%ls unknown %ls", source->keyword, v); } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-0")) { ret.contents_depth[0] = utoi(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-1")) { ret.contents_depth[1] = utoi(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-2")) { ret.contents_depth[2] = utoi(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-3")) { ret.contents_depth[3] = utoi(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-4")) { ret.contents_depth[4] = utoi(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-5")) { ret.contents_depth[5] = utoi(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-leaf-level")) { ret.leaf_level = utoi(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-leaf-smallest-contents")) { ret.leaf_smallest_contents = utoi(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-versionid")) { ret.include_version_id = utob(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-leaf-contains-contents")) { ret.leaf_contains_contents = utob(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-suppress-address")) { ret.suppress_address = utob(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-author")) { ret.author = uadv(source->keyword); } else if (!ustricmp(source->keyword, L"chm-toc-file")) { ret.chm_toc_file = uadv(source->keyword); } else if (!ustricmp(source->keyword, L"chm-ind-file")) { ret.chm_ind_file = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"xhtml-description")) { ret.description = uadv(source->keyword); } else if (configurekeyword(&ret.html_lang, L"xhtml-lang", source)) { } else if (configurekeyword(&ret.meta_charset, L"xhtml-meta-charset", source)) { } else if (configurekeyword(&ret.head_start, L"xhtml-head-start", source)) { } else if (configurekeyword(&ret.head_middle, L"xhtml-head-middle", source)) { } else if (configurekeyword(&ret.head_end, L"xhtml-head-end", source)) { } else if (ishtmlkeyword(source->keyword, L"xhtml-body-start")) { ret.body_start = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"xhtml-body-tag")) { ret.body = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"xhtml-body-end")) { ret.body_end = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"html-append-meta")) { ustr_slist_append(&ret.meta_append, uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-address-start")) { ret.address_start = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"xhtml-address-end")) { ret.address_end = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"xhtml-navigation-attributes")) { ret.nav_attrs = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"xhtml-chapter-numeric")) { ret.fchapter.just_numbers = utob(uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-chapter-suffix")) { ustrreplacedup(&ret.fchapter.number_suffix, uadv(source->keyword)); } else if (ishtmlkeyword(source->keyword, L"xhtml-rlink-prefix")) { ret.rlink_prefix = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"xhtml-rlink-suffix")) { ret.rlink_suffix = uadv(source->keyword); } else if (ishtmlkeyword(source->keyword, L"xhtml-section-numeric")) { wchar_t *p = uadv(source->keyword); int n = 0; if (uisdigit(*p)) { n = utoi(p); p = uadv(p); } if (n >= ret.nfsect) { int i; ret.fsect = resize(ret.fsect, n + 1); for (i = ret.nfsect; i <= n; i++) ret.fsect[i] = ret.fsect[ret.nfsect - 1]; ret.nfsect = n + 1; } ret.fsect[n].just_numbers = utob(p); } else if (ishtmlkeyword(source->keyword, L"xhtml-section-suffix")) { wchar_t *p = uadv(source->keyword); int n = 0; if (uisdigit(*p)) { n = utoi(p); p = uadv(p); } if (n >= ret.nfsect) { int i; ret.fsect = resize(ret.fsect, n + 1); for (i = ret.nfsect; i <= n; i++) ret.fsect[i] = ret.fsect[ret.nfsect - 1]; ret.nfsect = n + 1; } ustrreplacedup(&ret.fsect[n].number_suffix, p); } else if (ishtmlkeyword(source->keyword, L"xhtml-keywordfragments")) { ret.keywordfragments = utob(uadv(source->keyword)); } } } /* printf(" !!! leaf_level = %i\n", ret.leaf_level); printf(" !!! contentdepth-0 = %i\n", ret.contents_depth[0]); printf(" !!! contentdepth-1 = %i\n", ret.contents_depth[1]); printf(" !!! contentdepth-2 = %i\n", ret.contents_depth[2]); printf(" !!! contentdepth-3 = %i\n", ret.contents_depth[3]); printf(" !!! contentdepth-4 = %i\n", ret.contents_depth[4]); printf(" !!! contentdepth-5 = %i\n", ret.contents_depth[5]); printf(" !!! leaf_contains_contents = %i\n", ret.leaf_contains_contents); */ return ret; } static xhtmlsection *xhtml_new_section(xhtmlsection * last) { xhtmlsection *ret = mknew(xhtmlsection); ret->next = NULL; ret->child = NULL; ret->parent = NULL; ret->chain = last; ret->para = NULL; ret->file = NULL; ret->fragment = NULL; ret->level = -1; /* marker: end of chain */ return ret; } /* Returns NULL or the section that marks that paragraph */ static xhtmlsection *xhtml_find_section(paragraph * p) { xhtmlsection *ret = topsection; if (xhtml_para_level(p) == -1) { /* first, we back-track to a section paragraph */ paragraph *p2 = sourceparas; paragraph *p3 = NULL; while (p2 && p2 != p) { if (xhtml_para_level(p2) != -1) { p3 = p2; } p2 = p2->next; } if (p3 == NULL) { /* for some reason, we couldn't find a section before this paragraph ... ? */ /* Note that this can happen, if you have a cross-reference to before the first chapter starts. * So don't do that, then. */ return NULL; } p = p3; } while (ret && ret->para != p) { /* printf(" xhtml_find_section(): checking %s for para @ %p\n", ret->fragment, p);*/ ret = ret->chain; } return ret; } static xhtmlfile *xhtml_new_file(xhtmlsection * sect) { xhtmlfile *ret = mknew(xhtmlfile); ret->next = NULL; ret->child = NULL; ret->parent = NULL; ret->filename = NULL; ret->sections = sect; ret->is_leaf = (sect != NULL && sect->level == conf.leaf_level); if (sect == NULL) { if (conf.leaf_level == 0) { /* currently unused */ #define FILENAME_MANUAL "Manual.html" #define FILENAME_CONTENTS "Contents.html" ret->filename = smalloc(strlen(FILENAME_MANUAL) + 1); sprintf(ret->filename, FILENAME_MANUAL); } else { ret->filename = smalloc(strlen(FILENAME_CONTENTS) + 1); sprintf(ret->filename, FILENAME_CONTENTS); } } else { paragraph *p = sect->para; rdstringc fname_c = { 0, 0, NULL }; char *c; word *w; for (w = (p->kwtext) ? (p->kwtext) : (p->words); w; w = w->next) { switch (removeattr(w->type)) { case word_Normal: /*case word_Emph: case word_Code: case word_WeakCode: */ xhtml_utostr(w->text, &c); rdaddsc(&fname_c, c); sfree(c); break; } } rdaddsc(&fname_c, ".html"); ret->filename = rdtrimc(&fname_c); } /* printf(" ! new file '%s', is_leaf == %s\n", ret->filename, (ret->is_leaf)?("true"):("false")); */ return ret; } /* * Walk the tree fixing up files which are actually leaf (ie * have no children) but aren't at leaf level, so they have the * leaf flag set. */ void xhtml_fixup_layout(xhtmlfile * file) { if (file->child == NULL) { file->is_leaf = TRUE; } else { xhtml_fixup_layout(file->child); } if (file->next) xhtml_fixup_layout(file->next); } /* * Create the tree structure so we know where everything goes. * Method: * * Ignoring file splitting, we have three choices with each new section: * * +-----------------+-----------------+ * | | | * X +----X----+ (1) * | | * Y (2) * | * (3) * * Y is the last section we added (currentsect). * If sect is the section we want to add, then: * * (1) if sect->level < currentsect->level * (2) if sect->level == currentsect->level * (3) if sect->level > currentsect->level * * This requires the constraint that you never skip section numbers * (so you can't have a.b.c.d without all of a, a.b and a.b.c existing). * * Note that you _can_ have 1.1.1.1 followed by 1.2 - you can change * more than one level at a time. Lots of asserts, and probably part of * the algorithm here, rely on this being true. (It currently isn't * enforced by halibut, however.) * * File splitting makes this harder. For instance, say we added at (3) * above and now need to add another section. We are splitting at level * 2, ie the level of Y. Z is the last section we added: * * +-----------------+-----------------+ * | | | * X +----X----+ (1) * | | * +----Y----+ (1) * | | * Z (2) * | * (3) * * The (1) case is now split; we need to search upwards to find where * to actually link in. The other two cases remain the same (and will * always be like this). * * File splitting makes this harder, however. The decision of whether * to split to a new file is always on the same condition, however (is * the level of this section higher than the leaf_level configuration * value or not). * * Treating the cases backwards: * * (3) same file if sect->level > conf.leaf_level, otherwise new file * * if in the same file, currentsect->child points to sect * otherwise the linking is done through the file tree (which works * in more or less the same way, ie currentfile->child points to * the new file) * * (2) same file if sect->level > conf.leaf_level, otherwise new file * * if in the same file, currentsect->next points to sect * otherwise file linking and currentfile->next points to the new * file (we know that Z must have caused a new file to be created) * * (1) same file if sect->level > conf.leaf_level, otherwise new file * * this is actually effectively the same case as (2) here, * except that we first have to travel up the sections to figure * out which section this new one will be a sibling of. In doing * so, we may disappear off the top of a file and have to go up * to its parent in the file tree. * */ static void xhtml_ponder_layout(paragraph * p) { xhtmlsection *lastsection; xhtmlsection *currentsect; xhtmlfile *currentfile; lastfile = NULL; topsection = xhtml_new_section(NULL); topfile = xhtml_new_file(NULL); lastsection = topsection; currentfile = topfile; currentsect = topsection; if (conf.leaf_level == 0) { topfile->is_leaf = 1; topfile->sections = topsection; topsection->file = topfile; } for (; p; p = p->next) { int level = xhtml_para_level(p); if (level > 0) { /* actually a section */ xhtmlsection *sect; word *w; char *c; rdstringc fname_c = { 0, 0, NULL }; sect = xhtml_new_section(lastsection); lastsection = sect; sect->para = p; for (w = (p->kwtext2) ? (p->kwtext2) : (p->words); w; w = w->next) { /* kwtext2 because we want numbers only! */ switch (removeattr(w->type)) { case word_Normal: /*case word_Emph: case word_Code: case word_WeakCode: */ xhtml_utostr(w->text, &c); rdaddsc(&fname_c, c); sfree(c); break; } } /* rdaddsc(&fname_c, ".html");*/ sect->fragment = rdtrimc(&fname_c); sect->level = level; /* printf(" ! adding para @ %p as sect %s, level %i\n", sect->para, sect->fragment, level); */ if (level > currentsect->level) { /* case (3) */ if (level > conf.leaf_level) { /* same file */ assert(currentfile->is_leaf); currentsect->child = sect; sect->parent = currentsect; sect->file = currentfile; /* printf("connected '%s' to existing file '%s' [I]\n", sect->fragment, currentfile->filename); */ currentsect = sect; } else { /* new file */ xhtmlfile *file = xhtml_new_file(sect); assert(!currentfile->is_leaf); currentfile->child = file; sect->file = file; file->parent = currentfile; /* printf("connected '%s' to new file '%s' [I]\n", sect->fragment, file->filename); */ currentfile = file; currentsect = sect; } } else if (level >= currentsect->file->sections->level) { /* Case (1) or (2) *AND* still under the section that starts * the current file. * * I'm not convinced that this couldn't be rolled in with the * final else {} leg further down. It seems a lot of effort * this way. */ if (level > conf.leaf_level) { /* stick within the same file */ assert(currentfile->is_leaf); sect->file = currentfile; while (currentsect && currentsect->level > level && currentsect->file == currentsect->parent->file) { currentsect = currentsect->parent; } assert(currentsect); currentsect->next = sect; assert(currentsect->level == sect->level); sect->parent = currentsect->parent; currentsect = sect; /* printf("connected '%s' to existing file '%s' [II]\n", sect->fragment, currentfile->filename); */ } else { /* new file */ xhtmlfile *file = xhtml_new_file(sect); sect->file = file; currentfile->next = file; file->parent = currentfile->parent; file->is_leaf = (level == conf.leaf_level); file->sections = sect; /* printf("connected '%s' to new file '%s' [II]\n", sect->fragment, file->filename); */ currentfile = file; currentsect = sect; } } else { /* Case (1) or (2) and we must move up the file tree first */ /* this loop is now probably irrelevant - we know we can't connect * to anything in the current file */ while (currentsect && level < currentsect->level) { currentsect = currentsect->parent; if (currentsect) { /* printf(" * up one level to '%s'\n", currentsect->fragment); */ } else { /* printf(" * up one level (off top of current file)\n"); */ } } if (currentsect) { /* I'm pretty sure this can now never fire */ assert(currentfile->is_leaf); /* printf("connected '%s' to existing file '%s' [III]\n", sect->fragment, currentfile->filename); */ sect->file = currentfile; currentsect->next = sect; currentsect = sect; } else { /* find a file we can attach to */ while (currentfile && currentfile->sections && level < currentfile->sections->level) { currentfile = currentfile->parent; if (currentfile) { /* printf(" * up one file level to '%s'\n", currentfile->filename); */ } else { /* printf(" * up one file level (off top of tree)\n"); */ } } if (currentfile) { /* new file (we had to skip up a file to get here, so we must be dealing with a level no lower than the configured leaf_level */ xhtmlfile *file = xhtml_new_file(sect); currentfile->next = file; sect->file = file; file->parent = currentfile->parent; file->is_leaf = (level == conf.leaf_level); file->sections = sect; /* printf("connected '%s' to new file '%s' [III]\n", sect->fragment, file->filename); */ currentfile = file; currentsect = sect; } else { fatal(err_whatever, "Ran off the top trying to connect sibling: strange document."); } } } } } topsection = lastsection; /* get correct end of the chain */ xhtml_fixup_layout(topfile); /* leaf files not at leaf level marked as such */ } static void xhtml_hack_xhtmlify(word*words) // "\" "" --> "\" "
" { int prevwasslash = 0; if (!is_xhtml()) return ; for (;words; words = words->next) { if (!words->text) continue; if (prevwasslash) { if (words->text[0] == '<' && !ustricmp(words->text, L"next && words->next->text && words->next->text[0] == '\\') ustrreplacedup(&words->text, L"
text[0] == '\\' && !words->text[1]; } } #define NAMEDFRAGMENT_MAXLEN 200 /* More than enough for our usage */ /* * Get formatted fragment name for html anchor. * Uses para->keyword if possible, falls back to the ?#.#.# default. */ static char* xhtml_get_fragmentname(const xhtmlsection * section, char * fragmentbuf) { if (conf.keywordfragments) { paragraph *para = section->para; if (para && para->keyword && *para->keyword) return ustrtoa(para->keyword, fragmentbuf, NAMEDFRAGMENT_MAXLEN); } return section->fragment; } static void xhtml_do_index(); static void xhtml_do_file(xhtmlfile * file); static void xhtml_do_top_file(xhtmlfile * file, paragraph * sourceform); static void xhtml_do_paras(FILE * fp, paragraph * p); static int xhtml_do_contents_limit(FILE * fp, xhtmlfile * file, int limit); static int xhtml_do_contents_section_limit(FILE * fp, xhtmlsection * section, int limit); static int xhtml_add_contents_entry(FILE * fp, xhtmlsection * section, int limit); static int xhtml_do_contents(FILE * fp, xhtmlfile * file); static int xhtml_do_naked_contents(FILE * fp, xhtmlfile * file); static void xhtml_do_sections(FILE * fp, xhtmlsection * sections); /* * Do all the files in this structure. */ static void xhtml_do_files(xhtmlfile * file) { xhtml_do_file(file); if (file->child) xhtml_do_files(file->child); if (file->next) xhtml_do_files(file->next); } /* * Free up all memory used by the file tree from 'xfile' downwards */ static void xhtml_free_file(xhtmlfile * xfile) { if (xfile == NULL) { return; } if (xfile->filename) { sfree(xfile->filename); } xhtml_free_file(xfile->child); xhtml_free_file(xfile->next); sfree(xfile); } /* * Main function. */ void xhtml_backend(paragraph * sourceform, keywordlist * in_keywords, indexdata * in_idx) { /* int i;*/ indexentry *ientry; int ti; xhtmlsection *xsect; sourceparas = sourceform; conf = xhtml_configure(sourceform); keywords = in_keywords; idx = in_idx; /* Clear up the index entries backend data pointers */ for (ti = 0; (ientry = (indexentry *) index234(idx->entries, ti)) != NULL; ti++) { ientry->backend_data = NULL; } xhtml_ponder_layout(sourceform); /* old system ... (writes to *.alt, but gets some stuff wrong and is ugly) */ /* xhtml_level_0(sourceform); for (i=1; i<=conf.leaf_level; i++) { xhtml_level(sourceform, i); }*/ /* new system ... (writes to *.html, but isn't fully trusted) */ xhtml_do_top_file(topfile, sourceform); assert(!topfile->next); /* shouldn't have a sibling at all */ if (topfile->child) { xhtml_do_files(topfile->child); xhtml_do_index(); } /* release file, section, index data structures */ xsect = topsection; while (xsect) { xhtmlsection *tmp = xsect->chain; if (xsect->fragment) { sfree(xsect->fragment); } sfree(xsect); xsect = tmp; } xhtml_free_file(topfile); for (ti = 0; (ientry = (indexentry *) index234(idx->entries, ti)) != NULL; ti++) { if (ientry->backend_data != NULL) { xhtmlindex *xi = (xhtmlindex *) ientry->backend_data; if (xi->sections != NULL) { sfree(xi->sections); } sfree(xi); } ientry->backend_data = NULL; } { int i; sfree(conf.fchapter.number_suffix); for (i = 0; i < conf.nfsect; i++) sfree(conf.fsect[i].number_suffix); sfree(conf.fsect); } free_ustr_slist(conf.meta_append); } static int xhtml_para_level(paragraph * p) { switch (p->type) { case para_Title: return 0; break; case para_UnnumberedChapter: case para_Chapter: case para_Appendix: return 1; break; /* case para_BiblioCited: return 2; break;*/ case para_Heading: case para_Subsect: return p->aux + 2; break; default: return -1; break; } } static char *xhtml_index_filename = "IndexPage.html"; /* Output the nav links for the current file. * file == NULL means we're doing the index */ static void xhtml_donavlinks(FILE * fp, xhtmlfile * file) { xhtmlfile *xhtml_next_file = NULL; fprintf(fp, "", conf.nav_attrs); } else { fprintf(fp, ">"); } if (xhtml_last_file == NULL) { fprintf(fp, "Previous | "); } else { fprintf(fp, "Previous | ", xhtml_last_file->filename); } fprintf(fp, "Contents | "); if (file != NULL) { /* otherwise we're doing nav links for the index */ if (xhtml_next_file == NULL) xhtml_next_file = file->child; if (xhtml_next_file == NULL) xhtml_next_file = file->next; if (xhtml_next_file == NULL) xhtml_next_file = file->parent->next; } if (xhtml_next_file == NULL) { if (file == NULL) { /* index, so no next file */ fprintf(fp, "Next "); } else { fprintf(fp, "Next", xhtml_index_filename); } } else { fprintf(fp, "Next", xhtml_next_file->filename); } fprintf(fp, "

\n"); } /* Write out the index file */ static void xhtml_do_index_body(FILE * fp) { indexentry *y; int ti; if (count234(idx->entries) == 0) return; /* don't write anything at all */ fprintf(fp, "
\n"); /* iterate over idx->entries using the tree functions and display everything */ for (ti = 0; (y = (indexentry *) index234(idx->entries, ti)) != NULL; ti++) { if (y->backend_data) { int i; xhtmlindex *xi; fprintf(fp, "
"); xhtml_para(fp, y->text); fprintf(fp, "
\n
"); xi = (xhtmlindex *) y->backend_data; for (i = 0; i < xi->nsection; i++) { xhtmlsection *sect = xi->sections[i]; if (sect) { char fragmentbuf[NAMEDFRAGMENT_MAXLEN]; fprintf(fp, "", conf.leaf_level ? sect->file->filename : "", xhtml_get_fragmentname(sect, fragmentbuf)); if (sect->para->kwtext) { xhtml_para(fp, sect->para->kwtext); } else if (sect->para->words) { xhtml_para(fp, sect->para->words); } fprintf(fp, ""); if (i + 1 < xi->nsection) { fprintf(fp, ", "); } } } fprintf(fp, "
\n"); } } fprintf(fp, "
\n"); } static void xhtml_do_index() { word temp_word = { NULL, NULL, word_Normal, 0, 0, L"Index", {NULL, 0, 0} }; FILE *fp = fopen(xhtml_index_filename, "w"); if (fp == NULL) fatal(err_cantopenw, xhtml_index_filename); xhtml_doheader(fp, &temp_word); xhtml_donavlinks(fp, NULL); xhtml_do_index_body(fp); xhtml_donavlinks(fp, NULL); xhtml_dofooter(fp); fclose(fp); } /* Output the given file. This includes whatever contents at beginning and end, etc. etc. */ static void xhtml_do_file(xhtmlfile * file) { FILE *fp = fopen(file->filename, "w"); if (fp == NULL) fatal(err_cantopenw, file->filename); if (file->sections->para->words) { xhtml_doheader(fp, file->sections->para->words); } else if (file->sections->para->kwtext) { xhtml_doheader(fp, file->sections->para->kwtext); } else { xhtml_doheader(fp, NULL); } xhtml_donavlinks(fp, file); if (file->is_leaf && conf.leaf_contains_contents && xhtml_do_contents(NULL, file) >= conf.leaf_smallest_contents) xhtml_do_contents(fp, file); xhtml_do_sections(fp, file->sections); if (!file->is_leaf) xhtml_do_naked_contents(fp, file); xhtml_donavlinks(fp, file); xhtml_dofooter(fp); fclose(fp); xhtml_last_file = file; } /* Output the top-level file. */ static void xhtml_do_top_file(xhtmlfile * file, paragraph * sourceform) { paragraph *p; char fname[4096]; int done = FALSE; FILE *fp = fopen(file->filename, "w"); if (fp == NULL) fatal(err_cantopenw, file->filename); if (conf.chm_toc_file || conf.chm_ind_file) g_outputtype = OT_CHM | (g_outputtype & (OT_XHTML|OT_HTML5)); ustrtoa(conf.chm_toc_file, fname, 4096); if(*fname) { chm_toc = fopen(fname, "w"); if (chm_toc == NULL) fatal(err_cantopenw, fname); } else chm_toc = NULL; ustrtoa(conf.chm_ind_file, fname, 4096); if(*fname){ chm_ind = fopen(fname, "w"); if (chm_ind == NULL) fatal(err_cantopenw, fname); } else chm_ind = NULL; /* Do the title -- only one allowed */ for (p = sourceform; p && !done; p = p->next) { if (p->type == para_Title) { xhtml_doheader(fp, p->words); if(chm_toc)chm_doheader(chm_toc, p->words); if(chm_ind)chm_doheader(chm_ind, p->words); done = TRUE; } } if (!done) xhtml_doheader(fp, NULL /* Eek! */ ); /* * Display the title. */ for (p = sourceform; p; p = p->next) { if (p->type == para_Title) { xhtml_heading(fp, p); break; } } /* Do the preamble and copyright */ for (p = sourceform; p; p = p->next) { if (p->type == para_Preamble) { fprintf(fp, "

"); xhtml_hack_xhtmlify(p->words); xhtml_para(fp, p->words); fprintf(fp, "

\n"); } } for (p = sourceform; p; p = p->next) { if (p->type == para_Copyright) { fprintf(fp, "

"); xhtml_hack_xhtmlify(p->words); xhtml_para(fp, p->words); fprintf(fp, "

\n"); } } xhtml_do_contents(fp, file); xhtml_do_sections(fp, file->sections); /* * Put the index in the top file if we're in single-file mode * (leaf-level 0). */ if (conf.leaf_level == 0 && count234(idx->entries) > 0) { fprintf(fp, "

Index

\n"); xhtml_do_index_body(fp); } xhtml_dofooter(fp); if(chm_toc)chm_dofooter(chm_toc); if(chm_ind)chm_dofooter(chm_ind); fclose(fp); if(chm_toc) { fclose(chm_toc); chm_toc = NULL; } if(chm_ind) { fclose(chm_ind); chm_ind = NULL; } } /* Convert a Unicode string to an ASCII one. '?' is * used for unmappable characters. */ static void xhtml_utostr(wchar_t * in, char **out) { int l = ustrlen(in); int i; *out = smalloc(l + 1); for (i = 0; i < l; i++) { if (in[i] >= 32 && in[i] <= 126) (*out)[i] = (char) in[i]; else (*out)[i] = '?'; } (*out)[i] = 0; } /* * Write contents for the given file, and subfiles, down to * the appropriate contents depth. Returns the number of * entries written. */ static int xhtml_do_contents(FILE * fp, xhtmlfile * file) { int level, limit, start_level, count = 0; if (!file) return 0; level = (file->sections) ? (file->sections->level) : (0); limit = conf.contents_depth[(level > 5) ? (5) : (level)]; start_level = (file->is_leaf) ? (level - 1) : (level); last_level = start_level; count += xhtml_do_contents_section_limit(fp, file->sections, limit); count += xhtml_do_contents_limit(fp, file->child, limit); if (fp != NULL) { while (last_level > start_level) { last_level--; fprintf(fp, "\n"); if(chm_toc)fprintf(chm_toc, "\n"); } } return count; } /* As above, but doesn't do anything in the current file */ static int xhtml_do_naked_contents(FILE * fp, xhtmlfile * file) { int level, limit, start_level, count = 0; if (!file) return 0; level = (file->sections) ? (file->sections->level) : (0); limit = conf.contents_depth[(level > 5) ? (5) : (level)]; start_level = (file->is_leaf) ? (level - 1) : (level); last_level = start_level; count = xhtml_do_contents_limit(fp, file->child, limit); if (fp != NULL) { while (last_level > start_level) { last_level--; fprintf(fp, "\n"); if(chm_toc)fprintf(chm_toc, "\n"); } } return count; } /* * Write contents for the given file, children, and siblings, down to * given limit contents depth. */ static int xhtml_do_contents_limit(FILE * fp, xhtmlfile * file, int limit) { int count = 0; while (file) { count += xhtml_do_contents_section_limit(fp, file->sections, limit); count += xhtml_do_contents_limit(fp, file->child, limit); file = file->next; } return count; } /* * Write contents entries for the given section tree, down to the * limit contents depth. */ static int xhtml_do_contents_section_deep_limit(FILE * fp, xhtmlsection * section, int limit) { int count = 0; while (section) { if (!xhtml_add_contents_entry(fp, section, limit)) return 0; else count++; count += xhtml_do_contents_section_deep_limit(fp, section->child, limit); section = section->next; } return count; } /* * Write contents entries for the given section tree, down to the * limit contents depth. */ static int xhtml_do_contents_section_limit(FILE * fp, xhtmlsection * section, int limit) { int count = 0; if (!section) return 0; xhtml_add_contents_entry(fp, section, limit); count = 1; count += xhtml_do_contents_section_deep_limit(fp, section->child, limit); /* section=section->child; while (section && xhtml_add_contents_entry(fp, section, limit)) { section = section->next; } */ return count; } /* * Add a section entry, unless we're exceeding the limit, in which * case return FALSE (otherwise return TRUE). */ static int xhtml_add_contents_entry(FILE * fp, xhtmlsection * section, int limit) { char fragmentbuf[NAMEDFRAGMENT_MAXLEN], *fragment, *filename; if (!section || section->level > limit) return FALSE; if (fp == NULL || section->level < 0) return TRUE; while (last_level > section->level) { last_level--; fprintf(fp, "\n"); if(chm_toc)fprintf(chm_toc, "\n"); } while (last_level < section->level) { last_level++; fprintf(fp, "