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