Halibut will output fragment names in all specified formats. (I forget now
precisely why I thought this was necessary, but it seems potentially useful.)
Also ensure that legal fragment names are generated even if none of the
characters from the original turn out to be legal (e.g., %k with an entirely
numeric keyword), and correct an untruth I inserted in the documentation of
this.
(This commit hits more than just the HTML backend as I've generalised an error
message, and fixed a fault in the info backend's error handling while there.)
git-svn-id: svn://svn.tartarus.org/sgt/halibut@5457
cda61777-01e9-0310-a592-
d414129be87e
char *index_filename;
char *template_filename;
char *single_filename;
char *index_filename;
char *template_filename;
char *single_filename;
- char *template_fragment;
+ char **template_fragments;
+ int ntfragments;
char *head_end, *body_start, *body_end, *addr_start, *addr_end;
char *body_tag, *nav_attr;
wchar_t *author, *description;
char *head_end, *body_start, *body_end, *addr_start, *addr_end;
char *body_tag, *nav_attr;
wchar_t *author, *description;
paragraph *title, *text;
enum { NORMAL, TOP, INDEX } type;
int contents_depth;
paragraph *title, *text;
enum { NORMAL, TOP, INDEX } type;
int contents_depth;
htmlsect *sect, int depth);
static htmlfile *html_new_file(htmlfilelist *list, char *filename);
htmlsect *sect, int depth);
static htmlfile *html_new_file(htmlfilelist *list, char *filename);
-static htmlsect *html_new_sect(htmlsectlist *list, paragraph *title);
+static htmlsect *html_new_sect(htmlsectlist *list, paragraph *title,
+ htmlconfig *cfg);
/* Flags for html_words() flags parameter */
#define NOTHING 0x00
/* Flags for html_words() flags parameter */
#define NOTHING 0x00
ret.contents_filename = dupstr("Contents.html");
ret.index_filename = dupstr("IndexPage.html");
ret.template_filename = dupstr("%n.html");
ret.contents_filename = dupstr("Contents.html");
ret.index_filename = dupstr("IndexPage.html");
ret.template_filename = dupstr("%n.html");
- ret.template_fragment = dupstr("%b");
+ ret.ntfragments = 1;
+ ret.template_fragments = snewn(ret.ntfragments, char *);
+ ret.template_fragments[0] = dupstr("%b");
ret.head_end = ret.body_tag = ret.body_start = ret.body_end =
ret.addr_start = ret.addr_end = ret.nav_attr = NULL;
ret.author = ret.description = NULL;
ret.head_end = ret.body_tag = ret.body_start = ret.body_end =
ret.addr_start = ret.addr_end = ret.nav_attr = NULL;
ret.author = ret.description = NULL;
sfree(ret.template_filename);
ret.template_filename = dupstr(adv(p->origkeyword));
} else if (!ustricmp(k, L"html-template-fragment")) {
sfree(ret.template_filename);
ret.template_filename = dupstr(adv(p->origkeyword));
} else if (!ustricmp(k, L"html-template-fragment")) {
- sfree(ret.template_fragment);
- ret.template_fragment = dupstr(adv(p->origkeyword));
+ char *frag = adv(p->origkeyword);
+ if (*frag) {
+ while (ret.ntfragments--)
+ sfree(ret.template_fragments[ret.ntfragments]);
+ sfree(ret.template_fragments);
+ ret.template_fragments = NULL;
+ ret.ntfragments = 0;
+ while (*frag) {
+ ret.ntfragments++;
+ ret.template_fragments =
+ sresize(ret.template_fragments,
+ ret.ntfragments, char *);
+ ret.template_fragments[ret.ntfragments-1] =
+ dupstr(frag);
+ frag = adv(frag);
+ }
+ } else
+ error(err_cfginsufarg, &p->fpos, p->origkeyword, 1);
} else if (!ustricmp(k, L"html-chapter-numeric")) {
ret.achapter.just_numbers = utob(uadv(k));
} else if (!ustricmp(k, L"html-chapter-suffix")) {
} else if (!ustricmp(k, L"html-chapter-numeric")) {
ret.achapter.just_numbers = utob(uadv(k));
} else if (!ustricmp(k, L"html-chapter-suffix")) {
* source form but needs to be consistently mentioned in
* contents links.
*
* source form but needs to be consistently mentioned in
* contents links.
*
- * While we're here, we'll also invent the HTML fragment name
+ * While we're here, we'll also invent the HTML fragment name(s)
* for each section.
*/
{
htmlsect *topsect, *sect;
int d;
* for each section.
*/
{
htmlsect *topsect, *sect;
int d;
- topsect = html_new_sect(§s, NULL);
+ topsect = html_new_sect(§s, NULL, &conf);
topsect->type = TOP;
topsect->title = NULL;
topsect->text = sourceform;
topsect->contents_depth = contents_depth(conf, 0);
html_file_section(&conf, &files, topsect, -1);
topsect->type = TOP;
topsect->title = NULL;
topsect->text = sourceform;
topsect->contents_depth = contents_depth(conf, 0);
html_file_section(&conf, &files, topsect, -1);
- topsect->fragment = NULL;
for (p = sourceform; p; p = p->next)
if (is_heading_type(p->type)) {
for (p = sourceform; p; p = p->next)
if (is_heading_type(p->type)) {
- sect = html_new_sect(§s, p);
+ sect = html_new_sect(§s, p, &conf);
sect->text = p->next;
sect->contents_depth = contents_depth(conf, d+1) - (d+1);
sect->text = p->next;
sect->contents_depth = contents_depth(conf, d+1) - (d+1);
html_file_section(&conf, &files, sect, d);
html_file_section(&conf, &files, sect, d);
- sect->fragment = html_format(p, conf.template_fragment);
- sect->fragment = html_sanitise_fragment(&files, sect->file,
- sect->fragment);
+ {
+ int i;
+ for (i=0; i < conf.ntfragments; i++) {
+ sect->fragments[i] =
+ html_format(p, conf.template_fragments[i]);
+ sect->fragments[i] =
+ html_sanitise_fragment(&files, sect->file,
+ sect->fragments[i]);
+ }
+ }
}
/* And the index, if we have one. */
has_index = (count234(idx->entries) > 0);
if (has_index) {
}
/* And the index, if we have one. */
has_index = (count234(idx->entries) > 0);
if (has_index) {
- sect = html_new_sect(§s, NULL);
+ sect = html_new_sect(§s, NULL, &conf);
sect->text = NULL;
sect->type = INDEX;
sect->parent = topsect;
sect->contents_depth = 0;
html_file_section(&conf, &files, sect, 0); /* peer of chapters */
sect->text = NULL;
sect->type = INDEX;
sect->parent = topsect;
sect->contents_depth = 0;
html_file_section(&conf, &files, sect, 0); /* peer of chapters */
- sect->fragment = utoa_dup(conf.index_text, CS_ASCII);
- sect->fragment = html_sanitise_fragment(&files, sect->file,
- sect->fragment);
+ sect->fragments[0] = utoa_dup(conf.index_text, CS_ASCII);
+ sect->fragments[0] = html_sanitise_fragment(&files, sect->file,
+ sect->fragments[0]);
files.index = sect->file;
}
}
files.index = sect->file;
}
}
* won't attempt to add it to the contents or
* anything weird like that).
*/
* won't attempt to add it to the contents or
* anything weird like that).
*/
- sect = html_new_sect(&nonsects, p);
+ sect = html_new_sect(&nonsects, p, &conf);
sect->file = parent->file;
sect->parent = parent;
p->private_data = sect;
sect->file = parent->file;
sect->parent = parent;
p->private_data = sect;
* Fragment IDs for these paragraphs will simply be
* `p' followed by an integer.
*/
* Fragment IDs for these paragraphs will simply be
* `p' followed by an integer.
*/
- sect->fragment = snewn(40, char);
- sprintf(sect->fragment, "p%d",
+ sect->fragments[0] = snewn(40, char);
+ sprintf(sect->fragments[0], "p%d",
sect->file->last_fragment_number++);
sect->file->last_fragment_number++);
- sect->fragment = html_sanitise_fragment(&files, sect->file,
- sect->fragment);
+ sect->fragments[0] = html_sanitise_fragment(&files, sect->file,
+ sect->fragments[0]);
element_open(&ho, htag);
/*
element_open(&ho, htag);
/*
- * Provide anchor for cross-links to target.
+ * Provide anchor(s) for cross-links to target.
*
* (Also we'll have to do this separately in
* other paragraph types - NumberedList and
* BiblioCited.)
*/
*
* (Also we'll have to do this separately in
* other paragraph types - NumberedList and
* BiblioCited.)
*/
- if (s->fragment)
- html_fragment(&ho, s->fragment);
+ {
+ int i;
+ for (i=0; i < conf.ntfragments; i++)
+ if (s->fragments[i])
+ html_fragment(&ho, s->fragments[i]);
+ }
html_section_title(&ho, s, f, keywords, &conf, TRUE);
html_section_title(&ho, s, f, keywords, &conf, TRUE);
element_open(&ho, "p");
if (p->private_data) {
htmlsect *s = (htmlsect *)p->private_data;
element_open(&ho, "p");
if (p->private_data) {
htmlsect *s = (htmlsect *)p->private_data;
- html_fragment(&ho, s->fragment);
+ int i;
+ for (i=0; i < conf.ntfragments; i++)
+ if (s->fragments[i])
+ html_fragment(&ho, s->fragments[i]);
}
html_nl(&ho);
html_words(&ho, p->kwtext, ALL,
}
html_nl(&ho);
html_words(&ho, p->kwtext, ALL,
element_open(&ho, "li");
if (p->private_data) {
htmlsect *s = (htmlsect *)p->private_data;
element_open(&ho, "li");
if (p->private_data) {
htmlsect *s = (htmlsect *)p->private_data;
- html_fragment(&ho, s->fragment);
+ int i;
+ for (i=0; i < conf.ntfragments; i++)
+ if (s->fragments[i])
+ html_fragment(&ho, s->fragments[i]);
}
html_nl(&ho);
stackhead->itemtype = LI;
}
html_nl(&ho);
stackhead->itemtype = LI;
/*
* Free all the working data.
*/
/*
* Free all the working data.
*/
- sfree(conf.asect);
- sfree(conf.single_filename);
- sfree(conf.contents_filename);
- sfree(conf.index_filename);
- sfree(conf.template_filename);
- sfree(conf.template_fragment);
{
htmlfragment *frag;
while ( (frag = (htmlfragment *)delpos234(files.frags, 0)) != NULL ) {
{
htmlfragment *frag;
while ( (frag = (htmlfragment *)delpos234(files.frags, 0)) != NULL ) {
htmlsect *sect, *tmp;
sect = sects.head;
while (sect) {
htmlsect *sect, *tmp;
sect = sects.head;
while (sect) {
+ for (i=0; i < conf.ntfragments; i++)
+ sfree(sect->fragments[i]);
+ sfree(sect->fragments);
sfree(sect);
sect = tmp;
}
sect = nonsects.head;
while (sect) {
sfree(sect);
sect = tmp;
}
sect = nonsects.head;
while (sect) {
+ for (i=0; i < conf.ntfragments; i++)
+ sfree(sect->fragments[i]);
+ sfree(sect->fragments);
sfree(sect);
sect = tmp;
}
sfree(sect);
sect = tmp;
}
+ sfree(conf.asect);
+ sfree(conf.single_filename);
+ sfree(conf.contents_filename);
+ sfree(conf.index_filename);
+ sfree(conf.template_filename);
+ while (conf.ntfragments--)
+ sfree(conf.template_fragments[conf.ntfragments]);
+ sfree(conf.template_fragments);
}
static void html_file_section(htmlconfig *cfg, htmlfilelist *files,
}
static void html_file_section(htmlconfig *cfg, htmlfilelist *files,
-static htmlsect *html_new_sect(htmlsectlist *list, paragraph *title)
+static htmlsect *html_new_sect(htmlsectlist *list, paragraph *title,
+ htmlconfig *cfg)
{
htmlsect *ret = snew(htmlsect);
{
htmlsect *ret = snew(htmlsect);
ret->parent = NULL;
ret->type = NORMAL;
ret->parent = NULL;
ret->type = NORMAL;
+ ret->fragments = snewn(cfg->ntfragments, char *);
+ {
+ int i;
+ for (i=0; i < cfg->ntfragments; i++)
+ ret->fragments[i] = NULL;
+ }
+
- html_href(ho, file, s->file, s->fragment);
+ html_href(ho, file, s->file, s->fragments[0]);
}
break;
case word_HyperEnd:
}
break;
case word_HyperEnd:
+ /* If there's nothing left, make something valid up */
+ if (!*text) {
+ static const char *anonfrag = "anon";
+ text = sresize(text, lenof(anonfrag), char);
+ strcpy(text, anonfrag);
+ }
+
/*
* Now we check for clashes with other fragment names, and
* adjust this one if necessary by appending a hyphen followed
/*
* Now we check for clashes with other fragment names, and
* adjust this one if necessary by appending a hyphen followed
return;
element_open(ho, "li");
return;
element_open(ho, "li");
- html_href(ho, thisfile, s->file, s->fragment);
+ html_href(ho, thisfile, s->file, s->fragments[0]);
html_section_title(ho, s, thisfile, keywords, cfg, FALSE);
element_close(ho, "a");
/* <li> will be closed by a later invocation */
html_section_title(ho, s, thisfile, keywords, cfg, FALSE);
element_close(ho, "a");
/* <li> will be closed by a later invocation */
char *s;
section = uadv(p->keyword);
char *s;
section = uadv(p->keyword);
- shortname = *section ? uadv(section) : NULL;
- longname = *shortname ? uadv(shortname) : NULL;
- kw = *longname ? uadv(longname) : NULL;
+ shortname = *section ? uadv(section) : L"";
+ longname = *shortname ? uadv(shortname) : L"";
+ kw = *longname ? uadv(longname) : L"";
- error(err_infodirentry, &p->fpos);
+ error(err_cfginsufarg, &p->fpos, p->origkeyword, 3);
-\dt \I{\cw{\\cfg\{html-template-fragment\}}}\cw{\\cfg\{html-template-fragment\}\{}\e{template}\cw{\}}
+\dt \I{\cw{\\cfg\{html-template-fragment\}}}\cw{\\cfg\{html-template-fragment\}\{}\e{template}\cw{\}}[\cw{\{}\e{template}\cw{\}\{}...\cw{\}}]
\dd This directive lets you specify a \i{template}, with exactly the
same syntax used in \cw{\\cfg\{html-template-filename\}} (see
\dd This directive lets you specify a \i{template}, with exactly the
same syntax used in \cw{\\cfg\{html-template-filename\}} (see
-\k{output-html-file}), to be used for the anchor names (\i\cw{<A
-NAME="...">}) used to allow URLs to refer to specific sections
+\k{output-html-file}), to be used for the anchor names (\i\cw{<a
+name="...">}) used to allow URLs to refer to specific sections
within a particular HTML file. So if you set this to \cq{%k},
for example, then each individual section in your document will be
addressable by means of a URL ending in a \c{#} followed by your
within a particular HTML file. So if you set this to \cq{%k},
for example, then each individual section in your document will be
addressable by means of a URL ending in a \c{#} followed by your
-Note that no checking is done that the anchor name is valid HTML. So
-if you use \cq{%k}, for example, you may need to restrict your use of
-keyword names.
+If more than one template is specified, anchors are generated in all
+the specified formats; Halibut's own cross-references are generated
+with the first template.
+
+Characters that are not permitted in anchor names are stripped. If
+there are no valid characters left, or a fragment is non-unique,
+Halibut starts inventing fragment names and suffixes as appropriate.
+
+Note that there are potentially fragment names that are not controlled
+by this mechanism, such as index references.
sp);
flags = FILEPOS;
break;
sp);
flags = FILEPOS;
break;
fpos = *va_arg(ap, filepos *);
fpos = *va_arg(ap, filepos *);
- sprintf(error, "\\cfg{info-dir-entry} expects at least three"
- " parameters");
+ sp = va_arg(ap, char *);
+ i = va_arg(ap, int);
+ sprintf(error, "\\cfg{%s} expects at least %d parameter%s", sp,
+ i, (i==1)?"":"s");
flags = FILEPOS;
break;
case err_infonodechar:
flags = FILEPOS;
break;
case err_infonodechar:
err_multikw, /* keyword clash in sections */
err_misplacedlcont, /* \lcont not after a list item */
err_sectmarkerinblock, /* section marker appeared in block */
err_multikw, /* keyword clash in sections */
err_misplacedlcont, /* \lcont not after a list item */
err_sectmarkerinblock, /* section marker appeared in block */
- err_infodirentry, /* \cfg{info-dir-entry} missing param */
+ err_cfginsufarg, /* \cfg{%s} insufficient args (<%d) */
err_infonodechar, /* colon/comma in node name in info */
err_text_codeline, /* \c line too long in text backend */
err_htmlver, /* unrecognised HTML version keyword */
err_infonodechar, /* colon/comma in node name in info */
err_text_codeline, /* \c line too long in text backend */
err_htmlver, /* unrecognised HTML version keyword */