X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/blobdiff_plain/90a0531efd29b72074a52bcb9fae7543464b83e0..8f664e7e91c918cd13248f6b684580c4dd2cdb31:/bk_info.c diff --git a/bk_info.c b/bk_info.c index 0f9f1e5..d898e61 100644 --- 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: * @@ -21,6 +28,26 @@ * * 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 @@ -29,16 +56,23 @@ #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,