Escape &<> when they appear in href text.
[sgt/halibut] / bk_html.c
index 1d4aa5b..e3202a5 100644 (file)
--- a/bk_html.c
+++ b/bk_html.c
@@ -37,7 +37,7 @@
                           (p)->type == para_Title ? -1 : 0 )
 
 typedef struct {
-    int just_numbers;
+    int number_at_all, just_numbers;
     wchar_t *number_suffix;
 } sectlevel;
 
@@ -61,7 +61,7 @@ typedef struct {
     char *body_tag, *nav_attr;
     wchar_t *author, *description;
     wchar_t *index_text, *contents_text, *preamble_text, *title_separator;
-    wchar_t *nav_prev_text, *nav_next_text, *nav_separator;
+    wchar_t *nav_prev_text, *nav_next_text, *nav_up_text, *nav_separator;
     wchar_t *index_main_sep, *index_multi_sep;
     wchar_t *pre_versionid, *post_versionid;
     int restrict_charset, output_charset;
@@ -248,10 +248,12 @@ static htmlconfig html_configure(paragraph *source) {
      */
     ret.leaf_level = 2;
     ret.achapter.just_numbers = FALSE;
+    ret.achapter.number_at_all = TRUE;
     ret.achapter.number_suffix = L": ";
     ret.nasect = 1;
     ret.asect = snewn(ret.nasect, sectlevel);
     ret.asect[0].just_numbers = TRUE;
+    ret.asect[0].number_at_all = TRUE;
     ret.asect[0].number_suffix = L" ";
     ret.ncdepths = 0;
     ret.contents_depths = 0;
@@ -282,6 +284,7 @@ static htmlconfig html_configure(paragraph *source) {
     ret.title_separator = L" - ";
     ret.nav_prev_text = L"Previous";
     ret.nav_next_text = L"Next";
+    ret.nav_up_text = L"Up";
     ret.nav_separator = L" | ";
     ret.index_main_sep = L": ";
     ret.index_multi_sep = L", ";
@@ -381,6 +384,8 @@ static htmlconfig html_configure(paragraph *source) {
                    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-shownumber")) {
+               ret.achapter.number_at_all = utob(uadv(k));
            } else if (!ustricmp(k, L"html-suppress-navlinks")) {
                ret.navlinks = !utob(uadv(k));
            } else if (!ustricmp(k, L"html-rellinks")) {
@@ -410,6 +415,21 @@ static htmlconfig html_configure(paragraph *source) {
                    ret.nasect = n+1;
                }
                ret.asect[n].just_numbers = utob(q);
+           } else if (!ustricmp(k, L"html-section-shownumber")) {
+               wchar_t *q = uadv(k);
+               int n = 0;
+               if (uisdigit(*q)) {
+                   n = utoi(q);
+                   q = uadv(q);
+               }
+               if (n >= ret.nasect) {
+                   int i;
+                   ret.asect = sresize(ret.asect, n+1, sectlevel);
+                   for (i = ret.nasect; i <= n; i++)
+                       ret.asect[i] = ret.asect[ret.nasect-1];
+                   ret.nasect = n+1;
+               }
+               ret.asect[n].number_at_all = utob(q);
            } else if (!ustricmp(k, L"html-section-suffix")) {
                wchar_t *q = uadv(k);
                int n = 0;
@@ -494,6 +514,8 @@ static htmlconfig html_configure(paragraph *source) {
                ret.nav_prev_text = uadv(k);
            } else if (!ustricmp(k, L"html-nav-next-text")) {
                ret.nav_next_text = uadv(k);
+           } else if (!ustricmp(k, L"html-nav-up-text")) {
+               ret.nav_up_text = uadv(k);
            } else if (!ustricmp(k, L"html-nav-separator")) {
                ret.nav_separator = uadv(k);
            } else if (!ustricmp(k, L"html-index-main-separator")) {
@@ -862,7 +884,10 @@ void html_backend(paragraph *sourceform, keywordlist *keywords,
 #define listname(lt) ( (lt)==UL ? "ul" : (lt)==OL ? "ol" : "dl" )
 #define itemname(lt) ( (lt)==LI ? "li" : (lt)==DT ? "dt" : "dd" )
 
-           ho.fp = fopen(f->filename, "w");
+           if (!strcmp(f->filename, "-"))
+               ho.fp = stdout;
+           else
+               ho.fp = fopen(f->filename, "w");
            if (!ho.fp)
                error(err_cantopenw, f->filename);
 
@@ -970,8 +995,6 @@ void html_backend(paragraph *sourceform, keywordlist *keywords,
                    html_nl(&ho);
                }
 
-               /* FIXME: link rel="up" */
-
                if (f != files.head) {
                    element_empty(&ho, "link");
                    element_attr(&ho, "rel", "ToC");
@@ -979,6 +1002,17 @@ void html_backend(paragraph *sourceform, keywordlist *keywords,
                    html_nl(&ho);
                }
 
+               if (conf.leaf_level > 0) {
+                   htmlsect *p = f->first->parent;
+                   assert(p == f->last->parent);
+                   if (p) {
+                       element_empty(&ho, "link");
+                       element_attr(&ho, "rel", "up");
+                       element_attr(&ho, "href", p->file->filename);
+                       html_nl(&ho);
+                   }
+               }
+
                if (has_index && files.index && f != files.index) {
                    element_empty(&ho, "link");
                    element_attr(&ho, "rel", "index");
@@ -1057,6 +1091,22 @@ void html_backend(paragraph *sourceform, keywordlist *keywords,
                if (f != files.head)
                    element_close(&ho, "a");
 
+               /* We don't bother with "Up" links for leaf-level 1,
+                * as they would be identical to the "Contents" links. */
+               if (conf.leaf_level >= 2) {
+                   htmlsect *p = f->first->parent;
+                   assert(p == f->last->parent);
+                   html_text(&ho, conf.nav_separator);
+                   if (p) {
+                       element_open(&ho, "a");
+                       element_attr(&ho, "href", p->file->filename);
+                   }
+                   html_text(&ho, conf.nav_up_text);
+                   if (p) {
+                       element_close(&ho, "a");
+                   }
+               }
+
                if (has_index && files.index) {
                    html_text(&ho, conf.nav_separator);
                    if (f != files.index) {
@@ -2126,7 +2176,7 @@ static void html_words(htmloutput *ho, word *words, int flags,
                       htmlfile *file, keywordlist *keywords, htmlconfig *cfg)
 {
     word *w;
-    char *c;
+    char *c, *c2, *p, *q;
     int style, type;
 
     for (w = words; w; w = w->next) switch (w->type) {
@@ -2134,7 +2184,20 @@ static void html_words(htmloutput *ho, word *words, int flags,
        if (flags & LINKS) {
            element_open(ho, "a");
            c = utoa_dup(w->text, CS_ASCII);
-           element_attr(ho, "href", c);
+           c2 = snewn(1 + 10*strlen(c), char);
+           for (p = c, q = c2; *p; p++) {
+               if (*p == '&')
+                   q += sprintf(q, "&amp;");
+               else if (*p == '<')
+                   q += sprintf(q, "&lt;");
+               else if (*p == '>')
+                   q += sprintf(q, "&gt;");
+               else
+                   *q++ = *p;
+           }
+           *q = '\0';
+           element_attr(ho, "href", c2);
+           sfree(c2);
            sfree(c);
        }
        break;
@@ -2460,7 +2523,7 @@ static void html_text_limit_internal(htmloutput *ho, wchar_t const *text,
 static void cleanup(htmloutput *ho)
 {
     return_to_neutral(ho);
-    if (ho->fp)
+    if (ho->fp && ho->fp != stdout)
        fclose(ho->fp);
 }
 
@@ -2738,7 +2801,7 @@ static void html_section_title(htmloutput *ho, htmlsect *s, htmlfile *thisfile,
        else
            sl = &cfg->asect[cfg->nasect-1];
 
-       if (!sl)
+       if (!sl || !sl->number_at_all)
            number = NULL;
        else if (sl->just_numbers)
            number = s->title->kwtext2;