Add an error check for correct formatting in Deflate uncompressed
[sgt/halibut] / bk_text.c
CommitLineData
d7482997 1/*
2 * text backend for Halibut
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <assert.h>
8#include "halibut.h"
9
10typedef enum { LEFT, LEFTPLUS, CENTRE } alignment;
11typedef struct {
12 alignment align;
6e674706 13 int number_at_all, just_numbers;
db662ca1 14 wchar_t *underline;
63223c78 15 wchar_t *number_suffix;
d7482997 16} alignstruct;
17
18typedef struct {
19 int indent, indent_code;
20 int listindentbefore, listindentafter;
21 int width;
22 alignstruct atitle, achapter, *asect;
23 int nasect;
24 int include_version_id;
25 int indent_preambles;
2ac8ceac 26 int charset;
d7482997 27 word bullet;
db662ca1 28 wchar_t *lquote, *rquote, *rule;
50d6b4bd 29 char *filename;
db662ca1 30 wchar_t *listsuffix, *startemph, *endemph;
d7482997 31} textconfig;
32
2ac8ceac 33typedef struct {
34 FILE *fp;
35 int charset;
36 charset_state state;
37} textfile;
38
39static void text_heading(textfile *, word *, word *, word *, alignstruct,
db662ca1 40 int, int, textconfig *);
41static void text_rule(textfile *, int, int, textconfig *);
42static void text_para(textfile *, word *, wchar_t *, word *, int, int, int,
43 textconfig *);
2ac8ceac 44static void text_codepara(textfile *, word *, int, int);
db662ca1 45static void text_versionid(textfile *, word *, textconfig *);
d7482997 46
2ac8ceac 47static void text_output(textfile *, const wchar_t *);
48static void text_output_many(textfile *, int, wchar_t);
d7482997 49
50static alignment utoalign(wchar_t *p) {
51 if (!ustricmp(p, L"centre") || !ustricmp(p, L"center"))
52 return CENTRE;
53 if (!ustricmp(p, L"leftplus"))
54 return LEFTPLUS;
55 return LEFT;
56}
57
58static textconfig text_configure(paragraph *source) {
59 textconfig ret;
db662ca1 60 paragraph *p;
61 int n;
d7482997 62
63 /*
64 * Non-negotiables.
65 */
66 ret.bullet.next = NULL;
67 ret.bullet.alt = NULL;
68 ret.bullet.type = word_Normal;
69 ret.atitle.just_numbers = FALSE; /* ignored */
6e674706 70 ret.atitle.number_at_all = TRUE; /* ignored */
d7482997 71
72 /*
73 * Defaults.
74 */
75 ret.indent = 7;
76 ret.indent_code = 2;
77 ret.listindentbefore = 1;
78 ret.listindentafter = 3;
79 ret.width = 68;
80 ret.atitle.align = CENTRE;
db662ca1 81 ret.atitle.underline = L"\x2550\0=\0\0";
d7482997 82 ret.achapter.align = LEFT;
83 ret.achapter.just_numbers = FALSE;
6e674706 84 ret.achapter.number_at_all = TRUE;
e5e6bf9d 85 ret.achapter.number_suffix = L": ";
db662ca1 86 ret.achapter.underline = L"\x203E\0-\0\0";
d7482997 87 ret.nasect = 1;
f1530049 88 ret.asect = snewn(ret.nasect, alignstruct);
d7482997 89 ret.asect[0].align = LEFTPLUS;
90 ret.asect[0].just_numbers = TRUE;
6e674706 91 ret.asect[0].number_at_all = TRUE;
e5e6bf9d 92 ret.asect[0].number_suffix = L" ";
db662ca1 93 ret.asect[0].underline = L"\0";
d7482997 94 ret.include_version_id = TRUE;
95 ret.indent_preambles = FALSE;
db662ca1 96 ret.bullet.text = L"\x2022\0-\0\0";
97 ret.rule = L"\x2500\0-\0\0";
50d6b4bd 98 ret.filename = dupstr("output.txt");
3e030063 99 ret.startemph = L"_\0_\0\0";
100 ret.endemph = uadv(ret.startemph);
db662ca1 101 ret.listsuffix = L".";
2ac8ceac 102 ret.charset = CS_ASCII;
db662ca1 103 /*
104 * Default quote characters are Unicode matched single quotes,
105 * falling back to the TeXlike `'.
106 */
107 ret.lquote = L"\x2018\0\x2019\0`\0'\0\0";
108 ret.rquote = uadv(ret.lquote);
d7482997 109
db662ca1 110 /*
111 * Two-pass configuration so that we can pick up global config
112 * (e.g. `quotes') before having it overridden by specific
113 * config (`text-quotes'), irrespective of the order in which
114 * they occur.
115 */
116 for (p = source; p; p = p->next) {
117 if (p->type == para_Config) {
118 if (!ustricmp(p->keyword, L"quotes")) {
119 if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
120 ret.lquote = uadv(p->keyword);
121 ret.rquote = uadv(ret.lquote);
122 }
123 }
124 }
125 }
126
127 for (p = source; p; p = p->next) {
128 if (p->type == para_Config) {
129 if (!ustricmp(p->keyword, L"text-indent")) {
130 ret.indent = utoi(uadv(p->keyword));
131 } else if (!ustricmp(p->keyword, L"text-charset")) {
0960a3d8 132 ret.charset = charset_from_ustr(&p->fpos, uadv(p->keyword));
db662ca1 133 } else if (!ustricmp(p->keyword, L"text-filename")) {
50d6b4bd 134 sfree(ret.filename);
db662ca1 135 ret.filename = dupstr(adv(p->origkeyword));
136 } else if (!ustricmp(p->keyword, L"text-indent-code")) {
137 ret.indent_code = utoi(uadv(p->keyword));
138 } else if (!ustricmp(p->keyword, L"text-width")) {
139 ret.width = utoi(uadv(p->keyword));
140 } else if (!ustricmp(p->keyword, L"text-list-indent")) {
141 ret.listindentbefore = utoi(uadv(p->keyword));
142 } else if (!ustricmp(p->keyword, L"text-listitem-indent")) {
143 ret.listindentafter = utoi(uadv(p->keyword));
144 } else if (!ustricmp(p->keyword, L"text-chapter-align")) {
145 ret.achapter.align = utoalign(uadv(p->keyword));
146 } else if (!ustricmp(p->keyword, L"text-chapter-underline")) {
147 ret.achapter.underline = uadv(p->keyword);
148 } else if (!ustricmp(p->keyword, L"text-chapter-numeric")) {
149 ret.achapter.just_numbers = utob(uadv(p->keyword));
6e674706 150 } else if (!ustricmp(p->keyword, L"text-chapter-shownumber")) {
151 ret.achapter.number_at_all = utob(uadv(p->keyword));
db662ca1 152 } else if (!ustricmp(p->keyword, L"text-chapter-suffix")) {
153 ret.achapter.number_suffix = uadv(p->keyword);
154 } else if (!ustricmp(p->keyword, L"text-section-align")) {
155 wchar_t *q = uadv(p->keyword);
d7482997 156 int n = 0;
db662ca1 157 if (uisdigit(*q)) {
158 n = utoi(q);
159 q = uadv(q);
d7482997 160 }
161 if (n >= ret.nasect) {
162 int i;
f1530049 163 ret.asect = sresize(ret.asect, n+1, alignstruct);
d7482997 164 for (i = ret.nasect; i <= n; i++)
165 ret.asect[i] = ret.asect[ret.nasect-1];
166 ret.nasect = n+1;
167 }
db662ca1 168 ret.asect[n].align = utoalign(q);
169 } else if (!ustricmp(p->keyword, L"text-section-underline")) {
170 wchar_t *q = uadv(p->keyword);
d7482997 171 int n = 0;
db662ca1 172 if (uisdigit(*q)) {
173 n = utoi(q);
174 q = uadv(q);
d7482997 175 }
176 if (n >= ret.nasect) {
177 int i;
f1530049 178 ret.asect = sresize(ret.asect, n+1, alignstruct);
d7482997 179 for (i = ret.nasect; i <= n; i++)
180 ret.asect[i] = ret.asect[ret.nasect-1];
181 ret.nasect = n+1;
182 }
db662ca1 183 ret.asect[n].underline = q;
184 } else if (!ustricmp(p->keyword, L"text-section-numeric")) {
185 wchar_t *q = uadv(p->keyword);
d7482997 186 int n = 0;
db662ca1 187 if (uisdigit(*q)) {
188 n = utoi(q);
189 q = uadv(q);
d7482997 190 }
191 if (n >= ret.nasect) {
192 int i;
f1530049 193 ret.asect = sresize(ret.asect, n+1, alignstruct);
d7482997 194 for (i = ret.nasect; i <= n; i++)
195 ret.asect[i] = ret.asect[ret.nasect-1];
196 ret.nasect = n+1;
197 }
db662ca1 198 ret.asect[n].just_numbers = utob(q);
6e674706 199 } else if (!ustricmp(p->keyword, L"text-section-shownumber")) {
200 wchar_t *q = uadv(p->keyword);
201 int n = 0;
202 if (uisdigit(*q)) {
203 n = utoi(q);
204 q = uadv(q);
205 }
206 if (n >= ret.nasect) {
207 int i;
208 ret.asect = sresize(ret.asect, n+1, alignstruct);
209 for (i = ret.nasect; i <= n; i++)
210 ret.asect[i] = ret.asect[ret.nasect-1];
211 ret.nasect = n+1;
212 }
213 ret.asect[n].number_at_all = utob(q);
db662ca1 214 } else if (!ustricmp(p->keyword, L"text-section-suffix")) {
215 wchar_t *q = uadv(p->keyword);
63223c78 216 int n = 0;
db662ca1 217 if (uisdigit(*q)) {
218 n = utoi(q);
219 q = uadv(q);
63223c78 220 }
221 if (n >= ret.nasect) {
222 int i;
f1530049 223 ret.asect = sresize(ret.asect, n+1, alignstruct);
e5e6bf9d 224 for (i = ret.nasect; i <= n; i++) {
63223c78 225 ret.asect[i] = ret.asect[ret.nasect-1];
e5e6bf9d 226 }
63223c78 227 ret.nasect = n+1;
228 }
db662ca1 229 ret.asect[n].number_suffix = q;
230 } else if (!ustricmp(p->keyword, L"text-title-align")) {
231 ret.atitle.align = utoalign(uadv(p->keyword));
232 } else if (!ustricmp(p->keyword, L"text-title-underline")) {
233 ret.atitle.underline = uadv(p->keyword);
234 } else if (!ustricmp(p->keyword, L"text-versionid")) {
235 ret.include_version_id = utob(uadv(p->keyword));
236 } else if (!ustricmp(p->keyword, L"text-indent-preamble")) {
237 ret.indent_preambles = utob(uadv(p->keyword));
238 } else if (!ustricmp(p->keyword, L"text-bullet")) {
239 ret.bullet.text = uadv(p->keyword);
240 } else if (!ustricmp(p->keyword, L"text-rule")) {
241 ret.rule = uadv(p->keyword);
242 } else if (!ustricmp(p->keyword, L"text-list-suffix")) {
243 ret.listsuffix = uadv(p->keyword);
244 } else if (!ustricmp(p->keyword, L"text-emphasis")) {
245 if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
246 ret.startemph = uadv(p->keyword);
247 ret.endemph = uadv(ret.startemph);
248 }
249 } else if (!ustricmp(p->keyword, L"text-quotes")) {
250 if (*uadv(p->keyword) && *uadv(uadv(p->keyword))) {
251 ret.lquote = uadv(p->keyword);
252 ret.rquote = uadv(ret.lquote);
253 }
d7482997 254 }
255 }
256 }
257
db662ca1 258 /*
259 * Now process fallbacks on quote characters, underlines, the
260 * rule character, the emphasis characters, and bullets.
261 */
262 while (*uadv(ret.rquote) && *uadv(uadv(ret.rquote)) &&
263 (!cvt_ok(ret.charset, ret.lquote) ||
264 !cvt_ok(ret.charset, ret.rquote))) {
265 ret.lquote = uadv(ret.rquote);
266 ret.rquote = uadv(ret.lquote);
267 }
268
269 while (*uadv(ret.endemph) && *uadv(uadv(ret.endemph)) &&
270 (!cvt_ok(ret.charset, ret.startemph) ||
271 !cvt_ok(ret.charset, ret.endemph))) {
272 ret.startemph = uadv(ret.endemph);
273 ret.endemph = uadv(ret.startemph);
274 }
275
276 while (*ret.atitle.underline && *uadv(ret.atitle.underline) &&
277 !cvt_ok(ret.charset, ret.atitle.underline))
278 ret.atitle.underline = uadv(ret.atitle.underline);
279
280 while (*ret.achapter.underline && *uadv(ret.achapter.underline) &&
281 !cvt_ok(ret.charset, ret.achapter.underline))
282 ret.achapter.underline = uadv(ret.achapter.underline);
283
284 for (n = 0; n < ret.nasect; n++) {
285 while (*ret.asect[n].underline && *uadv(ret.asect[n].underline) &&
286 !cvt_ok(ret.charset, ret.asect[n].underline))
287 ret.asect[n].underline = uadv(ret.asect[n].underline);
288 }
289
290 while (*ret.bullet.text && *uadv(ret.bullet.text) &&
291 !cvt_ok(ret.charset, ret.bullet.text))
292 ret.bullet.text = uadv(ret.bullet.text);
293
294 while (*ret.rule && *uadv(ret.rule) &&
295 !cvt_ok(ret.charset, ret.rule))
296 ret.rule = uadv(ret.rule);
297
d7482997 298 return ret;
299}
300
ba9c1487 301paragraph *text_config_filename(char *filename)
302{
e4ea58f8 303 return cmdline_cfg_simple("text-filename", filename, NULL);
ba9c1487 304}
305
d7482997 306void text_backend(paragraph *sourceform, keywordlist *keywords,
43341922 307 indexdata *idx, void *unused) {
d7482997 308 paragraph *p;
309 textconfig conf;
310 word *prefix, *body, *wp;
311 word spaceword;
2ac8ceac 312 textfile tf;
313 wchar_t *prefixextra;
04c08204 314 int nesting, nestbase, nestindent;
d7482997 315 int indentb, indenta;
316
43341922 317 IGNORE(unused);
d7482997 318 IGNORE(keywords); /* we don't happen to need this */
319 IGNORE(idx); /* or this */
320
321 conf = text_configure(sourceform);
322
323 /*
50d6b4bd 324 * Open the output file.
d7482997 325 */
0041ffdb 326 if (!strcmp(conf.filename, "-"))
327 tf.fp = stdout;
328 else
329 tf.fp = fopen(conf.filename, "w");
2ac8ceac 330 if (!tf.fp) {
50d6b4bd 331 error(err_cantopenw, conf.filename);
d7482997 332 return;
333 }
2ac8ceac 334 tf.charset = conf.charset;
335 tf.state = charset_init_state;
d7482997 336
337 /* Do the title */
338 for (p = sourceform; p; p = p->next)
339 if (p->type == para_Title)
2ac8ceac 340 text_heading(&tf, NULL, NULL, p->words,
db662ca1 341 conf.atitle, conf.indent, conf.width, &conf);
d7482997 342
7136a6c7 343 nestindent = conf.listindentbefore + conf.listindentafter;
04c08204 344 nestbase = (conf.indent_preambles ? 0 : -conf.indent);
345 nesting = nestbase;
7136a6c7 346
d7482997 347 /* Do the main document */
348 for (p = sourceform; p; p = p->next) switch (p->type) {
349
2614b01d 350 case para_QuotePush:
351 nesting += 2;
352 break;
353 case para_QuotePop:
354 nesting -= 2;
355 assert(nesting >= 0);
356 break;
357
7136a6c7 358 case para_LcontPush:
2614b01d 359 nesting += nestindent;
7136a6c7 360 break;
361 case para_LcontPop:
2614b01d 362 nesting -= nestindent;
04c08204 363 assert(nesting >= nestbase);
7136a6c7 364 break;
365
d7482997 366 /*
367 * Things we ignore because we've already processed them or
368 * aren't going to touch them in this pass.
369 */
370 case para_IM:
371 case para_BR:
372 case para_Biblio: /* only touch BiblioCited */
373 case para_VersionID:
d7482997 374 case para_NoCite:
375 case para_Title:
376 break;
377
378 /*
379 * Chapter titles.
380 */
381 case para_Chapter:
382 case para_Appendix:
383 case para_UnnumberedChapter:
2ac8ceac 384 text_heading(&tf, p->kwtext, p->kwtext2, p->words,
db662ca1 385 conf.achapter, conf.indent, conf.width, &conf);
8902e0ed 386 nesting = 0;
d7482997 387 break;
388
389 case para_Heading:
390 case para_Subsect:
2ac8ceac 391 text_heading(&tf, p->kwtext, p->kwtext2, p->words,
d7482997 392 conf.asect[p->aux>=conf.nasect ? conf.nasect-1 : p->aux],
db662ca1 393 conf.indent, conf.width, &conf);
d7482997 394 break;
395
396 case para_Rule:
db662ca1 397 text_rule(&tf, conf.indent + nesting, conf.width - nesting, &conf);
d7482997 398 break;
399
400 case para_Normal:
9057a0a8 401 case para_Copyright:
7136a6c7 402 case para_DescribedThing:
403 case para_Description:
d7482997 404 case para_BiblioCited:
405 case para_Bullet:
406 case para_NumberedList:
407 if (p->type == para_Bullet) {
408 prefix = &conf.bullet;
409 prefixextra = NULL;
410 indentb = conf.listindentbefore;
411 indenta = conf.listindentafter;
412 } else if (p->type == para_NumberedList) {
413 prefix = p->kwtext;
db662ca1 414 prefixextra = conf.listsuffix;
d7482997 415 indentb = conf.listindentbefore;
416 indenta = conf.listindentafter;
7136a6c7 417 } else if (p->type == para_Description) {
418 prefix = NULL;
419 prefixextra = NULL;
420 indentb = conf.listindentbefore;
421 indenta = conf.listindentafter;
d7482997 422 } else {
423 prefix = NULL;
424 prefixextra = NULL;
425 indentb = indenta = 0;
426 }
427 if (p->type == para_BiblioCited) {
428 body = dup_word_list(p->kwtext);
429 for (wp = body; wp->next; wp = wp->next);
430 wp->next = &spaceword;
431 spaceword.next = p->words;
432 spaceword.alt = NULL;
433 spaceword.type = word_WhiteSpace;
434 spaceword.text = NULL;
435 } else {
436 wp = NULL;
437 body = p->words;
438 }
2ac8ceac 439 text_para(&tf, prefix, prefixextra, body,
2614b01d 440 conf.indent + nesting + indentb, indenta,
db662ca1 441 conf.width - nesting - indentb - indenta, &conf);
d7482997 442 if (wp) {
443 wp->next = NULL;
444 free_word_list(body);
445 }
446 break;
447
448 case para_Code:
2ac8ceac 449 text_codepara(&tf, p->words,
2614b01d 450 conf.indent + nesting + conf.indent_code,
451 conf.width - nesting - 2 * conf.indent_code);
d7482997 452 break;
453 }
454
455 /* Do the version ID */
456 if (conf.include_version_id) {
457 for (p = sourceform; p; p = p->next)
458 if (p->type == para_VersionID)
db662ca1 459 text_versionid(&tf, p->words, &conf);
d7482997 460 }
461
462 /*
463 * Tidy up
464 */
2ac8ceac 465 text_output(&tf, NULL); /* end charset conversion */
0041ffdb 466 if (tf.fp != stdout)
467 fclose(tf.fp);
e5e6bf9d 468 sfree(conf.asect);
50d6b4bd 469 sfree(conf.filename);
d7482997 470}
471
2ac8ceac 472static void text_output(textfile *tf, const wchar_t *s)
473{
474 char buf[256];
475 int ret, len;
476 const wchar_t **sp;
477
478 if (!s) {
479 sp = NULL;
480 len = 1;
481 } else {
482 sp = &s;
483 len = ustrlen(s);
484 }
485
486 while (len > 0) {
487 ret = charset_from_unicode(sp, &len, buf, lenof(buf),
488 tf->charset, &tf->state, NULL);
489 if (!sp)
490 len = 0;
491 fwrite(buf, 1, ret, tf->fp);
d7482997 492 }
d7482997 493}
494
2ac8ceac 495static void text_output_many(textfile *tf, int n, wchar_t c)
496{
497 wchar_t s[2];
498 s[0] = c;
499 s[1] = L'\0';
500 while (n--)
501 text_output(tf, s);
502}
d7482997 503
db662ca1 504static void text_rdaddw(rdstring *rs, word *text, word *end, textconfig *cfg) {
d7482997 505 for (; text && text != end; text = text->next) switch (text->type) {
506 case word_HyperLink:
507 case word_HyperEnd:
508 case word_UpperXref:
509 case word_LowerXref:
510 case word_XrefEnd:
511 case word_IndexRef:
512 break;
513
514 case word_Normal:
515 case word_Emph:
516 case word_Code:
517 case word_WeakCode:
518 case word_WhiteSpace:
519 case word_EmphSpace:
520 case word_CodeSpace:
521 case word_WkCodeSpace:
522 case word_Quote:
523 case word_EmphQuote:
524 case word_CodeQuote:
525 case word_WkCodeQuote:
526 assert(text->type != word_CodeQuote &&
527 text->type != word_WkCodeQuote);
528 if (towordstyle(text->type) == word_Emph &&
529 (attraux(text->aux) == attr_First ||
530 attraux(text->aux) == attr_Only))
db662ca1 531 rdadds(rs, cfg->startemph);
d7482997 532 else if (towordstyle(text->type) == word_Code &&
533 (attraux(text->aux) == attr_First ||
534 attraux(text->aux) == attr_Only))
db662ca1 535 rdadds(rs, cfg->lquote);
d7482997 536 if (removeattr(text->type) == word_Normal) {
db662ca1 537 if (cvt_ok(cfg->charset, text->text) || !text->alt)
2ac8ceac 538 rdadds(rs, text->text);
d7482997 539 else
db662ca1 540 text_rdaddw(rs, text->alt, NULL, cfg);
d7482997 541 } else if (removeattr(text->type) == word_WhiteSpace) {
2ac8ceac 542 rdadd(rs, L' ');
d7482997 543 } else if (removeattr(text->type) == word_Quote) {
db662ca1 544 rdadds(rs, quoteaux(text->aux) == quote_Open ?
545 cfg->lquote : cfg->rquote);
d7482997 546 }
547 if (towordstyle(text->type) == word_Emph &&
548 (attraux(text->aux) == attr_Last ||
549 attraux(text->aux) == attr_Only))
db662ca1 550 rdadds(rs, cfg->endemph);
d7482997 551 else if (towordstyle(text->type) == word_Code &&
552 (attraux(text->aux) == attr_Last ||
553 attraux(text->aux) == attr_Only))
db662ca1 554 rdadds(rs, cfg->rquote);
d7482997 555 break;
556 }
557}
558
43341922 559static int text_width(void *, word *);
d7482997 560
43341922 561static int text_width_list(void *ctx, word *text) {
d7482997 562 int w = 0;
563 while (text) {
43341922 564 w += text_width(ctx, text);
d7482997 565 text = text->next;
566 }
567 return w;
568}
569
43341922 570static int text_width(void *ctx, word *text) {
db662ca1 571 textconfig *cfg = (textconfig *)ctx;
572 int wid;
573 int attr;
43341922 574
d7482997 575 switch (text->type) {
576 case word_HyperLink:
577 case word_HyperEnd:
578 case word_UpperXref:
579 case word_LowerXref:
580 case word_XrefEnd:
581 case word_IndexRef:
582 return 0;
db662ca1 583 }
584
585 assert(text->type < word_internal_endattrs);
586
587 wid = 0;
588 attr = towordstyle(text->type);
589 if (attr == word_Emph || attr == word_Code) {
590 if (attraux(text->aux) == attr_Only ||
591 attraux(text->aux) == attr_First)
592 wid += ustrwid(attr == word_Emph ? cfg->startemph : cfg->lquote,
593 cfg->charset);
594 }
595 if (attr == word_Emph || attr == word_Code) {
596 if (attraux(text->aux) == attr_Only ||
597 attraux(text->aux) == attr_Last)
598 wid += ustrwid(attr == word_Emph ? cfg->startemph : cfg->lquote,
599 cfg->charset);
600 }
d7482997 601
db662ca1 602 switch (text->type) {
d7482997 603 case word_Normal:
604 case word_Emph:
605 case word_Code:
606 case word_WeakCode:
db662ca1 607 if (cvt_ok(cfg->charset, text->text) || !text->alt)
608 wid += ustrwid(text->text, cfg->charset);
609 else
610 wid += text_width_list(ctx, text->alt);
611 return wid;
d7482997 612
613 case word_WhiteSpace:
614 case word_EmphSpace:
615 case word_CodeSpace:
616 case word_WkCodeSpace:
617 case word_Quote:
618 case word_EmphQuote:
619 case word_CodeQuote:
620 case word_WkCodeQuote:
621 assert(text->type != word_CodeQuote &&
622 text->type != word_WkCodeQuote);
db662ca1 623 if (removeattr(text->type) == word_Quote) {
624 if (quoteaux(text->aux) == quote_Open)
625 wid += ustrwid(cfg->lquote, cfg->charset);
626 else
627 wid += ustrwid(cfg->rquote, cfg->charset);
628 } else
629 wid++; /* space */
d7482997 630 }
db662ca1 631
632 return wid;
d7482997 633}
634
2ac8ceac 635static void text_heading(textfile *tf, word *tprefix, word *nprefix,
636 word *text, alignstruct align,
db662ca1 637 int indent, int width, textconfig *cfg) {
2ac8ceac 638 rdstring t = { 0, 0, NULL };
d7482997 639 int margin, length;
640 int firstlinewidth, wrapwidth;
641 wrappedline *wrapping, *p;
642
6e674706 643 if (align.number_at_all) {
644 if (align.just_numbers && nprefix) {
645 text_rdaddw(&t, nprefix, NULL, cfg);
646 rdadds(&t, align.number_suffix);
647 } else if (!align.just_numbers && tprefix) {
648 text_rdaddw(&t, tprefix, NULL, cfg);
649 rdadds(&t, align.number_suffix);
650 }
d7482997 651 }
db662ca1 652 margin = length = ustrwid(t.text ? t.text : L"", cfg->charset);
d7482997 653
654 if (align.align == LEFTPLUS) {
655 margin = indent - margin;
656 if (margin < 0) margin = 0;
657 firstlinewidth = indent + width - margin - length;
658 wrapwidth = width;
659 } else if (align.align == LEFT || align.align == CENTRE) {
660 margin = 0;
661 firstlinewidth = indent + width - length;
662 wrapwidth = indent + width;
663 }
664
2ac8ceac 665 wrapping = wrap_para(text, firstlinewidth, wrapwidth,
db662ca1 666 text_width, cfg, 0);
d7482997 667 for (p = wrapping; p; p = p->next) {
db662ca1 668 text_rdaddw(&t, p->begin, p->end, cfg);
669 length = ustrwid(t.text ? t.text : L"", cfg->charset);
d7482997 670 if (align.align == CENTRE) {
671 margin = (indent + width - length)/2;
672 if (margin < 0) margin = 0;
673 }
2ac8ceac 674 text_output_many(tf, margin, L' ');
675 text_output(tf, t.text);
676 text_output(tf, L"\n");
db662ca1 677 if (*align.underline) {
2ac8ceac 678 text_output_many(tf, margin, L' ');
db662ca1 679 while (length > 0) {
680 text_output(tf, align.underline);
681 length -= ustrwid(align.underline, cfg->charset);
682 }
2ac8ceac 683 text_output(tf, L"\n");
d7482997 684 }
685 if (align.align == LEFTPLUS)
686 margin = indent;
687 else
688 margin = 0;
689 sfree(t.text);
2ac8ceac 690 t = empty_rdstring;
d7482997 691 }
692 wrap_free(wrapping);
2ac8ceac 693 text_output(tf, L"\n");
d7482997 694
695 sfree(t.text);
696}
697
db662ca1 698static void text_rule(textfile *tf, int indent, int width, textconfig *cfg) {
2ac8ceac 699 text_output_many(tf, indent, L' ');
db662ca1 700 while (width > 0) {
701 text_output(tf, cfg->rule);
702 width -= ustrwid(cfg->rule, cfg->charset);
703 }
2ac8ceac 704 text_output_many(tf, 2, L'\n');
d7482997 705}
706
2ac8ceac 707static void text_para(textfile *tf, word *prefix, wchar_t *prefixextra,
db662ca1 708 word *text, int indent, int extraindent, int width,
709 textconfig *cfg) {
d7482997 710 wrappedline *wrapping, *p;
2ac8ceac 711 rdstring pfx = { 0, 0, NULL };
d7482997 712 int e;
713 int firstlinewidth = width;
714
715 if (prefix) {
db662ca1 716 text_rdaddw(&pfx, prefix, NULL, cfg);
d7482997 717 if (prefixextra)
2ac8ceac 718 rdadds(&pfx, prefixextra);
719 text_output_many(tf, indent, L' ');
720 text_output(tf, pfx.text);
c83c6495 721 /* If the prefix is too long, shorten the first line to fit. */
db662ca1 722 e = extraindent - ustrwid(pfx.text ? pfx.text : L"", cfg->charset);
d7482997 723 if (e < 0) {
c83c6495 724 firstlinewidth += e; /* this decreases it, since e < 0 */
d7482997 725 if (firstlinewidth < 0) {
726 e = indent + extraindent;
727 firstlinewidth = width;
2ac8ceac 728 text_output(tf, L"\n");
c83c6495 729 } else
730 e = 0;
d7482997 731 }
732 sfree(pfx.text);
733 } else
734 e = indent + extraindent;
735
2ac8ceac 736 wrapping = wrap_para(text, firstlinewidth, width,
db662ca1 737 text_width, cfg, 0);
d7482997 738 for (p = wrapping; p; p = p->next) {
2ac8ceac 739 rdstring t = { 0, 0, NULL };
db662ca1 740 text_rdaddw(&t, p->begin, p->end, cfg);
2ac8ceac 741 text_output_many(tf, e, L' ');
742 text_output(tf, t.text);
743 text_output(tf, L"\n");
d7482997 744 e = indent + extraindent;
745 sfree(t.text);
746 }
747 wrap_free(wrapping);
2ac8ceac 748 text_output(tf, L"\n");
d7482997 749}
750
2ac8ceac 751static void text_codepara(textfile *tf, word *text, int indent, int width) {
d7482997 752 for (; text; text = text->next) if (text->type == word_WeakCode) {
db662ca1 753 int wid = ustrwid(text->text, tf->charset);
754 if (wid > width)
755 error(err_text_codeline, &text->fpos, wid, width);
2ac8ceac 756 text_output_many(tf, indent, L' ');
757 text_output(tf, text->text);
758 text_output(tf, L"\n");
d7482997 759 }
760
2ac8ceac 761 text_output(tf, L"\n");
d7482997 762}
763
db662ca1 764static void text_versionid(textfile *tf, word *text, textconfig *cfg) {
2ac8ceac 765 rdstring t = { 0, 0, NULL };
d7482997 766
db662ca1 767 rdadd(&t, L'[');
768 text_rdaddw(&t, text, NULL, cfg);
769 rdadd(&t, L']');
2ac8ceac 770 rdadd(&t, L'\n');
d7482997 771
2ac8ceac 772 text_output(tf, t.text);
d7482997 773 sfree(t.text);
774}