2 * text backend for Halibut
10 typedef enum { LEFT
, LEFTPLUS
, CENTRE
} alignment
;
18 int indent
, indent_code
;
19 int listindentbefore
, listindentafter
;
21 alignstruct atitle
, achapter
, *asect
;
23 int include_version_id
;
28 static int text_convert(wchar_t *, char **);
30 static void text_heading(FILE *, word
*, word
*, word
*, alignstruct
, int,int);
31 static void text_rule(FILE *, int, int);
32 static void text_para(FILE *, word
*, char *, word
*, int, int, int);
33 static void text_codepara(FILE *, word
*, int, int);
34 static void text_versionid(FILE *, word
*);
36 static alignment
utoalign(wchar_t *p
) {
37 if (!ustricmp(p
, L
"centre") || !ustricmp(p
, L
"center"))
39 if (!ustricmp(p
, L
"leftplus"))
44 static textconfig
text_configure(paragraph
*source
) {
50 ret
.bullet
.next
= NULL
;
51 ret
.bullet
.alt
= NULL
;
52 ret
.bullet
.type
= word_Normal
;
53 ret
.atitle
.just_numbers
= FALSE
; /* ignored */
60 ret
.listindentbefore
= 1;
61 ret
.listindentafter
= 3;
63 ret
.atitle
.align
= CENTRE
;
64 ret
.atitle
.underline
= L
'=';
65 ret
.achapter
.align
= LEFT
;
66 ret
.achapter
.just_numbers
= FALSE
;
67 ret
.achapter
.underline
= L
'-';
69 ret
.asect
= mknewa(alignstruct
, ret
.nasect
);
70 ret
.asect
[0].align
= LEFTPLUS
;
71 ret
.asect
[0].just_numbers
= TRUE
;
72 ret
.asect
[0].underline
= L
'\0';
73 ret
.include_version_id
= TRUE
;
74 ret
.indent_preambles
= FALSE
;
75 ret
.bullet
.text
= ustrdup(L
"-");
77 for (; source
; source
= source
->next
) {
78 if (source
->type
== para_Config
) {
79 if (!ustricmp(source
->keyword
, L
"text-indent")) {
80 ret
.indent
= utoi(uadv(source
->keyword
));
81 } else if (!ustricmp(source
->keyword
, L
"text-indent-code")) {
82 ret
.indent_code
= utoi(uadv(source
->keyword
));
83 } else if (!ustricmp(source
->keyword
, L
"text-width")) {
84 ret
.width
= utoi(uadv(source
->keyword
));
85 } else if (!ustricmp(source
->keyword
, L
"text-list-indent")) {
86 ret
.listindentbefore
= utoi(uadv(source
->keyword
));
87 } else if (!ustricmp(source
->keyword
, L
"text-listitem-indent")) {
88 ret
.listindentafter
= utoi(uadv(source
->keyword
));
89 } else if (!ustricmp(source
->keyword
, L
"text-chapter-align")) {
90 ret
.achapter
.align
= utoalign(uadv(source
->keyword
));
91 } else if (!ustricmp(source
->keyword
, L
"text-chapter-underline")) {
92 ret
.achapter
.underline
= *uadv(source
->keyword
);
93 } else if (!ustricmp(source
->keyword
, L
"text-chapter-numeric")) {
94 ret
.achapter
.underline
= utob(uadv(source
->keyword
));
95 } else if (!ustricmp(source
->keyword
, L
"text-section-align")) {
96 wchar_t *p
= uadv(source
->keyword
);
102 if (n
>= ret
.nasect
) {
104 ret
.asect
= resize(ret
.asect
, n
+1);
105 for (i
= ret
.nasect
; i
<= n
; i
++)
106 ret
.asect
[i
] = ret
.asect
[ret
.nasect
-1];
109 ret
.asect
[n
].align
= utoalign(p
);
110 } else if (!ustricmp(source
->keyword
, L
"text-section-underline")) {
111 wchar_t *p
= uadv(source
->keyword
);
117 if (n
>= ret
.nasect
) {
119 ret
.asect
= resize(ret
.asect
, n
+1);
120 for (i
= ret
.nasect
; i
<= n
; i
++)
121 ret
.asect
[i
] = ret
.asect
[ret
.nasect
-1];
124 ret
.asect
[n
].underline
= *p
;
125 } else if (!ustricmp(source
->keyword
, L
"text-section-numeric")) {
126 wchar_t *p
= uadv(source
->keyword
);
132 if (n
>= ret
.nasect
) {
134 ret
.asect
= resize(ret
.asect
, n
+1);
135 for (i
= ret
.nasect
; i
<= n
; i
++)
136 ret
.asect
[i
] = ret
.asect
[ret
.nasect
-1];
139 ret
.asect
[n
].just_numbers
= utob(p
);
140 } else if (!ustricmp(source
->keyword
, L
"text-title-align")) {
141 ret
.atitle
.align
= utoalign(uadv(source
->keyword
));
142 } else if (!ustricmp(source
->keyword
, L
"text-title-underline")) {
143 ret
.atitle
.underline
= *uadv(source
->keyword
);
144 } else if (!ustricmp(source
->keyword
, L
"text-versionid")) {
145 ret
.include_version_id
= utob(uadv(source
->keyword
));
146 } else if (!ustricmp(source
->keyword
, L
"text-indent-preamble")) {
147 ret
.indent_preambles
= utob(uadv(source
->keyword
));
148 } else if (!ustricmp(source
->keyword
, L
"text-bullet")) {
149 ret
.bullet
.text
= uadv(source
->keyword
);
157 void text_backend(paragraph
*sourceform
, keywordlist
*keywords
,
161 word
*prefix
, *body
, *wp
;
165 int indentb
, indenta
;
167 IGNORE(keywords
); /* we don't happen to need this */
168 IGNORE(idx
); /* or this */
170 conf
= text_configure(sourceform
);
173 * Determine the output file name, and open the output file
175 * FIXME: want configurable output file names here. For the
176 * moment, we'll just call it `output.txt'.
178 fp
= fopen("output.txt", "w");
180 error(err_cantopenw
, "output.txt");
185 for (p
= sourceform
; p
; p
= p
->next
)
186 if (p
->type
== para_Title
)
187 text_heading(fp
, NULL
, NULL
, p
->words
,
188 conf
.atitle
, conf
.indent
, conf
.width
);
190 /* Do the preamble and copyright */
191 for (p
= sourceform
; p
; p
= p
->next
)
192 if (p
->type
== para_Preamble
)
193 text_para(fp
, NULL
, NULL
, p
->words
,
194 conf
.indent_preambles ? conf
.indent
: 0, 0,
195 conf
.width
+ (conf
.indent_preambles ?
0 : conf
.indent
));
196 for (p
= sourceform
; p
; p
= p
->next
)
197 if (p
->type
== para_Copyright
)
198 text_para(fp
, NULL
, NULL
, p
->words
,
199 conf
.indent_preambles ? conf
.indent
: 0, 0,
200 conf
.width
+ (conf
.indent_preambles ?
0 : conf
.indent
));
202 /* Do the main document */
203 for (p
= sourceform
; p
; p
= p
->next
) switch (p
->type
) {
206 * Things we ignore because we've already processed them or
207 * aren't going to touch them in this pass.
211 case para_Biblio
: /* only touch BiblioCited */
224 case para_UnnumberedChapter
:
225 text_heading(fp
, p
->kwtext
, p
->kwtext2
, p
->words
,
226 conf
.achapter
, conf
.indent
, conf
.width
);
231 text_heading(fp
, p
->kwtext
, p
->kwtext2
, p
->words
,
232 conf
.asect
[p
->aux
>=conf
.nasect ? conf
.nasect
-1 : p
->aux
],
233 conf
.indent
, conf
.width
);
237 text_rule(fp
, conf
.indent
, conf
.width
);
241 case para_BiblioCited
:
243 case para_NumberedList
:
244 if (p
->type
== para_Bullet
) {
245 prefix
= &conf
.bullet
;
247 indentb
= conf
.listindentbefore
;
248 indenta
= conf
.listindentafter
;
249 } else if (p
->type
== para_NumberedList
) {
251 prefixextra
= "."; /* FIXME: configurability */
252 indentb
= conf
.listindentbefore
;
253 indenta
= conf
.listindentafter
;
257 indentb
= indenta
= 0;
259 if (p
->type
== para_BiblioCited
) {
260 body
= dup_word_list(p
->kwtext
);
261 for (wp
= body
; wp
->next
; wp
= wp
->next
);
262 wp
->next
= &spaceword
;
263 spaceword
.next
= p
->words
;
264 spaceword
.alt
= NULL
;
265 spaceword
.type
= word_WhiteSpace
;
266 spaceword
.text
= NULL
;
271 text_para(fp
, prefix
, prefixextra
, body
,
272 conf
.indent
+ indentb
, indenta
, conf
.width
);
275 free_word_list(body
);
280 text_codepara(fp
, p
->words
, conf
.indent
+ conf
.indent_code
, conf
.width
- 2 * conf
.indent_code
);
284 /* Do the version ID */
285 if (conf
.include_version_id
) {
286 for (p
= sourceform
; p
; p
= p
->next
)
287 if (p
->type
== para_VersionID
)
288 text_versionid(fp
, p
->words
);
295 sfree(conf
.bullet
.text
);
299 * Convert a wide string into a string of chars. If `result' is
300 * non-NULL, mallocs the resulting string and stores a pointer to
301 * it in `*result'. If `result' is NULL, merely checks whether all
302 * characters in the string are feasible for the output character
305 * Return is nonzero if all characters are OK. If not all
306 * characters are OK but `result' is non-NULL, a result _will_
307 * still be generated!
309 static int text_convert(wchar_t *s
, char **result
) {
311 * FIXME. Currently this is ISO8859-1 only.
313 int doing
= (result
!= 0);
316 int plen
= 0, psize
= 0;
322 if ((c
>= 32 && c
<= 126) ||
323 (c
>= 160 && c
<= 255)) {
327 /* Char is not OK. */
329 outc
= 0xBF; /* approximate the good old DEC `uh?' */
334 p
= resize(p
, psize
);
340 p
= resize(p
, plen
+1);
347 static void text_rdaddwc(rdstringc
*rs
, word
*text
, word
*end
) {
350 for (; text
&& text
!= end
; text
= text
->next
) switch (text
->type
) {
363 case word_WhiteSpace
:
366 case word_WkCodeSpace
:
370 case word_WkCodeQuote
:
371 assert(text
->type
!= word_CodeQuote
&&
372 text
->type
!= word_WkCodeQuote
);
373 if (towordstyle(text
->type
) == word_Emph
&&
374 (attraux(text
->aux
) == attr_First
||
375 attraux(text
->aux
) == attr_Only
))
376 rdaddc(rs
, '_'); /* FIXME: configurability */
377 else if (towordstyle(text
->type
) == word_Code
&&
378 (attraux(text
->aux
) == attr_First
||
379 attraux(text
->aux
) == attr_Only
))
380 rdaddc(rs
, '`'); /* FIXME: configurability */
381 if (removeattr(text
->type
) == word_Normal
) {
382 if (text_convert(text
->text
, &c
))
385 text_rdaddwc(rs
, text
->alt
, NULL
);
387 } else if (removeattr(text
->type
) == word_WhiteSpace
) {
389 } else if (removeattr(text
->type
) == word_Quote
) {
390 rdaddc(rs
, quoteaux(text
->aux
) == quote_Open ?
'`' : '\'');
391 /* FIXME: configurability */
393 if (towordstyle(text
->type
) == word_Emph
&&
394 (attraux(text
->aux
) == attr_Last
||
395 attraux(text
->aux
) == attr_Only
))
396 rdaddc(rs
, '_'); /* FIXME: configurability */
397 else if (towordstyle(text
->type
) == word_Code
&&
398 (attraux(text
->aux
) == attr_Last
||
399 attraux(text
->aux
) == attr_Only
))
400 rdaddc(rs
, '\''); /* FIXME: configurability */
405 static int text_width(word
*);
407 static int text_width_list(word
*text
) {
410 w
+= text_width(text
);
416 static int text_width(word
*text
) {
417 switch (text
->type
) {
430 return (((text
->type
== word_Emph
||
431 text
->type
== word_Code
)
432 ?
(attraux(text
->aux
) == attr_Only ?
2 :
433 attraux(text
->aux
) == attr_Always ?
0 : 1)
435 (text_convert(text
->text
, NULL
) ?
436 ustrlen(text
->text
) :
437 text_width_list(text
->alt
)));
439 case word_WhiteSpace
:
442 case word_WkCodeSpace
:
446 case word_WkCodeQuote
:
447 assert(text
->type
!= word_CodeQuote
&&
448 text
->type
!= word_WkCodeQuote
);
449 return (((towordstyle(text
->type
) == word_Emph
||
450 towordstyle(text
->type
) == word_Code
)
451 ?
(attraux(text
->aux
) == attr_Only ?
2 :
452 attraux(text
->aux
) == attr_Always ?
0 : 1)
455 return 0; /* should never happen */
458 static void text_heading(FILE *fp
, word
*tprefix
, word
*nprefix
, word
*text
,
459 alignstruct align
, int indent
, int width
) {
460 rdstringc t
= { 0, 0, NULL
};
462 int firstlinewidth
, wrapwidth
;
463 wrappedline
*wrapping
, *p
;
465 if (align
.just_numbers
&& nprefix
) {
466 text_rdaddwc(&t
, nprefix
, NULL
);
467 rdaddc(&t
, ' '); /* FIXME: as below */
468 } else if (!align
.just_numbers
&& tprefix
) {
469 text_rdaddwc(&t
, tprefix
, NULL
);
470 rdaddsc(&t
, ": "); /* FIXME: configurability */
472 margin
= length
= (t
.text ?
strlen(t
.text
) : 0);
474 if (align
.align
== LEFTPLUS
) {
475 margin
= indent
- margin
;
476 if (margin
< 0) margin
= 0;
477 firstlinewidth
= indent
+ width
- margin
- length
;
479 } else if (align
.align
== LEFT
|| align
.align
== CENTRE
) {
481 firstlinewidth
= indent
+ width
- length
;
482 wrapwidth
= indent
+ width
;
485 wrapping
= wrap_para(text
, firstlinewidth
, wrapwidth
, text_width
);
486 for (p
= wrapping
; p
; p
= p
->next
) {
487 text_rdaddwc(&t
, p
->begin
, p
->end
);
488 length
= (t
.text ?
strlen(t
.text
) : 0);
489 if (align
.align
== CENTRE
) {
490 margin
= (indent
+ width
- length
)/2;
491 if (margin
< 0) margin
= 0;
493 fprintf(fp
, "%*s%s\n", margin
, "", t
.text
);
494 if (align
.underline
!= L
'\0') {
497 uw
[0] = align
.underline
; uw
[1] = L
'\0';
498 text_convert(uw
, &u
);
501 fprintf(fp
, "%*s", margin
, "");
506 if (align
.align
== LEFTPLUS
)
519 static void text_rule(FILE *fp
, int indent
, int width
) {
520 while (indent
--) putc(' ', fp
);
521 while (width
--) putc('-', fp
); /* FIXME: configurability! */
526 static void text_para(FILE *fp
, word
*prefix
, char *prefixextra
, word
*text
,
527 int indent
, int extraindent
, int width
) {
528 wrappedline
*wrapping
, *p
;
529 rdstringc pfx
= { 0, 0, NULL
};
531 int firstlinewidth
= width
;
534 text_rdaddwc(&pfx
, prefix
, NULL
);
536 rdaddsc(&pfx
, prefixextra
);
537 fprintf(fp
, "%*s%s", indent
, "", pfx
.text
);
538 e
= extraindent
- strlen(pfx
.text
);
542 if (firstlinewidth
< 0) {
543 e
= indent
+ extraindent
;
544 firstlinewidth
= width
;
550 e
= indent
+ extraindent
;
552 wrapping
= wrap_para(text
, firstlinewidth
, width
, text_width
);
553 for (p
= wrapping
; p
; p
= p
->next
) {
554 rdstringc t
= { 0, 0, NULL
};
555 text_rdaddwc(&t
, p
->begin
, p
->end
);
556 fprintf(fp
, "%*s%s\n", e
, "", t
.text
);
557 e
= indent
+ extraindent
;
564 static void text_codepara(FILE *fp
, word
*text
, int indent
, int width
) {
565 for (; text
; text
= text
->next
) if (text
->type
== word_WeakCode
) {
567 text_convert(text
->text
, &c
);
568 if (strlen(c
) > (size_t)width
) {
571 fprintf(fp
, "%*s%s\n", indent
, "", c
);
578 static void text_versionid(FILE *fp
, word
*text
) {
579 rdstringc t
= { 0, 0, NULL
};
581 rdaddc(&t
, '['); /* FIXME: configurability */
582 text_rdaddwc(&t
, text
, NULL
);
583 rdaddc(&t
, ']'); /* FIXME: configurability */
585 fprintf(fp
, "%s\n", t
.text
);