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