From 5dd44dceca3dd3c1a4886dd6a7bdf05924e3447f Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 9 Apr 2004 18:16:43 +0000 Subject: [PATCH] Added an info(1) backend, which constructs .info files directly without going through the .texi source stage. A few things left to do, notably documentation, but the basics all seem to be there. git-svn-id: svn://svn.tartarus.org/sgt/halibut@4047 cda61777-01e9-0310-a592-d414129be87e --- Makefile | 2 +- bk_info.c | 965 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bk_xhtml.c | 3 +- doc/.cvsignore | 1 + doc/Makefile | 4 +- halibut.h | 16 +- main.c | 1 + misc.c | 4 +- ustring.c | 4 +- 9 files changed, 988 insertions(+), 12 deletions(-) create mode 100644 bk_info.c diff --git a/Makefile b/Makefile index 3264a70..3802c58 100644 --- a/Makefile +++ b/Makefile @@ -100,7 +100,7 @@ SRC := ../ MODULES := main malloc ustring error help licence version misc tree234 MODULES += input keywords contents index style biblio -MODULES += bk_text bk_xhtml bk_whlp bk_man +MODULES += bk_text bk_xhtml bk_whlp bk_man bk_info MODULES += winhelp OBJECTS := $(addsuffix .o,$(MODULES)) diff --git a/bk_info.c b/bk_info.c new file mode 100644 index 0000000..088c72d --- /dev/null +++ b/bk_info.c @@ -0,0 +1,965 @@ +/* + * info backend for Halibut + * + * TODO: + * + * - basic vital configuration: Info dir entries in heading, and + * how to allocate node names + * - escape, warn or simply remove commas and colons in node + * names; also test colons in index terms. + * - test everything in info(1), and probably jed too + * + * Later: + * + * - configurable indentation, bullets, emphasis, quotes etc? + */ + +#include +#include +#include +#include "halibut.h" + +typedef struct { + char *filename; + int maxfilesize; +} infoconfig; + +typedef struct node_tag node; +struct node_tag { + node *listnext; + node *up, *prev, *next, *lastchild; + int pos, started_menu, filenum; + char *name; + rdstringc text; +}; + +typedef struct { + char *text; + int nnodes, nodesize; + node **nodes; +} info_idx; + +static int info_convert(wchar_t *, char **); + +static void info_heading(rdstringc *, word *, word *, int); +static void info_rule(rdstringc *, int, int); +static void info_para(rdstringc *, word *, char *, word *, keywordlist *, + int, int, int); +static void info_codepara(rdstringc *, word *, int, int); +static void info_versionid(rdstringc *, word *); +static void info_menu_item(rdstringc *, node *, paragraph *); +static word *info_transform_wordlist(word *, keywordlist *); +static int info_check_index(word *, node *, indexdata *); + +static void info_rdaddwc(rdstringc *, word *, word *, int); + +static node *info_node_new(char *name); +static char *info_node_name(paragraph *p); + +static infoconfig info_configure(paragraph *source) { + infoconfig ret; + + /* + * Defaults. + */ + ret.filename = dupstr("output.info"); + ret.maxfilesize = 64 << 10; + + for (; source; source = source->next) { + if (source->type == para_Config) { + if (!ustricmp(source->keyword, L"info-filename")) { + sfree(ret.filename); + ret.filename = utoa_dup(uadv(source->keyword)); + } else if (!ustricmp(source->keyword, L"info-max-file-size")) { + ret.maxfilesize = utoi(uadv(source->keyword)); + } + } + } + + return ret; +} + +paragraph *info_config_filename(char *filename) +{ + paragraph *p; + wchar_t *ufilename, *up; + int len; + + p = mknew(paragraph); + memset(p, 0, sizeof(*p)); + p->type = para_Config; + p->next = NULL; + p->fpos.filename = ""; + p->fpos.line = p->fpos.col = -1; + + ufilename = ufroma_dup(filename); + len = ustrlen(ufilename) + 2 + lenof(L"info-filename"); + p->keyword = mknewa(wchar_t, len); + up = p->keyword; + ustrcpy(up, L"info-filename"); + up = uadv(up); + ustrcpy(up, ufilename); + up = uadv(up); + *up = L'\0'; + assert(up - p->keyword < len); + sfree(ufilename); + + return p; +} + +void info_backend(paragraph *sourceform, keywordlist *keywords, + indexdata *idx) { + paragraph *p; + infoconfig conf; + word *prefix, *body, *wp; + word spaceword; + char *prefixextra; + int nesting, nestindent; + int indentb, indenta; + int filepos; + int has_index; + rdstringc intro_text = { 0, 0, NULL }; + node *topnode, *currnode; + word bullet; + FILE *fp; + + /* + * FIXME + */ + int width = 70, listindentbefore = 1, listindentafter = 3; + int indent_code = 2, index_width = 40; + + IGNORE(keywords); /* we don't happen to need this */ + IGNORE(idx); /* or this */ + + conf = info_configure(sourceform); + + /* + * Go through and create a node for each section. + */ + topnode = info_node_new("Top"); + currnode = topnode; + for (p = sourceform; p; p = p->next) switch (p->type) { + /* + * Chapter titles. + */ + case para_Chapter: + case para_Appendix: + case para_UnnumberedChapter: + case para_Heading: + case para_Subsect: + { + node *newnode, *upnode; + char *nodename; + + nodename = info_node_name(p); + newnode = info_node_new(nodename); + sfree(nodename); + + p->private_data = newnode; + + if (p->parent) + upnode = (node *)p->parent->private_data; + else + upnode = topnode; + assert(upnode); + newnode->up = upnode; + + currnode->next = newnode; + newnode->prev = currnode; + + currnode->listnext = newnode; + currnode = newnode; + } + break; + } + + /* + * Set up the display form of each index entry. + */ + { + int i; + indexentry *entry; + + for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++) { + info_idx *ii = mknew(info_idx); + rdstringc rs = { 0, 0, NULL }; + + ii->nnodes = ii->nodesize = 0; + ii->nodes = NULL; + + info_rdaddwc(&rs, entry->text, NULL, FALSE); + /* + * FIXME: splatter colons. + */ + ii->text = rs.text; + + entry->backend_data = ii; + } + } + + /* + * An Info file begins with a piece of introductory text which + * is apparently never shown anywhere. This seems to me to be a + * good place to put the copyright notice and the version IDs. + * + * FIXME: also Info directory entries are expected to go here. + * This will need to be a configurable thing of some sort. + */ + + rdaddsc(&intro_text, + "This Info file generated by Halibut, "); + rdaddsc(&intro_text, version); + rdaddsc(&intro_text, "\n\n"); + + for (p = sourceform; p; p = p->next) + if (p->type == para_Copyright) + info_para(&intro_text, NULL, NULL, p->words, keywords, + 0, 0, width); + + for (p = sourceform; p; p = p->next) + if (p->type == para_VersionID) + info_versionid(&intro_text, p->words); + + if (intro_text.text[intro_text.pos-1] != '\n') + rdaddc(&intro_text, '\n'); + + /* Do the title */ + for (p = sourceform; p; p = p->next) + if (p->type == para_Title) + info_heading(&topnode->text, NULL, p->words, width); + + nestindent = listindentbefore + listindentafter; + nesting = 0; + + currnode = topnode; + + /* Do the main document */ + for (p = sourceform; p; p = p->next) switch (p->type) { + + case para_QuotePush: + nesting += 2; + break; + case para_QuotePop: + nesting -= 2; + assert(nesting >= 0); + break; + + case para_LcontPush: + nesting += nestindent; + break; + case para_LcontPop: + nesting -= nestindent; + assert(nesting >= 0); + break; + + /* + * Things we ignore because we've already processed them or + * aren't going to touch them in this pass. + */ + case para_IM: + case para_BR: + case para_Biblio: /* only touch BiblioCited */ + case para_VersionID: + case para_NoCite: + case para_Title: + break; + + /* + * Chapter titles. + */ + case para_Chapter: + case para_Appendix: + case para_UnnumberedChapter: + case para_Heading: + case para_Subsect: + currnode = p->private_data; + assert(currnode); + assert(currnode->up); + + if (!currnode->up->started_menu) { + rdaddsc(&currnode->up->text, "* Menu:\n\n"); + currnode->up->started_menu = TRUE; + } + info_menu_item(&currnode->up->text, currnode, p); + + has_index |= info_check_index(p->words, currnode, idx); + info_heading(&currnode->text, p->kwtext, p->words, width); + nesting = 0; + break; + + case para_Rule: + info_rule(&currnode->text, nesting, width - nesting); + break; + + case para_Normal: + case para_Copyright: + case para_DescribedThing: + case para_Description: + case para_BiblioCited: + case para_Bullet: + case para_NumberedList: + has_index |= info_check_index(p->words, currnode, idx); + if (p->type == para_Bullet) { + bullet.next = NULL; + bullet.alt = NULL; + bullet.type = word_Normal; + bullet.text = L"-"; /* FIXME: configurability */ + prefix = • + prefixextra = NULL; + indentb = listindentbefore; + indenta = listindentafter; + } else if (p->type == para_NumberedList) { + prefix = p->kwtext; + prefixextra = "."; /* FIXME: configurability */ + indentb = listindentbefore; + indenta = listindentafter; + } else if (p->type == para_Description) { + prefix = NULL; + prefixextra = NULL; + indentb = listindentbefore; + indenta = listindentafter; + } else { + prefix = NULL; + prefixextra = NULL; + indentb = indenta = 0; + } + if (p->type == para_BiblioCited) { + body = dup_word_list(p->kwtext); + for (wp = body; wp->next; wp = wp->next); + wp->next = &spaceword; + spaceword.next = p->words; + spaceword.alt = NULL; + spaceword.type = word_WhiteSpace; + spaceword.text = NULL; + } else { + wp = NULL; + body = p->words; + } + info_para(&currnode->text, prefix, prefixextra, body, keywords, + nesting + indentb, indenta, + width - nesting - indentb - indenta); + if (wp) { + wp->next = NULL; + free_word_list(body); + } + break; + + case para_Code: + info_codepara(&currnode->text, p->words, + nesting + indent_code, + width - nesting - 2 * indent_code); + break; + } + + /* + * Create an index node if required. + */ + if (has_index) { + node *newnode; + int i, j, k; + indexentry *entry; + + newnode = info_node_new("Index"); + newnode->up = topnode; + + currnode->next = newnode; + newnode->prev = currnode; + currnode->listnext = newnode; + + rdaddsc(&newnode->text, "Index\n-----\n\n* Menu:\n\n"); + + info_menu_item(&topnode->text, newnode, NULL); + + for (i = 0; (entry = index234(idx->entries, i)) != NULL; i++) { + info_idx *ii = (info_idx *)entry->backend_data; + + for (j = 0; j < ii->nnodes; j++) { + int pos0 = newnode->text.pos; + rdaddsc(&newnode->text, "* "); + /* + * When we have multiple references for a single + * index term, we only display the actual term on + * the first line, to make it clear that the terms + * really are the same. + */ + if (j == 0) + rdaddsc(&newnode->text, ii->text); + for (k = newnode->text.pos - pos0; k < index_width; k++) + rdaddc(&newnode->text, ' '); + rdaddsc(&newnode->text, ": "); + rdaddsc(&newnode->text, ii->nodes[j]->name); + rdaddsc(&newnode->text, ".\n"); + } + } + } + + /* + * Finalise the text of each node, by adding the ^_ delimiter + * and the node line at the top. + */ + for (currnode = topnode; currnode; currnode = currnode->listnext) { + char *origtext = currnode->text.text; + currnode->text.text = NULL; + currnode->text.pos = currnode->text.size = 0; + rdaddsc(&currnode->text, "\037\nFile: "); + rdaddsc(&currnode->text, conf.filename); + rdaddsc(&currnode->text, ", Node: "); + rdaddsc(&currnode->text, currnode->name); + if (currnode->prev) { + rdaddsc(&currnode->text, ", Prev: "); + rdaddsc(&currnode->text, currnode->prev->name); + } + rdaddsc(&currnode->text, ", Up: "); + rdaddsc(&currnode->text, (currnode->up ? + currnode->up->name : "(dir)")); + if (currnode->next) { + rdaddsc(&currnode->text, ", Next: "); + rdaddsc(&currnode->text, currnode->next->name); + } + rdaddsc(&currnode->text, "\n\n"); + rdaddsc(&currnode->text, origtext); + /* + * Just make _absolutely_ sure we end with a newline. + */ + if (currnode->text.text[currnode->text.pos-1] != '\n') + rdaddc(&currnode->text, '\n'); + + sfree(origtext); + } + + /* + * Compute the offsets for the tag table. + */ + filepos = intro_text.pos; + for (currnode = topnode; currnode; currnode = currnode->listnext) { + currnode->pos = filepos; + filepos += currnode->text.pos; + } + + /* + * Split into sub-files. + */ + if (conf.maxfilesize > 0) { + int currfilesize = intro_text.pos, currfilenum = 1; + for (currnode = topnode; currnode; currnode = currnode->listnext) { + if (currfilesize > intro_text.pos && + currfilesize + currnode->text.pos > conf.maxfilesize) { + currfilenum++; + currfilesize = intro_text.pos; + } + currnode->filenum = currfilenum; + currfilesize += currnode->text.pos; + } + } + + /* + * Write the primary output file. + */ + fp = fopen(conf.filename, "w"); + if (!fp) { + error(err_cantopenw, conf.filename); + return; + } + fputs(intro_text.text, fp); + if (conf.maxfilesize == 0) { + for (currnode = topnode; currnode; currnode = currnode->listnext) + fputs(currnode->text.text, fp); + } else { + int filenum = 0; + fprintf(fp, "\037\nIndirect:\n"); + for (currnode = topnode; currnode; currnode = currnode->listnext) + if (filenum != currnode->filenum) { + filenum = currnode->filenum; + fprintf(fp, "%s-%d: %d\n", conf.filename, filenum, + currnode->pos); + } + } + fprintf(fp, "\037\nTag Table:\n"); + if (conf.maxfilesize > 0) + fprintf(fp, "(Indirect)\n"); + for (currnode = topnode; currnode; currnode = currnode->listnext) + fprintf(fp, "Node: %s\177%d\n", currnode->name, currnode->pos); + fprintf(fp, "\037\nEnd Tag Table\n"); + fclose(fp); + + /* + * Write the subfiles. + */ + if (conf.maxfilesize > 0) { + int filenum = 0; + fp = NULL; + + for (currnode = topnode; currnode; currnode = currnode->listnext) { + if (filenum != currnode->filenum) { + char *fname; + + filenum = currnode->filenum; + + if (fp) + fclose(fp); + fname = mknewa(char, strlen(conf.filename) + 40); + sprintf(fname, "%s-%d", conf.filename, filenum); + fp = fopen(fname, "w"); + if (!fp) { + error(err_cantopenw, fname); + return; + } + sfree(fname); + fputs(intro_text.text, fp); + } + fputs(currnode->text.text, fp); + } + + if (fp) + fclose(fp); + } +} + +static int info_check_index(word *w, node *n, indexdata *idx) +{ + int ret = 0; + + for (; w; w = w->next) { + if (w->type == word_IndexRef) { + indextag *tag; + int i; + + tag = index_findtag(idx, w->text); + if (!tag) + break; + + for (i = 0; i < tag->nrefs; i++) { + indexentry *entry = tag->refs[i]; + info_idx *ii = (info_idx *)entry->backend_data; + + if (ii->nnodes > 0 && ii->nodes[ii->nnodes-1] == n) { + /* + * If the same index term is indexed twice + * within the same section, we only want to + * mention it once in the index. So do nothing + * here. + */ + continue; + } + + if (ii->nnodes >= ii->nodesize) { + ii->nodesize += 32; + ii->nodes = resize(ii->nodes, ii->nodesize); + } + + ii->nodes[ii->nnodes++] = n; + + ret = 1; + } + } + } + + return ret; +} + +/* + * Convert a wide string into a string of chars. If `result' is + * non-NULL, mallocs the resulting string and stores a pointer to + * it in `*result'. If `result' is NULL, merely checks whether all + * characters in the string are feasible for the output character + * set. + * + * Return is nonzero if all characters are OK. If not all + * characters are OK but `result' is non-NULL, a result _will_ + * still be generated! + */ +static int info_convert(wchar_t *s, char **result) { + /* + * FIXME. Currently this is ISO8859-1 only. + */ + int doing = (result != 0); + int ok = TRUE; + char *p = NULL; + int plen = 0, psize = 0; + + for (; *s; s++) { + wchar_t c = *s; + char outc; + + if ((c >= 32 && c <= 126) || + (c >= 160 && c <= 255)) { + /* Char is OK. */ + outc = (char)c; + } else { + /* Char is not OK. */ + ok = FALSE; + outc = 0xBF; /* approximate the good old DEC `uh?' */ + } + if (doing) { + if (plen >= psize) { + psize = plen + 256; + p = resize(p, psize); + } + p[plen++] = outc; + } + } + if (doing) { + p = resize(p, plen+1); + p[plen] = '\0'; + *result = p; + } + return ok; +} + +static word *info_transform_wordlist(word *words, keywordlist *keywords) +{ + word *ret = dup_word_list(words); + word *w; + keyword *kwl; + + for (w = ret; w; w = w->next) { + w->private_data = NULL; + if (w->type == word_UpperXref || w->type == word_LowerXref) { + kwl = kw_lookup(keywords, w->text); + if (kwl) { + if (kwl->para->type == para_NumberedList || + kwl->para->type == para_BiblioCited) { + /* + * In Info, we do nothing special for xrefs to + * numbered list items or bibliography entries. + */ + break; + } else { + /* + * An xref to a different section has its text + * completely replaced. + */ + word *w2, *w3, *w4; + w2 = w3 = w->next; + w4 = NULL; + while (w2) { + if (w2->type == word_XrefEnd) { + w4 = w2->next; + w2->next = NULL; + break; + } + w2 = w2->next; + } + free_word_list(w3); + + /* + * Now w is the UpperXref / LowerXref we + * started with, and w4 is the next word after + * the corresponding XrefEnd (if any). The + * simplest thing is just to stick a pointer to + * the target node structure in the private + * data field of the xref word, and let + * info_rdaddwc and friends read the node name + * out from there. + */ + w->next = w4; + w->private_data = kwl->para->private_data; + assert(w->private_data); + } + } + } + } + + return ret; +} + +static void info_rdaddwc(rdstringc *rs, word *words, word *end, int xrefs) { + char *c; + + for (; words && words != end; words = words->next) switch (words->type) { + case word_HyperLink: + case word_HyperEnd: + case word_XrefEnd: + case word_IndexRef: + break; + + case word_Normal: + case word_Emph: + case word_Code: + case word_WeakCode: + case word_WhiteSpace: + case word_EmphSpace: + case word_CodeSpace: + case word_WkCodeSpace: + case word_Quote: + case word_EmphQuote: + case word_CodeQuote: + case word_WkCodeQuote: + assert(words->type != word_CodeQuote && + words->type != word_WkCodeQuote); + if (towordstyle(words->type) == word_Emph && + (attraux(words->aux) == attr_First || + attraux(words->aux) == attr_Only)) + rdaddc(rs, '_'); /* FIXME: configurability */ + else if (towordstyle(words->type) == word_Code && + (attraux(words->aux) == attr_First || + attraux(words->aux) == attr_Only)) + rdaddc(rs, '`'); /* FIXME: configurability */ + if (removeattr(words->type) == word_Normal) { + if (info_convert(words->text, &c)) + rdaddsc(rs, c); + else + info_rdaddwc(rs, words->alt, NULL, FALSE); + sfree(c); + } else if (removeattr(words->type) == word_WhiteSpace) { + rdaddc(rs, ' '); + } else if (removeattr(words->type) == word_Quote) { + rdaddc(rs, quoteaux(words->aux) == quote_Open ? '`' : '\''); + /* FIXME: configurability */ + } + if (towordstyle(words->type) == word_Emph && + (attraux(words->aux) == attr_Last || + attraux(words->aux) == attr_Only)) + rdaddc(rs, '_'); /* FIXME: configurability */ + else if (towordstyle(words->type) == word_Code && + (attraux(words->aux) == attr_Last || + attraux(words->aux) == attr_Only)) + rdaddc(rs, '\''); /* FIXME: configurability */ + break; + + case word_UpperXref: + case word_LowerXref: + if (xrefs && words->private_data) { + rdaddsc(rs, "*Note "); + rdaddsc(rs, ((node *)words->private_data)->name); + rdaddsc(rs, "::"); + } + break; + } +} + +static int info_width_internal(word *words, int xrefs); + +static int info_width_internal_list(word *words, int xrefs) { + int w = 0; + while (words) { + w += info_width_internal(words, xrefs); + words = words->next; + } + return w; +} + +static int info_width_internal(word *words, int xrefs) { + switch (words->type) { + case word_HyperLink: + case word_HyperEnd: + case word_XrefEnd: + case word_IndexRef: + return 0; + + case word_Normal: + case word_Emph: + case word_Code: + case word_WeakCode: + return (((words->type == word_Emph || + words->type == word_Code) + ? (attraux(words->aux) == attr_Only ? 2 : + attraux(words->aux) == attr_Always ? 0 : 1) + : 0) + + (info_convert(words->text, NULL) ? + ustrlen(words->text) : + info_width_internal_list(words->alt, xrefs))); + + case word_WhiteSpace: + case word_EmphSpace: + case word_CodeSpace: + case word_WkCodeSpace: + case word_Quote: + case word_EmphQuote: + case word_CodeQuote: + case word_WkCodeQuote: + assert(words->type != word_CodeQuote && + words->type != word_WkCodeQuote); + return (((towordstyle(words->type) == word_Emph || + towordstyle(words->type) == word_Code) + ? (attraux(words->aux) == attr_Only ? 2 : + attraux(words->aux) == attr_Always ? 0 : 1) + : 0) + 1); + + case word_UpperXref: + case word_LowerXref: + if (xrefs && words->private_data) { + /* "*Note " plus "::" comes to 8 characters */ + return 8 + strlen(((node *)words->private_data)->name); + } + break; + } + return 0; /* should never happen */ +} + +static int info_width_noxrefs(word *words) +{ + return info_width_internal(words, FALSE); +} +static int info_width_xrefs(word *words) +{ + return info_width_internal(words, TRUE); +} + +static void info_heading(rdstringc *text, word *tprefix, + word *words, int width) { + rdstringc t = { 0, 0, NULL }; + int margin, length; + int firstlinewidth, wrapwidth; + int i; + wrappedline *wrapping, *p; + + if (tprefix) { + info_rdaddwc(&t, tprefix, NULL, FALSE); + rdaddsc(&t, ": "); /* FIXME: configurability */ + } + margin = length = (t.text ? strlen(t.text) : 0); + + margin = 0; + firstlinewidth = width - length; + wrapwidth = width; + + wrapping = wrap_para(words, firstlinewidth, wrapwidth, info_width_noxrefs); + for (p = wrapping; p; p = p->next) { + info_rdaddwc(&t, p->begin, p->end, FALSE); + length = (t.text ? strlen(t.text) : 0); + for (i = 0; i < margin; i++) + rdaddc(text, ' '); + rdaddsc(text, t.text); + rdaddc(text, '\n'); + for (i = 0; i < margin; i++) + rdaddc(text, ' '); + while (length--) + rdaddc(text, '-'); + rdaddc(text, '\n'); + margin = 0; + sfree(t.text); + t = empty_rdstringc; + } + wrap_free(wrapping); + rdaddc(text, '\n'); + + sfree(t.text); +} + +static void info_rule(rdstringc *text, int indent, int width) { + while (indent--) rdaddc(text, ' '); + while (width--) rdaddc(text, '-'); + rdaddc(text, '\n'); + rdaddc(text, '\n'); +} + +static void info_para(rdstringc *text, word *prefix, char *prefixextra, + word *input, keywordlist *keywords, + int indent, int extraindent, int width) { + wrappedline *wrapping, *p; + word *words; + rdstringc pfx = { 0, 0, NULL }; + int e; + int i; + int firstlinewidth = width; + + words = info_transform_wordlist(input, keywords); + + if (prefix) { + info_rdaddwc(&pfx, prefix, NULL, FALSE); + if (prefixextra) + rdaddsc(&pfx, prefixextra); + for (i = 0; i < indent; i++) + rdaddc(text, ' '); + rdaddsc(text, pfx.text); + /* If the prefix is too long, shorten the first line to fit. */ + e = extraindent - strlen(pfx.text); + if (e < 0) { + firstlinewidth += e; /* this decreases it, since e < 0 */ + if (firstlinewidth < 0) { + e = indent + extraindent; + firstlinewidth = width; + rdaddc(text, '\n'); + } else + e = 0; + } + sfree(pfx.text); + } else + e = indent + extraindent; + + wrapping = wrap_para(words, firstlinewidth, width, info_width_xrefs); + for (p = wrapping; p; p = p->next) { + for (i = 0; i < e; i++) + rdaddc(text, ' '); + info_rdaddwc(text, p->begin, p->end, TRUE); + rdaddc(text, '\n'); + e = indent + extraindent; + } + wrap_free(wrapping); + rdaddc(text, '\n'); + + free_word_list(words); +} + +static void info_codepara(rdstringc *text, word *words, + int indent, int width) { + int i; + + for (; words; words = words->next) if (words->type == word_WeakCode) { + char *c; + info_convert(words->text, &c); + if (strlen(c) > (size_t)width) { + /* FIXME: warn */ + } + for (i = 0; i < indent; i++) + rdaddc(text, ' '); + rdaddsc(text, c); + rdaddc(text, '\n'); + sfree(c); + } + + rdaddc(text, '\n'); +} + +static void info_versionid(rdstringc *text, word *words) { + rdaddc(text, '['); /* FIXME: configurability */ + info_rdaddwc(text, words, NULL, FALSE); + rdaddsc(text, "]\n"); +} + +static node *info_node_new(char *name) +{ + node *n; + + n = mknew(node); + n->text.text = NULL; + n->text.pos = n->text.size = 0; + n->up = n->next = n->prev = n->lastchild = n->listnext = NULL; + n->name = dupstr(name); + n->started_menu = FALSE; + + return n; +} + +static char *info_node_name(paragraph *p) +{ + rdstringc rsc = { 0, 0, NULL }; + info_rdaddwc(&rsc, p->kwtext ? p->kwtext : p->words, NULL, FALSE); + return rsc.text; +} + +static void info_menu_item(rdstringc *text, node *n, paragraph *p) +{ + /* + * FIXME: Depending on how we're doing node names in this info + * file, we might want to do + * + * * Node name:: Chapter title + * + * _or_ + * + * * Chapter number: Node name. + * + * + */ + rdaddsc(text, "* "); + rdaddsc(text, n->name); + rdaddsc(text, "::"); + if (p) { + rdaddc(text, ' '); + info_rdaddwc(text, p->words, NULL, FALSE); + } + rdaddc(text, '\n'); +} diff --git a/bk_xhtml.c b/bk_xhtml.c index cad2e8e..5a10137 100644 --- a/bk_xhtml.c +++ b/bk_xhtml.c @@ -913,7 +913,8 @@ static void xhtml_do_index_body(FILE *fp) } static void xhtml_do_index() { - word temp_word = { NULL, NULL, word_Normal, 0, 0, L"Index", { NULL, 0, 0} }; + word temp_word = { NULL, NULL, word_Normal, 0, 0, L"Index", + { NULL, 0, 0}, NULL }; FILE *fp = fopen(conf.index_filename, "w"); if (fp==NULL) diff --git a/doc/.cvsignore b/doc/.cvsignore index c59962b..72e10da 100644 --- a/doc/.cvsignore +++ b/doc/.cvsignore @@ -3,3 +3,4 @@ *.gid *.GID *.log *.1 +*.info *.info-* diff --git a/doc/Makefile b/doc/Makefile index 624bf54..762b1d1 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -7,10 +7,10 @@ HALIBUT = ../build/halibut all: Contents.html halibut.1 Contents.html: $(INPUTS) $(HALIBUT) - $(HALIBUT) --text=halibut.txt --html $(INPUTS) + $(HALIBUT) --text=halibut.txt --html --info=halibut.info $(INPUTS) halibut.1: manpage.but $(HALIBUT) --man=halibut.1 manpage.but clean: - rm -f *.html *.txt *.hlp *.cnt *.1 + rm -f *.html *.txt *.hlp *.cnt *.1 *.info* diff --git a/halibut.h b/halibut.h index 3fc752d..3e600c7 100644 --- a/halibut.h +++ b/halibut.h @@ -127,6 +127,8 @@ struct word_Tag { int breaks; /* can a line break after it? */ wchar_t *text; filepos fpos; + + void *private_data; /* for temp use in backends */ }; enum { /* ORDERING CONSTRAINT: these normal-word types ... */ @@ -252,9 +254,9 @@ char *ustrtoa(wchar_t *s, char *outbuf, int size); wchar_t *ustrfroma(char *s, wchar_t *outbuf, int size); char *utoa_dup(wchar_t *s); wchar_t *ufroma_dup(char *s); -int ustrlen(wchar_t *s); +int ustrlen(wchar_t const *s); wchar_t *uadv(wchar_t *s); -wchar_t *ustrcpy(wchar_t *dest, wchar_t *source); +wchar_t *ustrcpy(wchar_t *dest, wchar_t const *source); wchar_t utolower(wchar_t); int uisalpha(wchar_t); int ustrcmp(wchar_t *lhs, wchar_t *rhs); @@ -305,10 +307,10 @@ struct tagRdstringc { extern const rdstring empty_rdstring; extern const rdstringc empty_rdstringc; void rdadd(rdstring *rs, wchar_t c); -void rdadds(rdstring *rs, wchar_t *p); +void rdadds(rdstring *rs, wchar_t const *p); wchar_t *rdtrim(rdstring *rs); void rdaddc(rdstringc *rs, char c); -void rdaddsc(rdstringc *rs, char *p); +void rdaddsc(rdstringc *rs, char const *p); char *rdtrimc(rdstringc *rs); int compare_wordlists(word *a, word *b); @@ -436,4 +438,10 @@ paragraph *whlp_config_filename(char *filename); void man_backend(paragraph *, keywordlist *, indexdata *); paragraph *man_config_filename(char *filename); +/* + * bk_info.c + */ +void info_backend(paragraph *, keywordlist *, indexdata *); +paragraph *info_config_filename(char *filename); + #endif diff --git a/main.c b/main.c index b509928..ecb2d37 100644 --- a/main.c +++ b/main.c @@ -24,6 +24,7 @@ static const struct backend { {"whlp", whlp_backend, whlp_config_filename, 0x0004}, {"winhelp", whlp_backend, whlp_config_filename, 0x0004}, {"man", man_backend, man_config_filename, 0x0008}, + {"info", info_backend, info_config_filename, 0x0010}, }; int main(int argc, char **argv) { diff --git a/misc.c b/misc.c index a54cc6c..6d5497b 100644 --- a/misc.c +++ b/misc.c @@ -62,7 +62,7 @@ void rdadd(rdstring *rs, wchar_t c) { rs->text[rs->pos++] = c; rs->text[rs->pos] = 0; } -void rdadds(rdstring *rs, wchar_t *p) { +void rdadds(rdstring *rs, wchar_t const *p) { int len = ustrlen(p); if (rs->pos >= rs->size - len) { rs->size = rs->pos + len + 128; @@ -84,7 +84,7 @@ void rdaddc(rdstringc *rs, char c) { rs->text[rs->pos++] = c; rs->text[rs->pos] = 0; } -void rdaddsc(rdstringc *rs, char *p) { +void rdaddsc(rdstringc *rs, char const *p) { int len = strlen(p); if (rs->pos >= rs->size - len) { rs->size = rs->pos + len + 128; diff --git a/ustring.c b/ustring.c index c4af519..51c279b 100644 --- a/ustring.c +++ b/ustring.c @@ -78,7 +78,7 @@ wchar_t *ufroma_dup(char *s) { return buf; } -int ustrlen(wchar_t *s) { +int ustrlen(wchar_t const *s) { int len = 0; while (*s++) len++; return len; @@ -88,7 +88,7 @@ wchar_t *uadv(wchar_t *s) { return s + 1 + ustrlen(s); } -wchar_t *ustrcpy(wchar_t *dest, wchar_t *source) { +wchar_t *ustrcpy(wchar_t *dest, wchar_t const *source) { wchar_t *ret = dest; do { *dest++ = *source; -- 2.11.0