diff options
| author | Simon Tatham <anakin@pobox.com> | 1999-10-20 20:17:18 +0000 |
|---|---|---|
| committer | Simon Tatham <anakin@pobox.com> | 1999-10-20 20:17:18 +0000 |
| commit | 8511f4ae900f48999617bc8384e9c327673e2196 (patch) | |
| tree | d24d0a7d8cc217bf6d864157c3b08e05ed59868f | |
| parent | e44f985bd4f796d4c4b11eb3555436dbaa2d163b (diff) | |
| download | halibut-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]
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | biblio.c | 1 | ||||
| -rw-r--r-- | bk_text.c | 444 | ||||
| -rw-r--r-- | buttress.h | 45 | ||||
| -rw-r--r-- | contents.c | 10 | ||||
| -rw-r--r-- | error.c | 5 | ||||
| -rw-r--r-- | index.c | 20 | ||||
| -rw-r--r-- | input.c | 30 | ||||
| -rw-r--r-- | keywords.c | 6 | ||||
| -rw-r--r-- | main.c | 16 | ||||
| -rw-r--r-- | misc.c | 114 | ||||
| -rw-r--r-- | tree23.c | 2 |
12 files changed, 643 insertions, 51 deletions
@@ -45,6 +45,7 @@ SRC := ../ MODULES := main malloc ustring error help licence version misc tree23 MODULES += input keywords contents index style biblio +MODULES += bk_text OBJECTS := $(addsuffix .o,$(MODULES)) DEPS := $(addsuffix .d,$(MODULES)) @@ -99,6 +99,7 @@ void gen_citations(paragraph *source, keywordlist *kl) { wd->next = NULL; kw->text = wd; } + para->kwtext = kw->text; } } } 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); +} @@ -16,6 +16,9 @@ #define FALSE 0 #endif +/* For suppressing unused-parameter warnings */ +#define IGNORE(x) ( (x) = (x) ) + /* * Structure tags */ @@ -65,6 +68,7 @@ struct paragraph_Tag { word *words; /* list of words in paragraph */ int aux; /* number, in a numbered paragraph */ word *kwtext; /* chapter/section indication */ + word *kwtext2; /* numeric-only form of kwtext */ filepos fpos; }; enum { @@ -140,7 +144,8 @@ enum { err_nestedindex, /* unable to nest `\i' thingys */ err_nosuchkw, /* unresolved cross-reference */ err_multiBR, /* multiple \BRs on same keyword */ - err_nosuchidxtag /* \IM on unknown index tag (warning) */ + err_nosuchidxtag, /* \IM on unknown index tag (warning) */ + err_cantopenw /* can't open output file for write */ }; /* @@ -205,8 +210,36 @@ stack stk_new(void); void stk_free(stack); void stk_push(stack, void *); void *stk_pop(stack); + +typedef struct tagRdstring rdstring; +struct tagRdstring { + int pos, size; + wchar_t *text; +}; +typedef struct tagRdstringc rdstringc; +struct tagRdstringc { + int pos, size; + char *text; +}; +void rdadd(rdstring *rs, wchar_t c); +void rdadds(rdstring *rs, wchar_t *p); +wchar_t *rdtrim(rdstring *rs); +void rdaddc(rdstringc *rs, char c); +void rdaddsc(rdstringc *rs, char *p); +char *rdtrimc(rdstringc *rs); + int compare_wordlists(word *a, word *b); +typedef struct tagWrappedLine wrappedline; +struct tagWrappedLine { + wrappedline *next; + word *begin, *end; /* first & last words of line */ + int nspaces; /* number of whitespaces in line */ + int shortfall; /* how much shorter than max width */ +}; +wrappedline *wrap_para(word *, int, int, int (*)(word *)); +void wrap_free(wrappedline *); + /* * input.c */ @@ -243,12 +276,13 @@ void cleanup_index(index *); * takes responsibility for arg 2 */ void index_merge(index *, int is_explicit, wchar_t *, word *); void build_index(index *); +void index_debug(index *); /* * contents.c */ numberstate *number_init(void); -word *number_mktext(numberstate *, int, int, int); +word *number_mktext(numberstate *, int, int, int, word **); void number_free(numberstate *); /* @@ -275,6 +309,11 @@ void freetree23(tree23 *); void *add23(tree23 *, void *, int (*cmp)(void *, void *)); void *find23(tree23 *, void *, int (*cmp)(void *, void *)); void *first23(tree23 *, enum23 *); -void *next23(tree23 *, enum23 *); +void *next23(enum23 *); + +/* + * bk_text.c + */ +void text_backend(paragraph *, keywordlist *, index *); #endif @@ -89,8 +89,10 @@ static void doanumber(word ***wret, int num) { dotext(wret, p); } -word *number_mktext(numberstate *state, int para, int aux, int prev) { +word *number_mktext(numberstate *state, int para, int aux, int prev, + word **auxret) { word *ret = NULL; + word **ret2 = &ret; word **pret = &ret; int i, level; @@ -101,6 +103,7 @@ word *number_mktext(numberstate *state, int para, int aux, int prev) { state->sectionlevels[i] = 0; dotext(&pret, L"Chapter"); dospace(&pret); + ret2 = pret; donumber(&pret, state->chapternum); state->ischapter = 1; break; @@ -117,6 +120,7 @@ word *number_mktext(numberstate *state, int para, int aux, int prev) { state->sectionlevels[i] = 0; dotext(&pret, L"Section"); dospace(&pret); + ret2 = pret; if (state->ischapter) donumber(&pret, state->chapternum); else @@ -134,10 +138,12 @@ word *number_mktext(numberstate *state, int para, int aux, int prev) { state->sectionlevels[i] = 0; dotext(&pret, L"Appendix"); dospace(&pret); + ret2 = pret; doanumber(&pret, state->appendixnum); state->ischapter = 0; break; case para_NumberedList: + ret2 = pret; if (prev != para_NumberedList) state->listitem = 0; state->listitem++; @@ -145,5 +151,7 @@ word *number_mktext(numberstate *state, int para, int aux, int prev) { break; } + if (auxret) + *auxret = *ret2; return ret; } @@ -142,6 +142,11 @@ static void do_error(int code, va_list ap) { flags = 0; /* FIXME: need to get a filepos to here somehow */ break; + case err_cantopenw: + sp = va_arg(ap, char *); + sprintf(error, "unable to open output file `%.200s'", sp); + flags = PREFIX; + break; } if (flags & PREFIX) @@ -128,12 +128,13 @@ void index_merge(index *idx, int is_explicit, wchar_t *tags, word *text) { * entries. */ void build_index(index *i) { - indextag *t, **ta; + indextag *t; + word **ta; enum23 e; int j; for (t = (indextag *)first23(i->tags, &e); t; - t = (indextag *)next23(i->tags, &e)) { + t = (indextag *)next23(&e)) { if (t->implicit_text) { t->nrefs = 1; ta = &t->implicit_text; @@ -156,10 +157,9 @@ void cleanup_index(index *i) { indextag *t; indexentry *ent; enum23 e; - int j; for (t = (indextag *)first23(i->tags, &e); t; - t = (indextag *)next23(i->tags, &e)) { + t = (indextag *)next23(&e)) { sfree(t->name); free_word_list(t->implicit_text); sfree(t->explicit_texts); @@ -168,30 +168,29 @@ void cleanup_index(index *i) { } freetree23(i->tags); for (ent = (indexentry *)first23(i->entries, &e); ent; - ent = (indexentry *)next23(i->entries, &e)) { + ent = (indexentry *)next23(&e)) { sfree(ent); } freetree23(i->entries); sfree(i); } +static void dbg_prtwordlist(int level, word *w); +static void dbg_prtmerge(int is_explicit, wchar_t *tag, word *text); + void index_debug(index *i) { indextag *t; enum23 e; int j; for (t = (indextag *)first23(i->tags, &e); t; - t = (indextag *)next23(i->tags, &e)) { + t = (indextag *)next23(&e)) { if (t->implicit_text) dbg_prtmerge(0, t->name, t->implicit_text); for (j = 0; j < t->nexplicit; j++) dbg_prtmerge(1, t->name, t->explicit_texts[j]); } } -#define DEBUG - -#ifdef DEBUG -static void dbg_prtwordlist(int level, word *w); static void dbg_prtmerge(int is_explicit, wchar_t *tag, word *text) { printf("\\IM: %splicit: \"", is_explicit ? "ex" : "im"); @@ -221,4 +220,3 @@ static void dbg_prtwordlist(int level, word *w) { printf("\n"); } } -#endif @@ -76,36 +76,6 @@ static int get(input *in) { } /* - * Small routines to amalgamate a string from an input source. - */ -typedef struct tagRdstring rdstring; -struct tagRdstring { - int pos, size; - wchar_t *text; -}; -static void rdadd(rdstring *rs, wchar_t c) { - if (rs->pos >= rs->size-1) { - rs->size = rs->pos + 128; - rs->text = resize(rs->text, rs->size); - } - rs->text[rs->pos++] = c; - rs->text[rs->pos] = 0; -} -static void rdadds(rdstring *rs, wchar_t *p) { - int len = ustrlen(p); - if (rs->pos >= rs->size - len) { - rs->size = rs->pos + len + 128; - rs->text = resize(rs->text, rs->size); - } - ustrcpy(rs->text + rs->pos, p); - rs->pos += len; -} -static wchar_t *rdtrim(rdstring *rs) { - rs->text = resize(rs->text, rs->pos + 1); - return rs->text; -} - -/* * Lexical analysis of source files. */ typedef struct token_Tag token; @@ -100,7 +100,7 @@ keywordlist *get_keywords(paragraph *source) { * Number the chapter / section / list-item / whatever. */ source->kwtext = number_mktext(n, source->type, source->aux, - prevpara); + prevpara, &source->kwtext2); prevpara = source->type; if (source->keyword && *source->keyword) { @@ -161,7 +161,9 @@ void subst_keywords(paragraph *source, keywordlist *kl) { } else subst = dup_word_list(kw->text); - if (subst && ptr->type == word_LowerXref) + if (subst && ptr->type == word_LowerXref && + kw->para->type != para_Biblio && + kw->para->type != para_BiblioCited) ustrlow(subst->text); close = mknew(word); @@ -17,6 +17,7 @@ int main(int argc, char **argv) { int nogo; int errs; int reportcols; + int debug; /* * Set up initial (default) parameters. @@ -26,6 +27,7 @@ int main(int argc, char **argv) { nfiles = 0; nogo = errs = FALSE; reportcols = 0; + debug = 0; if (argc == 1) { usage(); @@ -85,6 +87,7 @@ int main(int argc, char **argv) { case 'V': case 'L': case 'P': + case 'd': /* * Option requiring no parameter. */ @@ -104,6 +107,9 @@ int main(int argc, char **argv) { case 'P': reportcols = 1; break; + case 'd': + debug = TRUE; + break; } break; case 'o': @@ -193,10 +199,14 @@ int main(int argc, char **argv) { index_merge(idx, TRUE, p->keyword, p->words); build_index(idx); - index_debug(idx); - dbg_prtkws(keywords); - dbg_prtsource(sourceform); + if (debug) { + index_debug(idx); + dbg_prtkws(keywords); + dbg_prtsource(sourceform); + } + + text_backend(sourceform, keywords, idx); free_para_list(sourceform); free_keywords(keywords); @@ -41,6 +41,53 @@ void *stk_pop(stack s) { return NULL; } +/* + * Small routines to amalgamate a string from an input source. + */ +void rdadd(rdstring *rs, wchar_t c) { + if (rs->pos >= rs->size-1) { + rs->size = rs->pos + 128; + rs->text = resize(rs->text, rs->size); + } + rs->text[rs->pos++] = c; + rs->text[rs->pos] = 0; +} +void rdadds(rdstring *rs, wchar_t *p) { + int len = ustrlen(p); + if (rs->pos >= rs->size - len) { + rs->size = rs->pos + len + 128; + rs->text = resize(rs->text, rs->size); + } + ustrcpy(rs->text + rs->pos, p); + rs->pos += len; +} +wchar_t *rdtrim(rdstring *rs) { + rs->text = resize(rs->text, rs->pos + 1); + return rs->text; +} + +void rdaddc(rdstringc *rs, char c) { + if (rs->pos >= rs->size-1) { + rs->size = rs->pos + 128; + rs->text = resize(rs->text, rs->size); + } + rs->text[rs->pos++] = c; + rs->text[rs->pos] = 0; +} +void rdaddsc(rdstringc *rs, char *p) { + int len = strlen(p); + if (rs->pos >= rs->size - len) { + rs->size = rs->pos + len + 128; + rs->text = resize(rs->text, rs->size); + } + strcpy(rs->text + rs->pos, p); + rs->pos += len; +} +char *rdtrimc(rdstringc *rs) { + rs->text = resize(rs->text, rs->pos + 1); + return rs->text; +} + int compare_wordlists(word *a, word *b) { int t; while (a && b) { @@ -84,3 +131,70 @@ int compare_wordlists(word *a, word *b) { else return 0; } + +wrappedline *wrap_para(word *text, int width, int subsequentwidth, + int (*widthfn)(word *)) { + wrappedline *head = NULL, **ptr = &head; + word *spc; + int nspc; + int thiswidth, lastgood; + + while (text) { + wrappedline *w = mknew(wrappedline); + *ptr = w; + ptr = &w->next; + w->next = NULL; + + w->begin = text; + spc = NULL; + nspc = 0; + thiswidth = lastgood = 0; + while (text) { + thiswidth += widthfn(text); + if (text->next && text->next->type == word_WhiteSpace) { + if (thiswidth > width) + break; + spc = text->next; + lastgood = thiswidth; + nspc++; + } + text = text->next; + } + /* + * We've exited this loop on one of three conditions. spc + * might be non-NULL and we've overflowed: we have broken + * the paragraph there. spc might be NULL and text might be + * NULL too: we've reached the end of the paragraph and + * should output the last line. Or text might be non-NULL + * and spc might be NULL: we've got an anomalously long + * line with no spaces we could have broken at. Output it + * anyway as the best we can do. + */ + if (spc && thiswidth > width) { + w->end = spc; + text = spc->next; + w->nspaces = nspc-1; + w->shortfall = width - lastgood; + } else if (!text) { + w->end = NULL; /* no end marker needed */ + w->nspaces = nspc; + w->shortfall = width - thiswidth; + } else { + w->end = text; + text = text->next; + w->nspaces = 0; + w->shortfall = width - thiswidth; + } + width = subsequentwidth; + } + + return head; +} + +void wrap_free(wrappedline *w) { + while (w) { + wrappedline *t = w->next; + sfree(w); + w = t; + } +} @@ -238,7 +238,7 @@ void *first23(tree23 *t, enum23 *e) { return n->elems[0]; } -void *next23(tree23 *t, enum23 *e) { +void *next23(enum23 *e) { node23 *n = (node23 *)e->node; int pos = e->posn; |