
git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7324 212acab6-be3b-0410-9dea-997c60f758d6
2074 lines
61 KiB
C
2074 lines
61 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#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() ? "<br />" : "<br>" )
|
|
#define gettagtxt_hr() ( is_xhtml() ? "<hr />" : "<hr>" )
|
|
|
|
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) // "\" "<br" "\" ">" --> "\" "<br /" "\" ">"
|
|
{
|
|
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"<br") && words->next && words->next->text && words->next->text[0] == '\\')
|
|
ustrreplacedup(&words->text, L"<br /");
|
|
}
|
|
else
|
|
prevwasslash = words->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, "<p");
|
|
if (conf.nav_attrs != NULL)
|
|
{
|
|
fprintf(fp, " %ls>", conf.nav_attrs);
|
|
} else
|
|
{
|
|
fprintf(fp, ">");
|
|
}
|
|
if (xhtml_last_file == NULL)
|
|
{
|
|
fprintf(fp, "Previous | ");
|
|
} else
|
|
{
|
|
fprintf(fp, "<a href='%s'>Previous</a> | ", xhtml_last_file->filename);
|
|
}
|
|
fprintf(fp, "<a href='Contents.html'>Contents</a> | ");
|
|
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, "<a href='%s'>Next</a>", xhtml_index_filename);
|
|
}
|
|
} else
|
|
{
|
|
fprintf(fp, "<a href='%s'>Next</a>", xhtml_next_file->filename);
|
|
}
|
|
fprintf(fp, "</p>\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, "<dl>\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, "<dt>");
|
|
xhtml_para(fp, y->text);
|
|
fprintf(fp, "</dt>\n<dd>");
|
|
|
|
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, "<a href='%s#%s'>", 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, "</a>");
|
|
if (i + 1 < xi->nsection)
|
|
{
|
|
fprintf(fp, ", ");
|
|
}
|
|
}
|
|
}
|
|
fprintf(fp, "</dd>\n");
|
|
}
|
|
}
|
|
fprintf(fp, "</dl>\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, "<p>");
|
|
xhtml_hack_xhtmlify(p->words);
|
|
xhtml_para(fp, p->words);
|
|
fprintf(fp, "</p>\n");
|
|
}
|
|
}
|
|
for (p = sourceform; p; p = p->next)
|
|
{
|
|
if (p->type == para_Copyright)
|
|
{
|
|
fprintf(fp, "<p>");
|
|
xhtml_hack_xhtmlify(p->words);
|
|
xhtml_para(fp, p->words);
|
|
fprintf(fp, "</p>\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, "<a name=\"index\"></a><h1>Index</h1>\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, "</ul>\n");
|
|
if(chm_toc)fprintf(chm_toc, "</ul>\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, "</ul>\n");
|
|
if(chm_toc)fprintf(chm_toc, "</ul>\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, "</ul>\n");
|
|
if(chm_toc)fprintf(chm_toc, "</ul>\n");
|
|
}
|
|
while (last_level < section->level)
|
|
{
|
|
last_level++;
|
|
fprintf(fp, "<ul>\n");
|
|
if(chm_toc)fprintf(chm_toc, "<ul>\n");
|
|
}
|
|
filename = conf.leaf_level ? section->file->filename : "";
|
|
fragment = "";
|
|
if (section->para->type != para_Chapter || !conf.leaf_level)
|
|
fragment = xhtml_get_fragmentname(section, fragmentbuf);
|
|
|
|
fprintf(fp, "<li>");
|
|
fprintf(fp, "<a %shref=\"%s#%s\">",
|
|
(section->para->type == para_Chapter|| section->para->type == para_Appendix) ? "class=\"btitle\" " : "",
|
|
filename, fragment);
|
|
if(chm_toc)fprintf(chm_toc, "<li><OBJECT type=\"text/sitemap\"><param name=\"Local\" value=\"%s#%s\"><param name=\"Name\" value=\"",
|
|
filename, fragment);
|
|
if(chm_ind)fprintf(chm_ind, "<li><OBJECT type=\"text/sitemap\"><param name=\"Local\" value=\"%s#%s\"><param name=\"Name\" value=\"",
|
|
filename, fragment);
|
|
//%s
|
|
if (section->para->type == para_Chapter
|
|
|| section->para->type == para_Appendix)
|
|
fprintf(fp, "<b>");
|
|
if ((section->para->type != para_Heading
|
|
&& section->para->type != para_Subsect) || (section->para->kwtext
|
|
&& !section->para->
|
|
words))
|
|
{
|
|
xhtml_para(fp, section->para->kwtext);
|
|
if(chm_toc)xhtml_para(chm_toc, section->para->kwtext);
|
|
if (section->para->words){
|
|
fprintf(fp, ": ");
|
|
if(chm_toc)fprintf(chm_toc, ": ");
|
|
}
|
|
}
|
|
if (section->para->type == para_Chapter
|
|
|| section->para->type == para_Appendix)
|
|
fprintf(fp, "</b>");
|
|
if (section->para->words)
|
|
{
|
|
xhtml_para(fp, section->para->words);
|
|
if(chm_toc)xhtml_para(chm_toc, section->para->words);
|
|
if(chm_ind)xhtml_para(chm_ind, section->para->words);
|
|
}
|
|
fprintf(fp, "</a></li>\n");
|
|
if(chm_toc)fprintf(chm_toc,"\"></OBJECT></li>\n");
|
|
if(chm_ind)fprintf(chm_ind,"\"></OBJECT></li>\n");
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Write all the sections in this file. Do all paragraphs in this section, then all
|
|
* children (recursively), then go on to the next one (tail recursively).
|
|
*/
|
|
static void xhtml_do_sections(FILE * fp, xhtmlsection * sections)
|
|
{
|
|
while (sections)
|
|
{
|
|
currentsection = sections;
|
|
xhtml_do_paras(fp, sections->para);
|
|
xhtml_do_sections(fp, sections->child);
|
|
sections = sections->next;
|
|
}
|
|
}
|
|
|
|
/* Write this list of paragraphs. Close off all lists at the end. */
|
|
static void xhtml_do_paras(FILE * fp, paragraph * p)
|
|
{
|
|
int last_type = -1, first = TRUE;
|
|
if (!p)
|
|
return;
|
|
|
|
/* for (; p && (xhtml_para_level(p)>limit || xhtml_para_level(p)==-1 || first); p=p->next) {*/
|
|
for (; p && (xhtml_para_level(p) == -1 || first); p = p->next)
|
|
{
|
|
first = FALSE;
|
|
switch (p->type)
|
|
{
|
|
/*
|
|
* Things we ignore because we've already processed them or
|
|
* aren't going to touch them in this pass.
|
|
*/
|
|
case para_IM:
|
|
case para_BR:
|
|
case para_Biblio: /* only touch BiblioCited */
|
|
case para_VersionID:
|
|
case para_Copyright:
|
|
case para_Preamble:
|
|
case para_NoCite:
|
|
case para_Title:
|
|
break;
|
|
|
|
/*
|
|
* Chapter titles.
|
|
*/
|
|
case para_Chapter:
|
|
case para_Appendix:
|
|
case para_UnnumberedChapter:
|
|
xhtml_heading(fp, p);
|
|
break;
|
|
|
|
case para_Heading:
|
|
case para_Subsect:
|
|
xhtml_heading(fp, p);
|
|
break;
|
|
|
|
case para_Rule:
|
|
fprintf(fp, "\n%s\n", gettagtxt_hr());
|
|
break;
|
|
|
|
case para_Normal:
|
|
fprintf(fp, "\n<p>");
|
|
xhtml_para(fp, p->words);
|
|
fprintf(fp, "</p>\n");
|
|
break;
|
|
|
|
case para_Bullet:
|
|
case para_NumberedList:
|
|
case para_BiblioCited:
|
|
if (last_type != p->type)
|
|
{
|
|
/* start up list if necessary */
|
|
if (p->type == para_Bullet)
|
|
{
|
|
fprintf(fp, "<ul>\n");
|
|
} else if (p->type == para_NumberedList)
|
|
{
|
|
fprintf(fp, "<ol>\n");
|
|
} else if (p->type == para_BiblioCited)
|
|
{
|
|
fprintf(fp, "<dl>\n");
|
|
}
|
|
}
|
|
if (p->type == para_Bullet || p->type == para_NumberedList)
|
|
fprintf(fp, "<li>");
|
|
else if (p->type == para_BiblioCited)
|
|
{
|
|
fprintf(fp, "<dt>");
|
|
xhtml_para(fp, p->kwtext);
|
|
fprintf(fp, "</dt>\n<dd>");
|
|
}
|
|
xhtml_para(fp, p->words);
|
|
if (p->type == para_BiblioCited)
|
|
{
|
|
fprintf(fp, "</dd>\n");
|
|
} else if (p->type == para_Bullet || p->type == para_NumberedList)
|
|
{
|
|
fprintf(fp, "</li>");
|
|
}
|
|
if (p->type == para_Bullet || p->type == para_NumberedList
|
|
|| p->type == para_BiblioCited)
|
|
/* close off list if necessary */
|
|
{
|
|
paragraph *p2 = p->next;
|
|
int close_off = FALSE;
|
|
/* if (p2 && (xhtml_para_level(p2)>limit || xhtml_para_level(p2)==-1)) {*/
|
|
if (p2 && xhtml_para_level(p2) == -1)
|
|
{
|
|
if (p2->type != p->type)
|
|
close_off = TRUE;
|
|
} else
|
|
{
|
|
close_off = TRUE;
|
|
}
|
|
if (close_off)
|
|
{
|
|
if (p->type == para_Bullet)
|
|
{
|
|
fprintf(fp, "</ul>\n");
|
|
} else if (p->type == para_NumberedList)
|
|
{
|
|
fprintf(fp, "</ol>\n");
|
|
} else if (p->type == para_BiblioCited)
|
|
{
|
|
fprintf(fp, "</dl>\n");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case para_Code:
|
|
xhtml_codepara(fp, p->words);
|
|
break;
|
|
}
|
|
last_type = p->type;
|
|
}
|
|
}
|
|
|
|
static void printoptstr(FILE * fp, const char*prefix, const wchar_t*str, const char*suffix)
|
|
{
|
|
if (str)
|
|
{
|
|
fprintf(fp, "%s%ls%s", prefix ? prefix : "", str, suffix ? suffix : "");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output a header for this XHTML file.
|
|
*/
|
|
static void xhtml_doheader(FILE * fp, word * title)
|
|
{
|
|
const int xhtml = is_xhtml(), html5 = is_html5();
|
|
const char *xhtmldoctype = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
|
|
const char *html4doctype = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n";
|
|
const char *xhtmlxmlns = xhtml ? " xmlns=\"http://www.w3.org/1999/xhtml\"" : 0;
|
|
const char *voidend = xhtml ? " /" : "";
|
|
const wchar_t *tmpwstr;
|
|
const ustr_slist *pussl;
|
|
|
|
if (xhtml && html5) fatal(err_whatever, "indeterminate format");
|
|
fprintf(fp, html5 ? "<!DOCTYPE html>\n" : xhtml ? xhtmldoctype : html4doctype);
|
|
fprintf(fp, "<html%s", xhtml ? xhtmlxmlns : "");
|
|
//www.w3.org/International/questions/qa-html-language-declarations
|
|
if (*(tmpwstr = ustrdef(conf.html_lang, L"")))
|
|
fprintf(fp, "%s%ls%s lang=\"%ls\"", xhtml ? " xml:lang=\"" : "", xhtml ? tmpwstr : L"", xhtml ? "\"" : "", tmpwstr);
|
|
fprintf(fp, "><head>\n");
|
|
if (ustricmp(L"none", (tmpwstr = ustrdef(conf.meta_charset, L"UTF-8"))))
|
|
fprintf(fp, (xhtml || !html5) ? "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%ls\"%s>" : "<meta charset=\"%ls\">\n", tmpwstr, voidend);
|
|
printoptstr(fp, "", conf.head_start, "\n");
|
|
fprintf(fp, "<title>");
|
|
if (title == NULL)
|
|
fprintf(fp, "Documentation");
|
|
else
|
|
xhtml_para(fp, title);
|
|
fprintf(fp, "</title>\n");
|
|
for (pussl = conf.meta_append; pussl; pussl = pussl->next)
|
|
fprintf(fp, "<meta %ls%s>", pussl->string, voidend);
|
|
printoptstr(fp, "", conf.head_middle, "\n");
|
|
fprintf(fp, "<meta name=\"generator\" content=\"Halibut %s xhtml-backend\"%s>\n", version, voidend);
|
|
if (conf.author)
|
|
fprintf(fp, "<meta name=\"author\" content=\"%ls\"%s>\n", conf.author, voidend);
|
|
if (conf.description)
|
|
fprintf(fp, "<meta name=\"description\" content=\"%ls\"%s>\n", conf.description, voidend);
|
|
printoptstr(fp, "", conf.head_end, "\n");
|
|
fprintf(fp, "</head>\n");
|
|
fprintf(fp, "%ls\n", conf.body ? conf.body : L"<body>");
|
|
if (conf.body_start)
|
|
fprintf(fp, "%ls\n", conf.body_start);
|
|
}
|
|
|
|
static void chm_doheader(FILE * fp, word * title)
|
|
{
|
|
fprintf(fp, "<HTML><BODY><UL><LI><OBJECT type=\"text/sitemap\"><param name=\"Name\" value=\"");
|
|
xhtml_para(fp, title);
|
|
fprintf(fp,"\"><param name=\"Local\" value=\"Contents.html\"></OBJECT></li>\n");
|
|
}
|
|
|
|
/*
|
|
* Output a footer for this XHTML file.
|
|
*/
|
|
static void xhtml_dofooter(FILE * fp)
|
|
{
|
|
int hr = conf.body_end || !conf.suppress_address;
|
|
|
|
if (hr)
|
|
fprintf(fp, "\n%s\n\n", gettagtxt_hr());
|
|
|
|
if (conf.body_end)
|
|
fprintf(fp, "%ls\n", conf.body_end);
|
|
if (!conf.suppress_address)
|
|
{
|
|
fprintf(fp, "<address>\n");
|
|
if (conf.address_start)
|
|
fprintf(fp, "%ls\n", conf.address_start);
|
|
/* Do the version ID */
|
|
if (conf.include_version_id)
|
|
{
|
|
paragraph *p;
|
|
int started = 0;
|
|
for (p = sourceparas; p; p = p->next)
|
|
if (p->type == para_VersionID)
|
|
{
|
|
xhtml_versionid(fp, p->words, started);
|
|
started = 1;
|
|
}
|
|
}
|
|
if (conf.address_end)
|
|
fprintf(fp, "%ls\n", conf.address_end);
|
|
fprintf(fp, "</address>\n");
|
|
}
|
|
fprintf(fp, "</body></html>\n");
|
|
}
|
|
static void chm_dofooter(FILE * fp)
|
|
{
|
|
fprintf(fp, "</ul></BODY></HTML>\n");
|
|
}
|
|
|
|
/*
|
|
* Output the versionid paragraph. Typically this is a version control
|
|
* ID string (such as $Id...$ in RCS).
|
|
*/
|
|
static void xhtml_versionid(FILE * fp, word * text, int started)
|
|
{
|
|
rdstringc t = { 0, 0, NULL };
|
|
|
|
rdaddc(&t, '['); /* FIXME: configurability */
|
|
xhtml_rdaddwc(&t, text, NULL);
|
|
rdaddc(&t, ']'); /* FIXME: configurability */
|
|
|
|
if (started)
|
|
fprintf(fp, "%s\n", gettagtxt_br());
|
|
fprintf(fp, "%s\n", t.text);
|
|
sfree(t.text);
|
|
}
|
|
|
|
/* Is this an XHTML reserved character? */
|
|
static int xhtml_reservedchar(int c)
|
|
{
|
|
if (c == '&' || c == '<' || c == '>' || c == '"')
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Convert a wide string into valid XHTML: Anything outside ASCII will
|
|
* be fixed up as an entity. Currently we don't worry about constraining the
|
|
* encoded character set, which we should probably do at some point (we can
|
|
* still fix up and return FALSE - see the last comment here). We also don't
|
|
* currently
|
|
*
|
|
* Because this is only used for words, spaces are HARD spaces (any other
|
|
* spaces will be word_Whitespace not word_Normal). So they become
|
|
* Unless hard_spaces is FALSE, of course (code paragraphs break the above
|
|
* rule).
|
|
*
|
|
* If `result' is non-NULL, mallocs the resulting string and stores a pointer to
|
|
* it in `*result'. If `result' is NULL, merely checks whether all
|
|
* characters in the string are feasible.
|
|
*
|
|
* Return is nonzero if all characters are OK. If not all
|
|
* characters are OK but `result' is non-NULL, a result _will_
|
|
* still be generated!
|
|
*/
|
|
static int xhtml_convert(wchar_t * s, char **result, int hard_spaces)
|
|
{
|
|
int doing = (result != 0);
|
|
int ok = TRUE;
|
|
char *p = NULL;
|
|
int plen = 0, psize = 0;
|
|
|
|
for (; *s; s++)
|
|
{
|
|
wchar_t c = *s;
|
|
|
|
#define ensure_size(i) if (i>=psize) { psize = i+256; p = resize(p, psize); }
|
|
|
|
if (((c == 32 && !hard_spaces)
|
|
|| (c > 32 && c <= 126 && !xhtml_reservedchar(c))))
|
|
{
|
|
/* Char is OK. */
|
|
if (doing)
|
|
{
|
|
ensure_size(plen);
|
|
p[plen++] = (char) c;
|
|
}
|
|
} else
|
|
{
|
|
/* Char needs fixing up. */
|
|
/* ok = FALSE; -- currently we never return FALSE; we
|
|
* might want to when considering a character set for the
|
|
* encoded document.
|
|
*/
|
|
if (doing)
|
|
{
|
|
if (c == 32)
|
|
{ /* a space in a word is a hard space */
|
|
ensure_size(plen + 7); /* includes space for the NUL, which is subsequently stomped on */
|
|
sprintf(p + plen, " ");
|
|
plen += 6;
|
|
} else
|
|
{
|
|
switch (c)
|
|
{
|
|
case '&':
|
|
ensure_size(plen + 6); /* includes space for the NUL, which is subsequently stomped on */
|
|
plen += sprintf(p + plen, "&");
|
|
break;
|
|
case '"':
|
|
ensure_size(plen + 7); /* includes space for the NUL, which is subsequently stomped on */
|
|
plen += sprintf(p + plen, """);
|
|
break;
|
|
case '<':
|
|
if (plen > 1 && *(s - 1) == '\\' && *(s - 2) == '\\')
|
|
{
|
|
ensure_size(--plen);
|
|
p[plen - 1] = (char) c;
|
|
p[plen] = 0;
|
|
} else
|
|
{
|
|
ensure_size(plen + 5); /* includes space for the NUL, which is subsequently stomped on */
|
|
plen += sprintf(p + plen, "<");
|
|
}
|
|
break;
|
|
case '>':
|
|
if (plen > 1 && *(s - 1) == '\\' && *(s - 2) == '\\')
|
|
{
|
|
ensure_size(--plen);
|
|
p[plen - 1] = (char) c;
|
|
p[plen] = 0;
|
|
} else
|
|
{
|
|
ensure_size(plen + 5); /* includes space for the NUL, which is subsequently stomped on */
|
|
plen += sprintf(p + plen, ">");
|
|
}
|
|
break;
|
|
default:
|
|
ensure_size(plen + 8); /* includes space for the NUL, which is subsequently stomped on */
|
|
plen += sprintf(p + plen, "&#%04i;", (int) c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (doing)
|
|
{
|
|
p = resize(p, plen + 1);
|
|
p[plen] = '\0';
|
|
*result = p;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
/*
|
|
* This formats the given words as XHTML.
|
|
*/
|
|
static void xhtml_rdaddwc(rdstringc * rs, word * text, word * end)
|
|
{
|
|
char *c;
|
|
keyword *kwl;
|
|
xhtmlsection *sect;
|
|
indextag *itag;
|
|
int ti;
|
|
wchar_t *s;
|
|
|
|
for (; text && text != end; text = text->next)
|
|
{
|
|
switch (text->type)
|
|
{
|
|
case word_HyperLink:
|
|
xhtml_utostr(text->text, &c);
|
|
rdaddsc(rs, "<a href=\"");
|
|
if(chm_toc && *c == '.' && *(c+1) == '.')
|
|
rdaddsc(rs, c + 1);
|
|
else
|
|
rdaddsc(rs, c);
|
|
rdaddsc(rs, "\">");
|
|
sfree(c);
|
|
break;
|
|
|
|
case word_LocalHyperLink:
|
|
xhtml_utostr(text->text, &c);
|
|
rdaddsc(rs, "<a href=\"");
|
|
if (conf.rlink_prefix)
|
|
{
|
|
char *c2;
|
|
xhtml_utostr(conf.rlink_prefix, &c2);
|
|
rdaddsc(rs, c2);
|
|
sfree(c2);
|
|
}
|
|
rdaddsc(rs, c);
|
|
if (conf.rlink_suffix)
|
|
{
|
|
char *c2;
|
|
xhtml_utostr(conf.rlink_suffix, &c2);
|
|
rdaddsc(rs, c2);
|
|
sfree(c2);
|
|
}
|
|
rdaddsc(rs, "\">");
|
|
sfree(c);
|
|
break;
|
|
|
|
case word_UpperXref:
|
|
case word_LowerXref:
|
|
case word_FreeTextXref:
|
|
kwl = kw_lookup(keywords, text->text);
|
|
if (kwl)
|
|
{
|
|
sect = xhtml_find_section(kwl->para);
|
|
if (sect)
|
|
{
|
|
char fragmentbuf[NAMEDFRAGMENT_MAXLEN];
|
|
rdaddsc(rs, "<a href=\"");
|
|
rdaddsc(rs, conf.leaf_level ? sect->file->filename : "");
|
|
rdaddc(rs, '#');
|
|
rdaddsc(rs, xhtml_get_fragmentname(sect, fragmentbuf));
|
|
rdaddsc(rs, "\">");
|
|
} else
|
|
{
|
|
rdaddsc(rs,
|
|
"<a href=\"Apologies.html\"><!-- probably a bibliography cross reference -->");
|
|
error(err_whatever,
|
|
"Couldn't locate cross-reference! (Probably a bibliography entry.)");
|
|
}
|
|
} else
|
|
{
|
|
rdaddsc(rs,
|
|
"<a href=\"Apologies.html\"><!-- unknown cross-reference -->");
|
|
error(err_whatever,
|
|
"Couldn't locate cross-reference! (Wasn't in source file.)");
|
|
}
|
|
break;
|
|
|
|
case word_IndexRef: /* in theory we could make an index target here */
|
|
/* rdaddsc(rs, "<a name=\"idx-");
|
|
xhtml_utostr(text->text, &c);
|
|
rdaddsc(rs, c);
|
|
sfree(c);
|
|
rdaddsc(rs, "\"></a>");*/
|
|
/* what we _do_ need to do is to fix up the backend data
|
|
* for any indexentry this points to.
|
|
*/
|
|
for (ti = 0;
|
|
(itag = (indextag *) index234(idx->tags, ti)) != NULL; ti++)
|
|
{
|
|
/* FIXME: really ustricmp() and not ustrcmp()? */
|
|
if (ustricmp(itag->name, text->text) == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (itag != NULL)
|
|
{
|
|
if (itag->refs != NULL)
|
|
{
|
|
int i;
|
|
for (i = 0; i < itag->nrefs; i++)
|
|
{
|
|
xhtmlindex *idx_ref;
|
|
indexentry *ientry;
|
|
|
|
ientry = itag->refs[i];
|
|
if (ientry->backend_data == NULL)
|
|
{
|
|
idx_ref = (xhtmlindex *) smalloc(sizeof(xhtmlindex));
|
|
if (idx_ref == NULL)
|
|
fatal(err_nomemory);
|
|
idx_ref->nsection = 0;
|
|
idx_ref->size = 4;
|
|
idx_ref->sections =
|
|
(xhtmlsection **) smalloc(idx_ref->size *
|
|
sizeof(xhtmlsection *));
|
|
if (idx_ref->sections == NULL)
|
|
fatal(err_nomemory);
|
|
ientry->backend_data = idx_ref;
|
|
} else
|
|
{
|
|
idx_ref = ientry->backend_data;
|
|
if (idx_ref->nsection + 1 > idx_ref->size)
|
|
{
|
|
int new_size = idx_ref->size * 2;
|
|
idx_ref->sections =
|
|
srealloc(idx_ref->sections,
|
|
new_size * sizeof(xhtmlsection));
|
|
if (idx_ref->sections == NULL)
|
|
{
|
|
fatal(err_nomemory);
|
|
}
|
|
idx_ref->size = new_size;
|
|
}
|
|
}
|
|
idx_ref->sections[idx_ref->nsection++] = currentsection;
|
|
#if 0
|
|
#endif
|
|
}
|
|
} else
|
|
{
|
|
fatal(err_whatever, "Index tag had no entries!");
|
|
}
|
|
} else
|
|
{
|
|
fprintf(stderr, "Looking for index entry '%ls'\n", text->text);
|
|
fatal(err_whatever,
|
|
"Couldn't locate index entry! (Wasn't in index.)");
|
|
}
|
|
break;
|
|
|
|
case word_HyperEnd:
|
|
case word_XrefEnd:
|
|
rdaddsc(rs, "</a>");
|
|
break;
|
|
|
|
case word_Normal:
|
|
case word_Emph:
|
|
case word_Code:
|
|
case word_WeakCode:
|
|
case word_WhiteSpace:
|
|
case word_EmphSpace:
|
|
case word_CodeSpace:
|
|
case word_WkCodeSpace:
|
|
case word_Quote:
|
|
case word_EmphQuote:
|
|
case word_CodeQuote:
|
|
case word_WkCodeQuote:
|
|
assert(text->type != word_CodeQuote &&
|
|
text->type != word_WkCodeQuote);
|
|
if (towordstyle(text->type) == word_Emph &&
|
|
(attraux(text->aux) == attr_First ||
|
|
attraux(text->aux) == attr_Only))
|
|
rdaddsc(rs, "<em>");
|
|
else if ((towordstyle(text->type) == word_Code
|
|
|| towordstyle(text->type) == word_WeakCode)
|
|
&& (attraux(text->aux) == attr_First
|
|
|| attraux(text->aux) == attr_Only))
|
|
rdaddsc(rs, "<code>");
|
|
|
|
if (removeattr(text->type) == word_Normal)
|
|
{
|
|
static int dont_convert = 0;
|
|
if (dont_convert)
|
|
{
|
|
char buf[2] = " ";
|
|
dont_convert = 0;
|
|
s = text->text;
|
|
for (; *s; s++)
|
|
{
|
|
buf[0] = (char) *s;
|
|
rdaddsc(rs, buf);
|
|
}
|
|
buf[0] = 0;
|
|
rdaddsc(rs, buf);
|
|
} else
|
|
{
|
|
if (*text->text == '\\' && text->next
|
|
&& text->next->text && (*text->next->text == '&'
|
|
|| *text->next->text == '<'
|
|
|| *text->next->text == '>'
|
|
|| *text->next->text == '"'))
|
|
dont_convert = 1;
|
|
else
|
|
{
|
|
if (xhtml_convert(text->text, &c, TRUE)) /* spaces in the word are hard */
|
|
rdaddsc(rs, c);
|
|
else
|
|
xhtml_rdaddwc(rs, text->alt, NULL);
|
|
sfree(c);
|
|
}
|
|
}
|
|
} else if (removeattr(text->type) == word_WhiteSpace)
|
|
{
|
|
rdaddc(rs, ' ');
|
|
} else if (removeattr(text->type) == word_Quote)
|
|
{
|
|
rdaddsc(rs, """);
|
|
}
|
|
|
|
if (towordstyle(text->type) == word_Emph &&
|
|
(attraux(text->aux) == attr_Last ||
|
|
attraux(text->aux) == attr_Only))
|
|
rdaddsc(rs, "</em>");
|
|
else if ((towordstyle(text->type) == word_Code
|
|
|| towordstyle(text->type) == word_WeakCode)
|
|
&& (attraux(text->aux) == attr_Last
|
|
|| attraux(text->aux) == attr_Only))
|
|
rdaddsc(rs, "</code>");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Output a heading, formatted as XHTML.
|
|
*/
|
|
static void xhtml_heading(FILE * fp, paragraph * p)
|
|
{
|
|
rdstringc t = { 0, 0, NULL };
|
|
word *tprefix = p->kwtext;
|
|
word *nprefix = p->kwtext2;
|
|
word *text = p->words;
|
|
int level = xhtml_para_level(p);
|
|
xhtmlsection *sect = xhtml_find_section(p);
|
|
xhtmlheadfmt *fmt;
|
|
char fragmentbuf[NAMEDFRAGMENT_MAXLEN], *fragment;
|
|
if (sect)
|
|
{
|
|
fragment = xhtml_get_fragmentname(sect, fragmentbuf);
|
|
} else
|
|
{
|
|
if (p->type == para_Title)
|
|
fragment = "title";
|
|
else
|
|
{
|
|
fragment = ""; /* FIXME: what else can we do? */
|
|
error(err_whatever, "Couldn't locate heading cross-reference!");
|
|
}
|
|
}
|
|
|
|
if (p->type == para_Title)
|
|
fmt = NULL;
|
|
else if (level == 1)
|
|
fmt = &conf.fchapter;
|
|
else if (level - 1 < conf.nfsect)
|
|
fmt = &conf.fsect[level - 1];
|
|
else
|
|
fmt = &conf.fsect[conf.nfsect - 1];
|
|
|
|
if (fmt && fmt->just_numbers && nprefix)
|
|
{
|
|
xhtml_rdaddwc(&t, nprefix, NULL);
|
|
if (fmt)
|
|
{
|
|
char *c;
|
|
if (xhtml_convert(fmt->number_suffix, &c, FALSE))
|
|
{
|
|
rdaddsc(&t, c);
|
|
sfree(c);
|
|
}
|
|
}
|
|
} else if (fmt && !fmt->just_numbers && tprefix)
|
|
{
|
|
xhtml_rdaddwc(&t, tprefix, NULL);
|
|
if (fmt)
|
|
{
|
|
char *c;
|
|
if (xhtml_convert(fmt->number_suffix, &c, FALSE))
|
|
{
|
|
rdaddsc(&t, c);
|
|
sfree(c);
|
|
}
|
|
}
|
|
}
|
|
xhtml_rdaddwc(&t, text, NULL);
|
|
/*
|
|
* If we're outputting in single-file mode, we need to lower
|
|
* the level of each heading by one, because the overall
|
|
* document title will be sitting right at the top as an <h1>
|
|
* and so chapters and sections should start at <h2>.
|
|
*
|
|
* Even if not, the document title will come back from
|
|
* xhtml_para_level() as level zero, so we must increment that
|
|
* no matter what leaf_level is set to.
|
|
*/
|
|
if (conf.leaf_level == 0 || level == 0)
|
|
level++;
|
|
fprintf(fp, "<a name=\"%s\"></a><h%i>%s</h%i>\n", fragment, level,
|
|
t.text, level);
|
|
sfree(t.text);
|
|
}
|
|
|
|
/* Output a paragraph. Styles are handled by xhtml_rdaddwc().
|
|
* This looks pretty simple; I may have missed something ...
|
|
*/
|
|
static void xhtml_para(FILE * fp, word * text)
|
|
{
|
|
rdstringc out = { 0, 0, NULL };
|
|
xhtml_rdaddwc(&out, text, NULL);
|
|
fprintf(fp, "%s", out.text);
|
|
sfree(out.text);
|
|
}
|
|
|
|
/* Output a code paragraph. I'm treating this as preformatted, which
|
|
* may not be entirely correct. See xhtml_para() for my worries about
|
|
* this being overly-simple; however I think that most of the complexity
|
|
* of the text backend came entirely out of word wrapping anyway.
|
|
*/
|
|
static void xhtml_codepara(FILE * fp, word * text)
|
|
{
|
|
fprintf(fp, "<pre>");
|
|
for (; text; text = text->next)
|
|
if (text->type == word_WeakCode)
|
|
{
|
|
char *c;
|
|
xhtml_convert(text->text, &c, FALSE);
|
|
fprintf(fp, "%s\n", c);
|
|
sfree(c);
|
|
}
|
|
fprintf(fp, "</pre>\n");
|
|
}
|