Add an error check for correct formatting in Deflate uncompressed
[sgt/halibut] / bk_info.c
index 0f9f1e5..d898e61 100644 (file)
--- a/bk_info.c
+++ b/bk_info.c
@@ -1,5 +1,12 @@
 /*
- * info backend for Halibut
+ * Info backend for Halibut
+ *
+ * The Info file format isn't well-specified, and what specification
+ * there is is scattered all over the place.  Sources include:
+ *   (info), from GNU Texinfo.
+ *   (texinfo), also from GNU Texinfo.
+ *   (Emacs)Misc Help, and (emacs)Info Lookup, from GNU Emacs.
+ *   info.el, from GNU Emacs.
  * 
  * Possible future work:
  * 
  *        * Section 1.2: Nodename. Title of section.
  * 
  *  - might be helpful to diagnose duplicate node names!
+ *
+ *  - Indices generated by makeinfo use a menu rather than a bunch of
+ *    cross-references, which reduces visual clutter rather.  For
+ *    singly-referenced items, it looks like:
+ *      * toner cartridge, replacing:              Toner.
+ *    It does a horrid job on multiply-referenced entries, though,
+ *    perhaps because the name before the colon is meant to be unique.
+ *    Info's 'i' command requires the use of a menu -- it fails to
+ *    find any index entries at all with Halibut's current index format.
+ *
+ *  - The string "*note" is matched case-insensitively, so we could
+ *    make things slightly less ugly by using the lower-case version
+ *    when the user asks for \k.  Unfortunately, standalone Info seems
+ *    to match node names case-sensitively, so we can't downcase that.
+ *
+ *  - The character encoding used in an Info file can be configured using
+ *    an Emacs local variables block at the end, like this:
+ *      Local Variables:
+ *      coding: iso-8859-1
+ *      End:
  */
 
 #include <stdio.h>
 #include "halibut.h"
 
 typedef struct {
+    wchar_t *underline;
+} alignstruct;
+
+typedef struct {
     char *filename;
     int maxfilesize;
     int charset;
     int listindentbefore, listindentafter;
     int indent_code, width, index_width;
+    alignstruct atitle, achapter, *asect;
+    int nasect;
     wchar_t *bullet, *listsuffix;
     wchar_t *startemph, *endemph;
     wchar_t *lquote, *rquote;
-    wchar_t *sectsuffix, *underline;
+    wchar_t *sectsuffix;
     wchar_t *rule;
+    wchar_t *index_text;
 } infoconfig;
 
 typedef struct {
@@ -71,7 +105,8 @@ static int info_rdadds(info_data *, wchar_t const *);
 static int info_rdaddc(info_data *, char);
 static int info_rdaddsc(info_data *, char const *);
 
-static void info_heading(info_data *, word *, word *, int, infoconfig *);
+static void info_heading(info_data *, word *, word *, alignstruct, int,
+                        infoconfig *);
 static void info_rule(info_data *, int, int, infoconfig *);
 static void info_para(info_data *, word *, wchar_t *, word *, keywordlist *,
                      int, int, int, infoconfig *);
@@ -84,11 +119,13 @@ static int info_check_index(word *, node *, indexdata *);
 static int info_rdaddwc(info_data *, word *, word *, int, infoconfig *);
 
 static node *info_node_new(char *name, int charset);
-static char *info_node_name(paragraph *p, infoconfig *);
+static char *info_node_name_for_para(paragraph *p, infoconfig *);
+static char *info_node_name_for_text(wchar_t *text, infoconfig *);
 
 static infoconfig info_configure(paragraph *source) {
     infoconfig ret;
     paragraph *p;
+    int n;
 
     /*
      * Defaults.
@@ -109,7 +146,17 @@ static infoconfig info_configure(paragraph *source) {
     ret.lquote = L"\x2018\0\x2019\0`\0'\0\0";
     ret.rquote = uadv(ret.lquote);
     ret.sectsuffix = L": ";
-    ret.underline = L"\x203E\0-\0\0";
+    /*
+     * Default underline characters are chosen to match those recognised by
+     * Info-fontify-node.
+     */
+    ret.atitle.underline = L"*\0\0";
+    ret.achapter.underline = L"=\0\0";
+    ret.nasect = 2;
+    ret.asect = snewn(ret.nasect, alignstruct);
+    ret.asect[0].underline = L"-\0\0";
+    ret.asect[1].underline = L".\0\0";
+    ret.index_text = L"Index";
 
     /*
      * Two-pass configuration so that we can pick up global config
@@ -124,6 +171,8 @@ static infoconfig info_configure(paragraph *source) {
                    ret.lquote = uadv(p->keyword);
                    ret.rquote = uadv(ret.lquote);
                }
+           } else if (!ustricmp(p->keyword, L"index")) {
+               ret.index_text = uadv(p->keyword);
            }
        }
     }
@@ -150,7 +199,29 @@ static infoconfig info_configure(paragraph *source) {
            } else if (!ustricmp(p->keyword, L"info-section-suffix")) {
                ret.sectsuffix = uadv(p->keyword);
            } else if (!ustricmp(p->keyword, L"info-underline")) {
-               ret.underline = uadv(p->keyword);
+               ret.atitle.underline = ret.achapter.underline =
+                   uadv(p->keyword);
+               for (n = 0; n < ret.nasect; n++)
+                   ret.asect[n].underline = ret.atitle.underline;
+           } else if (!ustricmp(p->keyword, L"info-chapter-underline")) {
+               ret.achapter.underline = uadv(p->keyword);
+           } else if (!ustricmp(p->keyword, L"info-section-underline")) {
+               wchar_t *q = uadv(p->keyword);
+               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, alignstruct);
+                   for (i = ret.nasect; i <= n; i++)
+                       ret.asect[i] = ret.asect[ret.nasect-1];
+                   ret.nasect = n+1;
+               }
+               ret.asect[n].underline = q;
+           } else if (!ustricmp(p->keyword, L"text-title-underline")) {
+               ret.atitle.underline = uadv(p->keyword);
            } else if (!ustricmp(p->keyword, L"info-bullet")) {
                ret.bullet = uadv(p->keyword);
            } else if (!ustricmp(p->keyword, L"info-rule")) {
@@ -189,10 +260,20 @@ static infoconfig info_configure(paragraph *source) {
        ret.endemph = uadv(ret.startemph);
     }
 
-    while (*ret.underline && *uadv(ret.underline) &&
-          !cvt_ok(ret.charset, ret.underline))
-       ret.underline = uadv(ret.underline);
-
+    while (*ret.atitle.underline && *uadv(ret.atitle.underline) &&
+          !cvt_ok(ret.charset, ret.atitle.underline))
+       ret.atitle.underline = uadv(ret.atitle.underline);
+    
+    while (*ret.achapter.underline && *uadv(ret.achapter.underline) &&
+          !cvt_ok(ret.charset, ret.achapter.underline))
+       ret.achapter.underline = uadv(ret.achapter.underline);
+
+    for (n = 0; n < ret.nasect; n++) {
+       while (*ret.asect[n].underline && *uadv(ret.asect[n].underline) &&
+              !cvt_ok(ret.charset, ret.asect[n].underline))
+           ret.asect[n].underline = uadv(ret.asect[n].underline);
+    }
+    
     while (*ret.bullet && *uadv(ret.bullet) &&
           !cvt_ok(ret.charset, ret.bullet))
        ret.bullet = uadv(ret.bullet);
@@ -247,7 +328,7 @@ void info_backend(paragraph *sourceform, keywordlist *keywords,
            node *newnode, *upnode;
            char *nodename;
 
-           nodename = info_node_name(p, &conf);
+           nodename = info_node_name_for_para(p, &conf);
            newnode = info_node_new(nodename, conf.charset);
            sfree(nodename);
 
@@ -267,6 +348,9 @@ void info_backend(paragraph *sourceform, keywordlist *keywords,
            currnode = newnode;
        }
        break;
+      default:
+        p->private_data = NULL;
+        break;
     }
 
     /*
@@ -313,12 +397,12 @@ void info_backend(paragraph *sourceform, keywordlist *keywords,
            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"";
 
            if (!*longname) {
-               error(err_infodirentry, &p->fpos);
+               error(err_cfginsufarg, &p->fpos, p->origkeyword, 3);
                continue;
            }
 
@@ -360,7 +444,8 @@ void info_backend(paragraph *sourceform, keywordlist *keywords,
     /* Do the title */
     for (p = sourceform; p; p = p->next)
        if (p->type == para_Title)
-           info_heading(&topnode->text, NULL, p->words, conf.width, &conf);
+           info_heading(&topnode->text, NULL, p->words,
+                        conf.atitle, conf.width, &conf);
 
     nestindent = conf.listindentbefore + conf.listindentafter;
     nesting = 0;
@@ -417,7 +502,14 @@ void info_backend(paragraph *sourceform, keywordlist *keywords,
        info_menu_item(&currnode->up->text, currnode, p, &conf);
 
        has_index |= info_check_index(p->words, currnode, idx);
-       info_heading(&currnode->text, p->kwtext, p->words, conf.width, &conf);
+       if (p->type == para_Chapter || p->type == para_Appendix ||
+           p->type == para_UnnumberedChapter)
+           info_heading(&currnode->text, p->kwtext, p->words,
+                        conf.achapter, conf.width, &conf);
+       else
+           info_heading(&currnode->text, p->kwtext, p->words,
+                        conf.asect[p->aux>=conf.nasect?conf.nasect-1:p->aux],
+                        conf.width, &conf);
        nesting = 0;
        break;
 
@@ -492,15 +584,25 @@ void info_backend(paragraph *sourceform, keywordlist *keywords,
        node *newnode;
        int i, j, k;
        indexentry *entry;
+       char *nodename;
+
+       nodename = info_node_name_for_text(conf.index_text, &conf);
+       newnode = info_node_new(nodename, conf.charset);
+       sfree(nodename);
 
-       newnode = info_node_new("Index", conf.charset);
        newnode->up = topnode;
 
        currnode->next = newnode;
        newnode->prev = currnode;
        currnode->listnext = newnode;
 
-       info_rdaddsc(&newnode->text, "Index\n-----\n\n");
+       k = info_rdadds(&newnode->text, conf.index_text);
+       info_rdaddsc(&newnode->text, "\n");
+       while (k > 0) {
+           info_rdadds(&newnode->text, conf.achapter.underline);
+           k -= ustrwid(conf.achapter.underline, conf.charset);
+       }
+       info_rdaddsc(&newnode->text, "\n\n");
 
        info_menu_item(&topnode->text, newnode, NULL, &conf);
 
@@ -909,7 +1011,8 @@ static int info_width_xrefs(void *ctx, word *words)
 }
 
 static void info_heading(info_data *text, word *tprefix,
-                        word *words, int width, infoconfig *cfg) {
+                        word *words, alignstruct align,
+                        int width, infoconfig *cfg) {
     int length;
     int firstlinewidth, wrapwidth;
     wrappedline *wrapping, *p;
@@ -928,11 +1031,13 @@ static void info_heading(info_data *text, word *tprefix,
     for (p = wrapping; p; p = p->next) {
        length += info_rdaddwc(text, p->begin, p->end, FALSE, cfg);
        info_rdadd(text, L'\n');
-       while (length > 0) {
-           info_rdadds(text, cfg->underline);
-           length -= ustrwid(cfg->underline, cfg->charset);
+       if (*align.underline) {
+           while (length > 0) {
+               info_rdadds(text, align.underline);
+               length -= ustrwid(align.underline, cfg->charset);
+           }
+           info_rdadd(text, L'\n');
        }
-       info_rdadd(text, L'\n');
        length = 0;
     }
     wrap_free(wrapping);
@@ -1032,32 +1137,49 @@ static node *info_node_new(char *name, int charset)
     return n;
 }
 
-static char *info_node_name(paragraph *par, infoconfig *cfg)
+static char *info_node_name_core(info_data *id, filepos *fpos)
 {
-    info_data id = EMPTY_INFO_DATA;
     char *p, *q;
 
-    id.charset = cfg->charset;
-    info_rdaddwc(&id, par->kwtext ? par->kwtext : par->words,
-                NULL, FALSE, cfg);
-    info_rdaddsc(&id, NULL);
-
     /*
-     * We cannot have commas or colons in a node name. Remove any
-     * that we find, with a warning.
+     * We cannot have commas, colons or parentheses in a node name.
+     * Remove any that we find, with a warning.
      */
-    p = q = id.output.text;
+    p = q = id->output.text;
     while (*p) {
-       if (*p == ':' || *p == ',') {
-           error(err_infonodechar, &par->fpos, *p);
+       if (*p == ':' || *p == ',' || *p == '(' || *p == ')') {
+           error(err_infonodechar, fpos, *p);
        } else {
            *q++ = *p;
        }
        p++;
     }
-    *p = '\0';
+    *q = '\0';
+
+    return id->output.text;
+}
+
+static char *info_node_name_for_para(paragraph *par, infoconfig *cfg)
+{
+    info_data id = EMPTY_INFO_DATA;
+
+    id.charset = cfg->charset;
+    info_rdaddwc(&id, par->kwtext ? par->kwtext : par->words,
+                NULL, FALSE, cfg);
+    info_rdaddsc(&id, NULL);
+
+    return info_node_name_core(&id, &par->fpos);
+}
+
+static char *info_node_name_for_text(wchar_t *text, infoconfig *cfg)
+{
+    info_data id = EMPTY_INFO_DATA;
+
+    id.charset = cfg->charset;
+    info_rdadds(&id, text);
+    info_rdaddsc(&id, NULL);
 
-    return id.output.text;
+    return info_node_name_core(&id, NULL);
 }
 
 static void info_menu_item(info_data *text, node *n, paragraph *p,