2 * text backend for Halibut
10 typedef enum { LEFT
, LEFTPLUS
, CENTRE
} alignment
;
15 wchar_t *number_suffix
;
19 int indent
, indent_code
;
20 int listindentbefore
, listindentafter
;
22 alignstruct atitle
, achapter
, *asect
;
24 int include_version_id
;
30 static int text_convert(wchar_t *, char **);
32 static void text_heading(FILE *, word
*, word
*, word
*, alignstruct
, int,int);
33 static void text_rule(FILE *, int, int);
34 static void text_para(FILE *, word
*, char *, word
*, int, int, int);
35 static void text_codepara(FILE *, word
*, int, int);
36 static void text_versionid(FILE *, word
*);
38 static alignment
utoalign(wchar_t *p
) {
39 if (!ustricmp(p
, L
"centre") || !ustricmp(p
, L
"center"))
41 if (!ustricmp(p
, L
"leftplus"))
46 static textconfig
text_configure(paragraph
*source
) {
52 ret
.bullet
.next
= NULL
;
53 ret
.bullet
.alt
= NULL
;
54 ret
.bullet
.type
= word_Normal
;
55 ret
.atitle
.just_numbers
= FALSE
; /* ignored */
62 ret
.listindentbefore
= 1;
63 ret
.listindentafter
= 3;
65 ret
.atitle
.align
= CENTRE
;
66 ret
.atitle
.underline
= L
'=';
67 ret
.achapter
.align
= LEFT
;
68 ret
.achapter
.just_numbers
= FALSE
;
69 ret
.achapter
.number_suffix
= L
": ";
70 ret
.achapter
.underline
= L
'-';
72 ret
.asect
= mknewa(alignstruct
, ret
.nasect
);
73 ret
.asect
[0].align
= LEFTPLUS
;
74 ret
.asect
[0].just_numbers
= TRUE
;
75 ret
.asect
[0].number_suffix
= L
" ";
76 ret
.asect
[0].underline
= L
'\0';
77 ret
.include_version_id
= TRUE
;
78 ret
.indent_preambles
= FALSE
;
79 ret
.bullet
.text
= L
"-";
80 ret
.filename
= dupstr("output.txt");
82 for (; source
; source
= source
->next
) {
83 if (source
->type
== para_Config
) {
84 if (!ustricmp(source
->keyword
, L
"text-indent")) {
85 ret
.indent
= utoi(uadv(source
->keyword
));
86 } else if (!ustricmp(source
->keyword
, L
"text-filename")) {
88 ret
.filename
= utoa_dup(uadv(source
->keyword
));
89 } else if (!ustricmp(source
->keyword
, L
"text-indent-code")) {
90 ret
.indent_code
= utoi(uadv(source
->keyword
));
91 } else if (!ustricmp(source
->keyword
, L
"text-width")) {
92 ret
.width
= utoi(uadv(source
->keyword
));
93 } else if (!ustricmp(source
->keyword
, L
"text-list-indent")) {
94 ret
.listindentbefore
= utoi(uadv(source
->keyword
));
95 } else if (!ustricmp(source
->keyword
, L
"text-listitem-indent")) {
96 ret
.listindentafter
= utoi(uadv(source
->keyword
));
97 } else if (!ustricmp(source
->keyword
, L
"text-chapter-align")) {
98 ret
.achapter
.align
= utoalign(uadv(source
->keyword
));
99 } else if (!ustricmp(source
->keyword
, L
"text-chapter-underline")) {
100 ret
.achapter
.underline
= *uadv(source
->keyword
);
101 } else if (!ustricmp(source
->keyword
, L
"text-chapter-numeric")) {
102 ret
.achapter
.just_numbers
= utob(uadv(source
->keyword
));
103 } else if (!ustricmp(source
->keyword
, L
"text-chapter-suffix")) {
104 ret
.achapter
.number_suffix
= uadv(source
->keyword
);
105 } else if (!ustricmp(source
->keyword
, L
"text-section-align")) {
106 wchar_t *p
= uadv(source
->keyword
);
112 if (n
>= ret
.nasect
) {
114 ret
.asect
= resize(ret
.asect
, n
+1);
115 for (i
= ret
.nasect
; i
<= n
; i
++)
116 ret
.asect
[i
] = ret
.asect
[ret
.nasect
-1];
119 ret
.asect
[n
].align
= utoalign(p
);
120 } else if (!ustricmp(source
->keyword
, L
"text-section-underline")) {
121 wchar_t *p
= uadv(source
->keyword
);
127 if (n
>= ret
.nasect
) {
129 ret
.asect
= resize(ret
.asect
, n
+1);
130 for (i
= ret
.nasect
; i
<= n
; i
++)
131 ret
.asect
[i
] = ret
.asect
[ret
.nasect
-1];
134 ret
.asect
[n
].underline
= *p
;
135 } else if (!ustricmp(source
->keyword
, L
"text-section-numeric")) {
136 wchar_t *p
= uadv(source
->keyword
);
142 if (n
>= ret
.nasect
) {
144 ret
.asect
= resize(ret
.asect
, n
+1);
145 for (i
= ret
.nasect
; i
<= n
; i
++)
146 ret
.asect
[i
] = ret
.asect
[ret
.nasect
-1];
149 ret
.asect
[n
].just_numbers
= utob(p
);
150 } else if (!ustricmp(source
->keyword
, L
"text-section-suffix")) {
151 wchar_t *p
= uadv(source
->keyword
);
157 if (n
>= ret
.nasect
) {
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];
165 ret
.asect
[n
].number_suffix
= p
;
166 } else if (!ustricmp(source
->keyword
, L
"text-title-align")) {
167 ret
.atitle
.align
= utoalign(uadv(source
->keyword
));
168 } else if (!ustricmp(source
->keyword
, L
"text-title-underline")) {
169 ret
.atitle
.underline
= *uadv(source
->keyword
);
170 } else if (!ustricmp(source
->keyword
, L
"text-versionid")) {
171 ret
.include_version_id
= utob(uadv(source
->keyword
));
172 } else if (!ustricmp(source
->keyword
, L
"text-indent-preamble")) {
173 ret
.indent_preambles
= utob(uadv(source
->keyword
));
174 } else if (!ustricmp(source
->keyword
, L
"text-bullet")) {
175 ret
.bullet
.text
= uadv(source
->keyword
);
183 paragraph
*text_config_filename(char *filename
)
186 wchar_t *ufilename
, *up
;
189 p
= mknew(paragraph
);
190 memset(p
, 0, sizeof(*p
));
191 p
->type
= para_Config
;
193 p
->fpos
.filename
= "<command line>";
194 p
->fpos
.line
= p
->fpos
.col
= -1;
196 ufilename
= ufroma_dup(filename
);
197 len
= ustrlen(ufilename
) + 2 + lenof(L
"text-filename");
198 p
->keyword
= mknewa(wchar_t, len
);
200 ustrcpy(up
, L
"text-filename");
202 ustrcpy(up
, ufilename
);
205 assert(up
- p
->keyword
< len
);
211 void text_backend(paragraph
*sourceform
, keywordlist
*keywords
,
212 indexdata
*idx
, void *unused
) {
215 word
*prefix
, *body
, *wp
;
219 int nesting
, nestindent
;
220 int indentb
, indenta
;
223 IGNORE(keywords
); /* we don't happen to need this */
224 IGNORE(idx
); /* or this */
226 conf
= text_configure(sourceform
);
229 * Open the output file.
231 fp
= fopen(conf
.filename
, "w");
233 error(err_cantopenw
, conf
.filename
);
238 for (p
= sourceform
; p
; p
= p
->next
)
239 if (p
->type
== para_Title
)
240 text_heading(fp
, NULL
, NULL
, p
->words
,
241 conf
.atitle
, conf
.indent
, conf
.width
);
243 nestindent
= conf
.listindentbefore
+ conf
.listindentafter
;
244 nesting
= (conf
.indent_preambles ?
0 : -conf
.indent
);
246 /* Do the main document */
247 for (p
= sourceform
; p
; p
= p
->next
) switch (p
->type
) {
254 assert(nesting
>= 0);
258 nesting
+= nestindent
;
261 nesting
-= nestindent
;
262 assert(nesting
>= 0);
266 * Things we ignore because we've already processed them or
267 * aren't going to touch them in this pass.
271 case para_Biblio
: /* only touch BiblioCited */
282 case para_UnnumberedChapter
:
283 text_heading(fp
, p
->kwtext
, p
->kwtext2
, p
->words
,
284 conf
.achapter
, conf
.indent
, conf
.width
);
290 text_heading(fp
, p
->kwtext
, p
->kwtext2
, p
->words
,
291 conf
.asect
[p
->aux
>=conf
.nasect ? conf
.nasect
-1 : p
->aux
],
292 conf
.indent
, conf
.width
);
296 text_rule(fp
, conf
.indent
+ nesting
, conf
.width
- nesting
);
301 case para_DescribedThing
:
302 case para_Description
:
303 case para_BiblioCited
:
305 case para_NumberedList
:
306 if (p
->type
== para_Bullet
) {
307 prefix
= &conf
.bullet
;
309 indentb
= conf
.listindentbefore
;
310 indenta
= conf
.listindentafter
;
311 } else if (p
->type
== para_NumberedList
) {
313 prefixextra
= "."; /* FIXME: configurability */
314 indentb
= conf
.listindentbefore
;
315 indenta
= conf
.listindentafter
;
316 } else if (p
->type
== para_Description
) {
319 indentb
= conf
.listindentbefore
;
320 indenta
= conf
.listindentafter
;
324 indentb
= indenta
= 0;
326 if (p
->type
== para_BiblioCited
) {
327 body
= dup_word_list(p
->kwtext
);
328 for (wp
= body
; wp
->next
; wp
= wp
->next
);
329 wp
->next
= &spaceword
;
330 spaceword
.next
= p
->words
;
331 spaceword
.alt
= NULL
;
332 spaceword
.type
= word_WhiteSpace
;
333 spaceword
.text
= NULL
;
338 text_para(fp
, prefix
, prefixextra
, body
,
339 conf
.indent
+ nesting
+ indentb
, indenta
,
340 conf
.width
- nesting
- indentb
- indenta
);
343 free_word_list(body
);
348 text_codepara(fp
, p
->words
,
349 conf
.indent
+ nesting
+ conf
.indent_code
,
350 conf
.width
- nesting
- 2 * conf
.indent_code
);
354 /* Do the version ID */
355 if (conf
.include_version_id
) {
356 for (p
= sourceform
; p
; p
= p
->next
)
357 if (p
->type
== para_VersionID
)
358 text_versionid(fp
, p
->words
);
366 sfree(conf
.filename
);
370 * Convert a wide string into a string of chars. If `result' is
371 * non-NULL, mallocs the resulting string and stores a pointer to
372 * it in `*result'. If `result' is NULL, merely checks whether all
373 * characters in the string are feasible for the output character
376 * Return is nonzero if all characters are OK. If not all
377 * characters are OK but `result' is non-NULL, a result _will_
378 * still be generated!
380 static int text_convert(wchar_t *s
, char **result
) {
382 * FIXME. Currently this is ISO8859-1 only.
384 int doing
= (result
!= 0);
387 int plen
= 0, psize
= 0;
393 if ((c
>= 32 && c
<= 126) ||
394 (c
>= 160 && c
<= 255)) {
398 /* Char is not OK. */
400 outc
= 0xBF; /* approximate the good old DEC `uh?' */
405 p
= resize(p
, psize
);
411 p
= resize(p
, plen
+1);
418 static void text_rdaddwc(rdstringc
*rs
, word
*text
, word
*end
) {
421 for (; text
&& text
!= end
; text
= text
->next
) switch (text
->type
) {
434 case word_WhiteSpace
:
437 case word_WkCodeSpace
:
441 case word_WkCodeQuote
:
442 assert(text
->type
!= word_CodeQuote
&&
443 text
->type
!= word_WkCodeQuote
);
444 if (towordstyle(text
->type
) == word_Emph
&&
445 (attraux(text
->aux
) == attr_First
||
446 attraux(text
->aux
) == attr_Only
))
447 rdaddc(rs
, '_'); /* FIXME: configurability */
448 else if (towordstyle(text
->type
) == word_Code
&&
449 (attraux(text
->aux
) == attr_First
||
450 attraux(text
->aux
) == attr_Only
))
451 rdaddc(rs
, '`'); /* FIXME: configurability */
452 if (removeattr(text
->type
) == word_Normal
) {
453 if (text_convert(text
->text
, &c
))
456 text_rdaddwc(rs
, text
->alt
, NULL
);
458 } else if (removeattr(text
->type
) == word_WhiteSpace
) {
460 } else if (removeattr(text
->type
) == word_Quote
) {
461 rdaddc(rs
, quoteaux(text
->aux
) == quote_Open ?
'`' : '\'');
462 /* FIXME: configurability */
464 if (towordstyle(text
->type
) == word_Emph
&&
465 (attraux(text
->aux
) == attr_Last
||
466 attraux(text
->aux
) == attr_Only
))
467 rdaddc(rs
, '_'); /* FIXME: configurability */
468 else if (towordstyle(text
->type
) == word_Code
&&
469 (attraux(text
->aux
) == attr_Last
||
470 attraux(text
->aux
) == attr_Only
))
471 rdaddc(rs
, '\''); /* FIXME: configurability */
476 static int text_width(word
*);
478 static int text_width_list(word
*text
) {
481 w
+= text_width(text
);
487 static int text_width(word
*text
) {
488 switch (text
->type
) {
501 return (((text
->type
== word_Emph
||
502 text
->type
== word_Code
)
503 ?
(attraux(text
->aux
) == attr_Only ?
2 :
504 attraux(text
->aux
) == attr_Always ?
0 : 1)
506 (text_convert(text
->text
, NULL
) ?
507 ustrlen(text
->text
) :
508 text_width_list(text
->alt
)));
510 case word_WhiteSpace
:
513 case word_WkCodeSpace
:
517 case word_WkCodeQuote
:
518 assert(text
->type
!= word_CodeQuote
&&
519 text
->type
!= word_WkCodeQuote
);
520 return (((towordstyle(text
->type
) == word_Emph
||
521 towordstyle(text
->type
) == word_Code
)
522 ?
(attraux(text
->aux
) == attr_Only ?
2 :
523 attraux(text
->aux
) == attr_Always ?
0 : 1)
526 return 0; /* should never happen */
529 static void text_heading(FILE *fp
, word
*tprefix
, word
*nprefix
, word
*text
,
530 alignstruct align
, int indent
, int width
) {
531 rdstringc t
= { 0, 0, NULL
};
533 int firstlinewidth
, wrapwidth
;
534 wrappedline
*wrapping
, *p
;
536 if (align
.just_numbers
&& nprefix
) {
538 text_rdaddwc(&t
, nprefix
, NULL
);
539 if (text_convert(align
.number_suffix
, &c
)) {
543 } else if (!align
.just_numbers
&& tprefix
) {
545 text_rdaddwc(&t
, tprefix
, NULL
);
546 if (text_convert(align
.number_suffix
, &c
)) {
551 margin
= length
= (t
.text ?
strlen(t
.text
) : 0);
553 if (align
.align
== LEFTPLUS
) {
554 margin
= indent
- margin
;
555 if (margin
< 0) margin
= 0;
556 firstlinewidth
= indent
+ width
- margin
- length
;
558 } else if (align
.align
== LEFT
|| align
.align
== CENTRE
) {
560 firstlinewidth
= indent
+ width
- length
;
561 wrapwidth
= indent
+ width
;
564 wrapping
= wrap_para(text
, firstlinewidth
, wrapwidth
, text_width
);
565 for (p
= wrapping
; p
; p
= p
->next
) {
566 text_rdaddwc(&t
, p
->begin
, p
->end
);
567 length
= (t
.text ?
strlen(t
.text
) : 0);
568 if (align
.align
== CENTRE
) {
569 margin
= (indent
+ width
- length
)/2;
570 if (margin
< 0) margin
= 0;
572 fprintf(fp
, "%*s%s\n", margin
, "", t
.text
);
573 if (align
.underline
!= L
'\0') {
576 uw
[0] = align
.underline
; uw
[1] = L
'\0';
577 text_convert(uw
, &u
);
580 fprintf(fp
, "%*s", margin
, "");
585 if (align
.align
== LEFTPLUS
)
598 static void text_rule(FILE *fp
, int indent
, int width
) {
599 while (indent
--) putc(' ', fp
);
600 while (width
--) putc('-', fp
); /* FIXME: configurability! */
605 static void text_para(FILE *fp
, word
*prefix
, char *prefixextra
, word
*text
,
606 int indent
, int extraindent
, int width
) {
607 wrappedline
*wrapping
, *p
;
608 rdstringc pfx
= { 0, 0, NULL
};
610 int firstlinewidth
= width
;
613 text_rdaddwc(&pfx
, prefix
, NULL
);
615 rdaddsc(&pfx
, prefixextra
);
616 fprintf(fp
, "%*s%s", indent
, "", pfx
.text
);
617 /* If the prefix is too long, shorten the first line to fit. */
618 e
= extraindent
- strlen(pfx
.text
);
620 firstlinewidth
+= e
; /* this decreases it, since e < 0 */
621 if (firstlinewidth
< 0) {
622 e
= indent
+ extraindent
;
623 firstlinewidth
= width
;
630 e
= indent
+ extraindent
;
632 wrapping
= wrap_para(text
, firstlinewidth
, width
, text_width
);
633 for (p
= wrapping
; p
; p
= p
->next
) {
634 rdstringc t
= { 0, 0, NULL
};
635 text_rdaddwc(&t
, p
->begin
, p
->end
);
636 fprintf(fp
, "%*s%s\n", e
, "", t
.text
);
637 e
= indent
+ extraindent
;
644 static void text_codepara(FILE *fp
, word
*text
, int indent
, int width
) {
645 for (; text
; text
= text
->next
) if (text
->type
== word_WeakCode
) {
647 text_convert(text
->text
, &c
);
648 if (strlen(c
) > (size_t)width
) {
651 fprintf(fp
, "%*s%s\n", indent
, "", c
);
658 static void text_versionid(FILE *fp
, word
*text
) {
659 rdstringc t
= { 0, 0, NULL
};
661 rdaddc(&t
, '['); /* FIXME: configurability */
662 text_rdaddwc(&t
, text
, NULL
);
663 rdaddc(&t
, ']'); /* FIXME: configurability */
665 fprintf(fp
, "%s\n", t
.text
);