#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 */
@@ -74,8 +84,9 @@ typedef struct {
int leaf_smallest_contents;
int include_version_id;
wchar_t *author, *description;
- wchar_t *head_end, *body, *body_start, *body_end, *address_start,
- *address_end, *nav_attrs;
+ wchar_t *html_lang, *meta_charset;
+ wchar_t *head_start, *head_middle, *head_end, *body, *body_start, *body_end;
+ 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;
@@ -123,6 +134,23 @@ 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)
{
@@ -143,7 +171,9 @@ static xhtmlconfig xhtml_configure(paragraph * source)
ret.include_version_id = TRUE;
ret.author = NULL;
ret.description = NULL;
- ret.head_end = 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;
@@ -171,42 +201,48 @@ static xhtmlconfig xhtml_configure(paragraph * source)
{
if (source->type == para_Config)
{
- if (!ustricmp(source->keyword, L"xhtml-contents-depth-0"))
+ 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 (!ustricmp(source->keyword, L"xhtml-contents-depth-1"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-1"))
{
ret.contents_depth[1] = utoi(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-contents-depth-2"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-2"))
{
ret.contents_depth[2] = utoi(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-contents-depth-3"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-3"))
{
ret.contents_depth[3] = utoi(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-contents-depth-4"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-4"))
{
ret.contents_depth[4] = utoi(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-contents-depth-5"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-contents-depth-5"))
{
ret.contents_depth[5] = utoi(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-leaf-level"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-leaf-level"))
{
ret.leaf_level = utoi(uadv(source->keyword));
} else
- if (!ustricmp(source->keyword, L"xhtml-leaf-smallest-contents"))
+ if (ishtmlkeyword(source->keyword, L"xhtml-leaf-smallest-contents"))
{
ret.leaf_smallest_contents = utoi(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-versionid"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-versionid"))
{
ret.include_version_id = utob(uadv(source->keyword));
- } else
- if (!ustricmp(source->keyword, L"xhtml-leaf-contains-contents"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-leaf-contains-contents"))
{
ret.leaf_contains_contents = utob(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-suppress-address"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-suppress-address"))
{
ret.suppress_address = utob(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-author"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-author"))
{
ret.author = uadv(source->keyword);
} else if (!ustricmp(source->keyword, L"chm-toc-file"))
@@ -215,44 +251,46 @@ static xhtmlconfig xhtml_configure(paragraph * source)
} else if (!ustricmp(source->keyword, L"chm-ind-file"))
{
ret.chm_ind_file = uadv(source->keyword);
- } else if (!ustricmp(source->keyword, L"xhtml-description"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-description"))
{
ret.description = uadv(source->keyword);
- } else if (!ustricmp(source->keyword, L"xhtml-head-end"))
- {
- ret.head_end = uadv(source->keyword);
- } else if (!ustricmp(source->keyword, L"xhtml-body-start"))
+ } 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 (!ustricmp(source->keyword, L"xhtml-body-tag"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-body-tag"))
{
ret.body = uadv(source->keyword);
- } else if (!ustricmp(source->keyword, L"xhtml-body-end"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-body-end"))
{
ret.body_end = uadv(source->keyword);
- } else if (!ustricmp(source->keyword, L"xhtml-address-start"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-address-start"))
{
ret.address_start = uadv(source->keyword);
- } else if (!ustricmp(source->keyword, L"xhtml-address-end"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-address-end"))
{
ret.address_end = uadv(source->keyword);
} else
- if (!ustricmp(source->keyword, L"xhtml-navigation-attributes"))
+ if (ishtmlkeyword(source->keyword, L"xhtml-navigation-attributes"))
{
ret.nav_attrs = uadv(source->keyword);
- } else if (!ustricmp(source->keyword, L"xhtml-chapter-numeric"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-chapter-numeric"))
{
ret.fchapter.just_numbers = utob(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-chapter-suffix"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-chapter-suffix"))
{
- ret.fchapter.number_suffix = ustrdup(uadv(source->keyword));
- } else if (!ustricmp(source->keyword, L"xhtml-rlink-prefix"))
+ 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 (!ustricmp(source->keyword, L"xhtml-rlink-suffix"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-rlink-suffix"))
{
ret.rlink_suffix = uadv(source->keyword);
- } else if (!ustricmp(source->keyword, L"xhtml-section-numeric"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-section-numeric"))
{
wchar_t *p = uadv(source->keyword);
int n = 0;
@@ -270,7 +308,7 @@ static xhtmlconfig xhtml_configure(paragraph * source)
ret.nfsect = n + 1;
}
ret.fsect[n].just_numbers = utob(p);
- } else if (!ustricmp(source->keyword, L"xhtml-section-suffix"))
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-section-suffix"))
{
wchar_t *p = uadv(source->keyword);
int n = 0;
@@ -287,8 +325,8 @@ static xhtmlconfig xhtml_configure(paragraph * source)
ret.fsect[i] = ret.fsect[ret.nfsect - 1];
ret.nfsect = n + 1;
}
- ret.fsect[n].number_suffix = ustrdup(p);
- } else if (!ustricmp(source->keyword, L"xhtml-keywordfragments"))
+ ustrreplacedup(&ret.fsect[n].number_suffix, p);
+ } else if (ishtmlkeyword(source->keyword, L"xhtml-keywordfragments"))
{
ret.keywordfragments = utob(uadv(source->keyword));
}
@@ -670,6 +708,23 @@ static void xhtml_ponder_layout(paragraph * p)
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.
@@ -995,6 +1050,8 @@ static void xhtml_do_top_file(xhtmlfile * file, paragraph * sourceform)
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)
{
@@ -1046,6 +1103,7 @@ static void xhtml_do_top_file(xhtmlfile * file, paragraph * sourceform)
if (p->type == para_Preamble)
{
fprintf(fp, "");
+ xhtml_hack_xhtmlify(p->words);
xhtml_para(fp, p->words);
fprintf(fp, "
\n");
}
@@ -1055,6 +1113,7 @@ static void xhtml_do_top_file(xhtmlfile * file, paragraph * sourceform)
if (p->type == para_Copyright)
{
fprintf(fp, "");
+ xhtml_hack_xhtmlify(p->words);
xhtml_para(fp, p->words);
fprintf(fp, "
\n");
}
@@ -1345,7 +1404,7 @@ static void xhtml_do_paras(FILE * fp, paragraph * p)
break;
case para_Rule:
- fprintf(fp, "\n
\n");
+ fprintf(fp, "\n%s\n", gettagtxt_hr());
break;
case para_Normal:
@@ -1426,39 +1485,51 @@ static void xhtml_do_paras(FILE * fp, paragraph * p)
}
}
+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)
{
- fprintf(fp,
- "\n");
- fprintf(fp,
- "\n" \
- "\n" \
- "");
+ const int xhtml = is_xhtml(), html5 = is_html5();
+ const char *xhtmldoctype = "\n";
+ const char *html4doctype = "";
+ const char *xmlns = xhtml ? " xmlns=\"http://www.w3.org/1999/xhtml\"" : 0;
+ const char *voidend = xhtml ? " /" : "";
+ const wchar_t *tmpwstr;
+
+ if (xhtml && html5) fatal(err_whatever, "indeterminate format");
+ fprintf(fp, html5 ? "\n" : xhtml ? xhtmldoctype : html4doctype);
+ fprintf(fp, "\n");
+ if (ustricmp(L"none", (tmpwstr = ustrdef(conf.meta_charset, L"UTF-8"))))
+ fprintf(fp, (xhtml || !html5) ? "" : "\n", tmpwstr, voidend);
+ printoptstr(fp, "", conf.head_start, "\n");
+ fprintf(fp, "\n" "", voidend);
if (title == NULL)
fprintf(fp, "Documentation");
else
xhtml_para(fp, title);
fprintf(fp, "\n");
- fprintf(fp,
- "\n",
- version);
+ printoptstr(fp, "", conf.head_middle, "\n");
+ fprintf(fp, "\n", version, voidend);
if (conf.author)
- fprintf(fp, "\n", conf.author);
+ fprintf(fp, "\n", conf.author, voidend);
if (conf.description)
- fprintf(fp, "\n",
- conf.description);
- if (conf.head_end)
- fprintf(fp, "%ls\n", conf.head_end);
- fprintf(fp, "\n\n");
- if (conf.body)
- fprintf(fp, "%ls\n", conf.body);
- else
- fprintf(fp, "\n");
+ fprintf(fp, "\n", conf.description, voidend);
+ printoptstr(fp, "", conf.head_end, "\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "%ls\n", conf.body ? conf.body : L"");
if (conf.body_start)
fprintf(fp, "%ls\n", conf.body_start);
}
@@ -1475,7 +1546,7 @@ static void chm_doheader(FILE * fp, word * title)
*/
static void xhtml_dofooter(FILE * fp)
{
- fprintf(fp, "\n
\n\n");
+ fprintf(fp, "\n%s\n\n", gettagtxt_hr());
if (conf.body_end)
fprintf(fp, "%ls\n", conf.body_end);
if (!conf.suppress_address)
@@ -1519,7 +1590,7 @@ static void xhtml_versionid(FILE * fp, word * text, int started)
rdaddc(&t, ']'); /* FIXME: configurability */
if (started)
- fprintf(fp, "
\n");
+ fprintf(fp, "%s\n", gettagtxt_br());
fprintf(fp, "%s\n", t.text);
sfree(t.text);
}
@@ -1666,7 +1737,7 @@ static void xhtml_rdaddwc(rdstringc * rs, word * text, word * end)
if(chm_toc && *c == '.' && *(c+1) == '.')
rdaddsc(rs, c + 1);
else
- rdaddsc(rs, c);
+ rdaddsc(rs, c);
rdaddsc(rs, "\">");
sfree(c);
break;
@@ -1681,7 +1752,7 @@ static void xhtml_rdaddwc(rdstringc * rs, word * text, word * end)
rdaddsc(rs, c2);
sfree(c2);
}
- rdaddsc(rs, c);
+ rdaddsc(rs, c);
if (conf.rlink_suffix)
{
char *c2;
diff --git a/Docs/src/bin/halibut/halibut.h b/Docs/src/bin/halibut/halibut.h
index 149f4784..9872f77d 100644
--- a/Docs/src/bin/halibut/halibut.h
+++ b/Docs/src/bin/halibut/halibut.h
@@ -240,14 +240,17 @@ char *dupstr(char *s);
/*
* ustring.c
*/
+#define asciistrdef(s, d) ( (s) ? (s) : (d) )
+#define ustrdef(s, d) ( (s) ? (s) : (d) )
wchar_t *ustrdup(wchar_t * s);
+wchar_t *ustrreplacedup(wchar_t **dest, wchar_t *src);
char *ustrtoa(wchar_t * s, char *outbuf, int size);
int ustrlen(wchar_t * s);
wchar_t *uadv(wchar_t * s);
wchar_t *ustrcpy(wchar_t * dest, wchar_t * source);
wchar_t utolower(wchar_t);
-int ustrcmp(wchar_t * lhs, wchar_t * rhs);
-int ustricmp(wchar_t * lhs, wchar_t * rhs);
+int ustrcmp(const wchar_t * lhs, const wchar_t * rhs);
+int ustricmp(const wchar_t * lhs, const wchar_t * rhs);
int utoi(wchar_t *);
int utob(wchar_t *);
int uisdigit(wchar_t);
diff --git a/Docs/src/bin/halibut/ustring.c b/Docs/src/bin/halibut/ustring.c
index 9ab4ba65..0192d8b0 100644
--- a/Docs/src/bin/halibut/ustring.c
+++ b/Docs/src/bin/halibut/ustring.c
@@ -21,6 +21,12 @@ wchar_t *ustrdup(wchar_t * s)
return r;
}
+wchar_t *ustrreplacedup(wchar_t **dest, wchar_t *src)
+{
+ sfree(*dest);
+ return *dest = ustrdup(src);
+}
+
char *ustrtoa(wchar_t * s, char *outbuf, int size)
{
char *p;
@@ -62,7 +68,7 @@ wchar_t *ustrcpy(wchar_t * dest, wchar_t * source)
return ret;
}
-int ustrcmp(wchar_t * lhs, wchar_t * rhs)
+int ustrcmp(const wchar_t * lhs, const wchar_t * rhs)
{
if (!lhs && !rhs)
return 0;
@@ -89,7 +95,7 @@ wchar_t utolower(wchar_t c)
return c;
}
-int ustricmp(wchar_t * lhs, wchar_t * rhs)
+int ustricmp(const wchar_t * lhs, const wchar_t * rhs)
{
wchar_t lc, rc;
while ((lc = utolower(*lhs)) == (rc = utolower(*rhs)) && lc && rc)
diff --git a/Docs/src/bin/halibut/version.c b/Docs/src/bin/halibut/version.c
index 1b46fc8f..632bdec8 100644
--- a/Docs/src/bin/halibut/version.c
+++ b/Docs/src/bin/halibut/version.c
@@ -22,6 +22,7 @@ void initversionstring(void)
{
char scmverbuf[VERSTRSCMREVMAX+1];
int cchsvnrev = 0;
+ /* SCM trigger 20210912 */
const char*svnproprev = "$Revision$";
if ('$' == *svnproprev++)
{
diff --git a/Docs/src/config.but b/Docs/src/config.but
index 6f1e01b0..f3020639 100644
--- a/Docs/src/config.but
+++ b/Docs/src/config.but
@@ -2,6 +2,10 @@
\#{* Common config shared by all output formats *}
\#{**********************************************}
+\cfg{html-lang}{en}
+
+\cfg{html-suppress-address}{true}
+
\define{NsisCopyright} Copyright (C) 1999-2021 Contributors
\copyright \NsisCopyright
diff --git a/Source/ResourceEditor.cpp b/Source/ResourceEditor.cpp
index 47609647..ac47f52c 100644
--- a/Source/ResourceEditor.cpp
+++ b/Source/ResourceEditor.cpp
@@ -867,7 +867,7 @@ bool CResourceEditor::UpdateResourceFromExternalT(const TCHAR* Type, WORD Name,
{
bool success = false;
const TCHAR *srctype, *srcname;
- LANGID srclang;
+ LANGID srclang = 0;
TCHAR *resproto = ParseResProtocolAlloc(File, srctype, srcname, srclang);
if (resproto) {
File = resproto;