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