summaryrefslogtreecommitdiff
path: root/bk_text.c
diff options
context:
space:
mode:
authorSimon Tatham <anakin@pobox.com>1999-10-20 20:17:18 +0000
committerSimon Tatham <anakin@pobox.com>1999-10-20 20:17:18 +0000
commit8511f4ae900f48999617bc8384e9c327673e2196 (patch)
treed24d0a7d8cc217bf6d864157c3b08e05ed59868f /bk_text.c
parente44f985bd4f796d4c4b11eb3555436dbaa2d163b (diff)
downloadhalibut-8511f4ae900f48999617bc8384e9c327673e2196.zip
halibut-8511f4ae900f48999617bc8384e9c327673e2196.tar.gz
halibut-8511f4ae900f48999617bc8384e9c327673e2196.tar.bz2
halibut-8511f4ae900f48999617bc8384e9c327673e2196.tar.xz
First backend! Text output now pretty much works.
[originally from svn r240]
Diffstat (limited to 'bk_text.c')
-rw-r--r--bk_text.c444
1 files changed, 444 insertions, 0 deletions
diff --git a/bk_text.c b/bk_text.c
new file mode 100644
index 0000000..786c126
--- /dev/null
+++ b/bk_text.c
@@ -0,0 +1,444 @@
+/*
+ * text backend for Buttress
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "buttress.h"
+
+typedef enum { LEFT, CENTRE } alignment;
+
+typedef struct {
+ int indent;
+ int listindentbefore, listindentafter;
+ int width;
+ alignment titlealign, chapteralign;
+ wchar_t titleunderline, chapterunderline;
+ int include_version_id;
+ int indent_preambles;
+ word bullet;
+} textconfig;
+
+static int text_convert(wchar_t *, char **);
+
+static void text_title(FILE *, word *, word *, alignment, wchar_t, int, int);
+static void text_heading(FILE *, word *, word *, int, int);
+static void text_para(FILE *, word *, char *, word *, int, int, int);
+static void text_codepara(FILE *, word *, int, int);
+static void text_versionid(FILE *, word *);
+
+static textconfig text_configure(paragraph *sourceform) {
+ textconfig ret;
+
+ /*
+ * Non-negotiables.
+ */
+ ret.bullet.next = NULL;
+ ret.bullet.alt = NULL;
+ ret.bullet.type = word_Normal;
+
+ /*
+ * Defaults.
+ */
+ ret.indent = 7;
+ ret.listindentbefore = 1;
+ ret.listindentafter = 3;
+ ret.width = 68;
+ ret.titlealign = CENTRE;
+ ret.titleunderline = L'=';
+ ret.chapteralign = LEFT;
+ ret.chapterunderline = L'-';
+ ret.include_version_id = TRUE;
+ ret.indent_preambles = FALSE;
+ ret.bullet.text = ustrdup(L"-");
+
+ /*
+ * FIXME: must walk the source form gleaning configuration from
+ * \config paragraphs.
+ */
+ IGNORE(sourceform); /* for now */
+
+ return ret;
+}
+
+void text_backend(paragraph *sourceform, keywordlist *keywords, index *idx) {
+ paragraph *p;
+ textconfig conf;
+ word *prefix, *body, *wp;
+ word spaceword;
+ FILE *fp;
+ char *prefixextra;
+ int indentb, indenta;
+
+ IGNORE(keywords); /* we don't happen to need this */
+ IGNORE(idx); /* or this */
+
+ conf = text_configure(sourceform);
+
+ /*
+ * Determine the output file name, and open the output file
+ *
+ * FIXME: want configurable output file names here. For the
+ * moment, we'll just call it `output.txt'.
+ */
+ fp = fopen("output.txt", "w");
+ if (!fp) {
+ error(err_cantopenw, "output.txt");
+ return;
+ }
+
+ /* Do the title */
+ for (p = sourceform; p; p = p->next)
+ if (p->type == para_Title)
+ text_title(fp, NULL, p->words,
+ conf.titlealign, conf.titleunderline,
+ conf.indent, conf.width);
+
+ /* Do the preamble and copyright */
+ for (p = sourceform; p; p = p->next)
+ if (p->type == para_Preamble)
+ text_para(fp, NULL, NULL, p->words,
+ conf.indent_preambles ? conf.indent : 0, 0,
+ conf.width + (conf.indent_preambles ? 0 : conf.indent));
+ for (p = sourceform; p; p = p->next)
+ if (p->type == para_Copyright)
+ text_para(fp, NULL, NULL, p->words,
+ conf.indent_preambles ? conf.indent : 0, 0,
+ conf.width + (conf.indent_preambles ? 0 : conf.indent));
+
+ /* Do the main document */
+ for (p = sourceform; p; p = p->next) switch (p->type) {
+
+ /*
+ * 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_Copyright:
+ case para_Preamble:
+ case para_NoCite:
+ case para_Title:
+ break;
+
+ /*
+ * Chapter titles.
+ */
+ case para_Chapter:
+ case para_Appendix:
+ case para_UnnumberedChapter:
+ text_title(fp, p->kwtext, p->words,
+ conf.chapteralign, conf.chapterunderline,
+ conf.indent, conf.width);
+ break;
+
+ case para_Heading:
+ case para_Subsect:
+ text_heading(fp, p->kwtext2, p->words, conf.indent, conf.width);
+ break;
+
+ case para_Normal:
+ case para_BiblioCited: /* FIXME: put the citation on front */
+ case para_Bullet:
+ case para_NumberedList:
+ if (p->type == para_Bullet) {
+ prefix = &conf.bullet;
+ prefixextra = NULL;
+ indentb = conf.listindentbefore;
+ indenta = conf.listindentafter;
+ } else if (p->type == para_NumberedList) {
+ prefix = p->kwtext;
+ prefixextra = ".";
+ indentb = conf.listindentbefore;
+ indenta = conf.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;
+ }
+ text_para(fp, prefix, prefixextra, body,
+ conf.indent + indentb, indenta, conf.width);
+ if (wp) {
+ wp->next = NULL;
+ free_word_list(body);
+ }
+ break;
+
+ case para_Code:
+ text_codepara(fp, p->words, conf.indent, conf.width);
+ break;
+ }
+
+ /* Do the version ID */
+ if (conf.include_version_id) {
+ for (p = sourceform; p; p = p->next)
+ if (p->type == para_VersionID)
+ text_versionid(fp, p->words);
+ }
+
+ /*
+ * Tidy up
+ */
+ fclose(fp);
+ sfree(conf.bullet.text);
+}
+
+/*
+ * 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 text_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 void text_rdaddwc(rdstringc *rs, word *text, word *end) {
+ char *c;
+
+ for (; text && text != end; text = text->next) switch (text->type) {
+ case word_HyperLink:
+ case word_HyperEnd:
+ case word_UpperXref:
+ case word_LowerXref:
+ case word_XrefEnd:
+ case word_IndexRef:
+ break;
+
+ case word_Normal:
+ case word_Emph:
+ case word_Code:
+ case word_WeakCode:
+ if (text->type == word_Emph)
+ rdaddc(rs, '_'); /* FIXME: configurability */
+ else if (text->type == word_Code)
+ rdaddc(rs, '`'); /* FIXME: configurability */
+ if (text_convert(text->text, &c))
+ rdaddsc(rs, c);
+ else
+ text_rdaddwc(rs, text->alt, NULL);
+ sfree(c);
+ if (text->type == word_Emph)
+ rdaddc(rs, '_'); /* FIXME: configurability */
+ else if (text->type == word_Code)
+ rdaddc(rs, '\''); /* FIXME: configurability */
+ break;
+
+ case word_WhiteSpace:
+ rdaddc(rs, ' ');
+ break;
+ }
+}
+
+static int text_width(word *);
+
+static int text_width_list(word *text) {
+ int w = 0;
+ while (text) {
+ w += text_width(text);
+ text = text->next;
+ }
+ return w;
+}
+
+static int text_width(word *text) {
+ switch (text->type) {
+ case word_HyperLink:
+ case word_HyperEnd:
+ case word_UpperXref:
+ case word_LowerXref:
+ case word_XrefEnd:
+ case word_IndexRef:
+ return 0;
+
+ case word_Normal:
+ case word_Emph:
+ case word_Code:
+ case word_WeakCode:
+ return ((text->type == word_Emph || text->type == word_Code ? 2 : 0) +
+ (text_convert(text->text, NULL) ?
+ ustrlen(text->text) :
+ text_width_list(text->alt)));
+
+ case word_WhiteSpace:
+ return 1;
+ }
+ return 0; /* should never happen */
+}
+
+static void text_title(FILE *fp, word *prefix, word *text,
+ alignment align, wchar_t underline,
+ int indent, int width) {
+ rdstringc t = { 0, 0, NULL };
+ int margin, length;
+
+ if (prefix) {
+ text_rdaddwc(&t, prefix, NULL);
+ rdaddsc(&t, ": ");
+ }
+ text_rdaddwc(&t, text, NULL);
+
+ length = strlen(t.text);
+ if (align == CENTRE) {
+ margin = (indent + width - length)/2;
+ if (margin < 0) margin = 0;
+ } else
+ margin = 0;
+
+ fprintf(fp, "%*s%s\n", margin, "", t.text);
+ if (underline != L'\0') {
+ char *u, uc;
+ wchar_t uw[2];
+ uw[0] = underline; uw[1] = L'\0';
+ text_convert(uw, &u);
+ uc = u[0];
+ sfree(u);
+ fprintf(fp, "%*s", margin, "");
+ while (length--)
+ putc(uc, fp);
+ putc('\n', fp);
+ }
+ putc('\n', fp);
+
+ sfree(t.text);
+}
+
+static void text_heading(FILE *fp, word *prefix, word *text,
+ int indent, int width) {
+ rdstringc t = { 0, 0, NULL };
+ int margin;
+
+ if (prefix) {
+ text_rdaddwc(&t, prefix, NULL);
+ rdaddc(&t, ' ');
+ margin = strlen(t.text);
+ }
+ text_rdaddwc(&t, text, NULL);
+
+ margin = indent - margin;
+ if (margin < 0) margin = 0;
+
+ fprintf(fp, "%*s%s\n\n", margin, "", t.text);
+
+ if (strlen(t.text) > (size_t)width) {
+ /* FIXME: warn */
+ }
+
+ sfree(t.text);
+}
+
+static void text_para(FILE *fp, word *prefix, char *prefixextra, word *text,
+ int indent, int extraindent, int width) {
+ wrappedline *wrapping, *p;
+ rdstringc pfx = { 0, 0, NULL };
+ int e;
+ int firstlinewidth = width;
+
+ if (prefix) {
+ text_rdaddwc(&pfx, prefix, NULL);
+ if (prefixextra)
+ rdaddsc(&pfx, prefixextra);
+ fprintf(fp, "%*s%s", indent, "", pfx.text);
+ e = extraindent - strlen(pfx.text);
+ if (e < 0) {
+ e = 0;
+ firstlinewidth -= e;
+ if (firstlinewidth < 0) {
+ e = indent + extraindent;
+ firstlinewidth = width;
+ fprintf(fp, "\n");
+ }
+ }
+ sfree(pfx.text);
+ } else
+ e = indent + extraindent;
+
+ wrapping = wrap_para(text, firstlinewidth, width, text_width);
+ for (p = wrapping; p; p = p->next) {
+ rdstringc t = { 0, 0, NULL };
+ text_rdaddwc(&t, p->begin, p->end);
+ fprintf(fp, "%*s%s\n", e, "", t.text);
+ e = indent + extraindent;
+ sfree(t.text);
+ }
+ wrap_free(wrapping);
+ putc('\n', fp);
+}
+
+static void text_codepara(FILE *fp, word *text, int indent, int width) {
+ for (; text; text = text->next) if (text->type == word_WeakCode) {
+ char *c;
+ text_convert(text->text, &c);
+ if (strlen(c) > (size_t)width) {
+ /* FIXME: warn */
+ }
+ fprintf(fp, "%*s%s\n", indent, "", c);
+ sfree(c);
+ }
+
+ putc('\n', fp);
+}
+
+static void text_versionid(FILE *fp, word *text) {
+ rdstringc t = { 0, 0, NULL };
+
+ rdaddc(&t, '['); /* FIXME: configurability */
+ text_rdaddwc(&t, text, NULL);
+ rdaddc(&t, ']'); /* FIXME: configurability */
+
+ fprintf(fp, "%s\n\n", t.text);
+ sfree(t.text);
+}