2 * Windows Help backend for Halibut
13 struct bk_whlp_state
{
16 keywordlist
*keywords
;
17 WHLP_TOPIC curr_topic
;
21 int cnt_last_level
, cnt_workaround
;
26 wchar_t *bullet
, *lquote
, *rquote
, *titlepage
, *sectsuffix
, *listsuffix
;
27 wchar_t *contents_text
;
32 * Indexes of fonts in our standard font descriptor set.
46 static void whlp_rdaddwc(rdstringc
*rs
, word
*text
, whlpconf
*conf
,
47 charset_state
*state
);
48 static void whlp_rdadds(rdstringc
*rs
, const wchar_t *text
, whlpconf
*conf
,
49 charset_state
*state
);
50 static void whlp_mkparagraph(struct bk_whlp_state
*state
,
51 int font
, word
*text
, int subsidiary
,
53 static void whlp_navmenu(struct bk_whlp_state
*state
, paragraph
*p
,
55 static void whlp_contents_write(struct bk_whlp_state
*state
,
56 int level
, char *text
, WHLP_TOPIC topic
);
57 static void whlp_wtext(struct bk_whlp_state
*state
, const wchar_t *text
);
59 paragraph
*whlp_config_filename(char *filename
)
61 return cmdline_cfg_simple("winhelp-filename", filename
, NULL
);
64 static whlpconf
whlp_configure(paragraph
*source
) {
71 ret
.charset
= CS_CP1252
;
72 ret
.bullet
= L
"\x2022\0-\0\0";
73 ret
.lquote
= L
"\x2018\0\x2019\0\"\0\"\0\0";
74 ret
.rquote
= uadv(ret
.lquote
);
75 ret
.filename
= dupstr("output.hlp");
76 ret
.titlepage
= L
"Title page";
77 ret
.contents_text
= L
"Contents";
78 ret
.sectsuffix
= L
": ";
79 ret
.listsuffix
= L
".";
82 * Two-pass configuration so that we can pick up global config
83 * (e.g. `quotes') before having it overridden by specific
84 * config (`win-quotes'), irrespective of the order in which
87 for (p
= source
; p
; p
= p
->next
) {
88 if (p
->type
== para_Config
) {
89 if (!ustricmp(p
->keyword
, L
"quotes")) {
90 if (*uadv(p
->keyword
) && *uadv(uadv(p
->keyword
))) {
91 ret
.lquote
= uadv(p
->keyword
);
92 ret
.rquote
= uadv(ret
.lquote
);
98 for (p
= source
; p
; p
= p
->next
) {
99 p
->private_data
= NULL
;
100 if (p
->type
== para_Config
) {
102 * In principle we should support a `winhelp-charset'
103 * here. We don't, because my WinHelp output code
104 * doesn't know how to change character set. Once I
105 * find out, I'll support it.
107 if (p
->parent
&& !ustricmp(p
->keyword
, L
"winhelp-topic")) {
108 /* Store the topic name in the private_data field of the
109 * containing section. */
110 p
->parent
->private_data
= uadv(p
->keyword
);
111 } else if (!ustricmp(p
->keyword
, L
"winhelp-filename")) {
113 ret
.filename
= dupstr(adv(p
->origkeyword
));
114 } else if (!ustricmp(p
->keyword
, L
"winhelp-bullet")) {
115 ret
.bullet
= uadv(p
->keyword
);
116 } else if (!ustricmp(p
->keyword
, L
"winhelp-section-suffix")) {
117 ret
.sectsuffix
= uadv(p
->keyword
);
118 } else if (!ustricmp(p
->keyword
, L
"winhelp-list-suffix")) {
119 ret
.listsuffix
= uadv(p
->keyword
);
120 } else if (!ustricmp(p
->keyword
, L
"winhelp-contents-titlepage")) {
121 ret
.titlepage
= uadv(p
->keyword
);
122 } else if (!ustricmp(p
->keyword
, L
"winhelp-quotes")) {
123 if (*uadv(p
->keyword
) && *uadv(uadv(p
->keyword
))) {
124 ret
.lquote
= uadv(p
->keyword
);
125 ret
.rquote
= uadv(ret
.lquote
);
127 } else if (!ustricmp(p
->keyword
, L
"contents")) {
128 ret
.contents_text
= uadv(p
->keyword
);
134 * Now process fallbacks on quote characters and bullets.
136 while (*uadv(ret
.rquote
) && *uadv(uadv(ret
.rquote
)) &&
137 (!cvt_ok(ret
.charset
, ret
.lquote
) ||
138 !cvt_ok(ret
.charset
, ret
.rquote
))) {
139 ret
.lquote
= uadv(ret
.rquote
);
140 ret
.rquote
= uadv(ret
.lquote
);
143 while (*ret
.bullet
&& *uadv(ret
.bullet
) &&
144 !cvt_ok(ret
.charset
, ret
.bullet
))
145 ret
.bullet
= uadv(ret
.bullet
);
150 void whlp_backend(paragraph
*sourceform
, keywordlist
*keywords
,
151 indexdata
*idx
, void *unused
) {
154 paragraph
*p
, *lastsect
;
155 struct bk_whlp_state state
;
156 WHLP_TOPIC contents_topic
;
160 int done_contents_topic
= FALSE
;
165 h
= state
.h
= whlp_new();
166 state
.keywords
= keywords
;
169 whlp_start_macro(h
, "CB(\"btn_about\",\"&About\",\"About()\")");
170 whlp_start_macro(h
, "CB(\"btn_up\",\"&Up\",\"Contents()\")");
171 whlp_start_macro(h
, "BrowseButtons()");
173 whlp_create_font(h
, "Times New Roman", WHLP_FONTFAM_SERIF
, 24,
175 whlp_create_font(h
, "Times New Roman", WHLP_FONTFAM_SERIF
, 24,
176 WHLP_FONT_ITALIC
, 0, 0, 0);
177 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_FIXED
, 24,
179 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_FIXED
, 24,
180 WHLP_FONT_ITALIC
, 0, 0, 0);
181 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_FIXED
, 24,
182 WHLP_FONT_BOLD
, 0, 0, 0);
183 whlp_create_font(h
, "Arial", WHLP_FONTFAM_SERIF
, 30,
184 WHLP_FONT_BOLD
, 0, 0, 0);
185 whlp_create_font(h
, "Arial", WHLP_FONTFAM_SERIF
, 30,
186 WHLP_FONT_BOLD
|WHLP_FONT_ITALIC
, 0, 0, 0);
187 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_FIXED
, 30,
188 WHLP_FONT_BOLD
, 0, 0, 0);
189 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_SANS
, 18,
190 WHLP_FONT_STRIKEOUT
, 0, 0, 0);
192 conf
= whlp_configure(sourceform
);
194 state
.charset
= conf
.charset
;
197 * Ensure the output file name has a .hlp extension. This is
198 * required since we must create the .cnt file in parallel with
202 int len
= strlen(conf
.filename
);
203 if (len
< 4 || conf
.filename
[len
-4] != '.' ||
204 tolower(conf
.filename
[len
-3] != 'h') ||
205 tolower(conf
.filename
[len
-2] != 'l') ||
206 tolower(conf
.filename
[len
-1] != 'p')) {
208 newf
= snewn(len
+ 5, char);
209 sprintf(newf
, "%s.hlp", conf
.filename
);
210 sfree(conf
.filename
);
211 conf
.filename
= newf
;
214 cntname
= snewn(len
+1, char);
215 sprintf(cntname
, "%.*s.cnt", len
-4, conf
.filename
);
218 state
.cntfp
= fopen(cntname
, "wb");
220 error(err_cantopenw
, cntname
);
223 state
.cnt_last_level
= -1; state
.cnt_workaround
= 0;
226 * Loop over the source form registering WHLP_TOPICs for
230 contents_topic
= whlp_register_topic(h
, "Top", NULL
);
231 whlp_primary_topic(h
, contents_topic
);
232 for (p
= sourceform
; p
; p
= p
->next
) {
233 if (p
->type
== para_Chapter
||
234 p
->type
== para_Appendix
||
235 p
->type
== para_UnnumberedChapter
||
236 p
->type
== para_Heading
||
237 p
->type
== para_Subsect
) {
239 rdstringc rs
= { 0, 0, NULL
};
242 whlp_rdadds(&rs
, (wchar_t *)p
->private_data
, &conf
, NULL
);
244 p
->private_data
= whlp_register_topic(h
, rs
.text
, &errstr
);
245 if (!p
->private_data
) {
246 p
->private_data
= whlp_register_topic(h
, NULL
, NULL
);
247 error(err_winhelp_ctxclash
, &p
->fpos
, rs
.text
, errstr
);
254 * Loop over the index entries, preparing final text forms for
258 indexentry
*ie_prev
= NULL
;
261 for (i
= 0; (ie
= index234(idx
->entries
, i
)) != NULL
; i
++) {
262 rdstringc rs
= {0, 0, NULL
};
263 charset_state state
= CHARSET_INIT_STATE
;
264 whlp_rdaddwc(&rs
, ie
->text
, &conf
, &state
);
268 * It appears that Windows Help's index mechanism
269 * is inherently case-insensitive. Therefore, if two
270 * adjacent index terms compare equal apart from
271 * case, I'm going to append nonbreaking spaces to
272 * the end of the second one so that Windows will
273 * treat them as distinct.
275 * This is nasty because we're depending on our
276 * case-insensitive comparison having the same
277 * semantics as the Windows one :-/ but I see no
282 a
= ufroma_dup((char *)ie_prev
->backend_data
, conf
.charset
);
283 b
= ufroma_dup(rs
.text
, conf
.charset
);
284 if (!ustricmp(a
, b
)) {
286 for (j
= 0; j
< nspaces
; j
++)
287 whlp_rdadds(&rs
, L
"\xA0", &conf
, &state
);
289 * Add one to nspaces, so that if another term
290 * appears which is equivalent to the previous
291 * two it'll acquire one more space.
300 whlp_rdadds(&rs
, NULL
, &conf
, &state
);
302 ie
->backend_data
= rs
.text
;
305 * Only move ie_prev on if nspaces==1 (since when we
306 * have three or more adjacent terms differing only in
307 * case, we will want to compare with the _first_ of
308 * them because that won't have had any extra spaces
309 * added on which will foul up the comparison).
318 /* ------------------------------------------------------------------
319 * Begin the contents page.
322 rdstringc rs
= {0, 0, NULL
};
323 whlp_rdadds(&rs
, conf
.contents_text
, &conf
, NULL
);
324 whlp_begin_topic(h
, contents_topic
, rs
.text
, "DB(\"btn_up\")", NULL
);
325 state
.curr_topic
= contents_topic
;
330 * The manual title goes in the non-scroll region, and also
331 * goes into the system title slot.
334 rdstringc rs
= {0, 0, NULL
};
335 for (p
= sourceform
; p
; p
= p
->next
) {
336 if (p
->type
== para_Title
) {
337 whlp_begin_para(h
, WHLP_PARA_NONSCROLL
);
338 state
.cstate
= charset_init_state
;
339 whlp_mkparagraph(&state
, FONT_TITLE
, p
->words
, FALSE
, &conf
);
340 whlp_wtext(&state
, NULL
);
342 whlp_rdaddwc(&rs
, p
->words
, &conf
, NULL
);
346 whlp_title(h
, rs
.text
);
347 fprintf(state
.cntfp
, ":Title %s\r\n", rs
.text
);
351 rdstringc rs2
= {0,0,NULL
};
352 whlp_rdadds(&rs2
, conf
.titlepage
, &conf
, NULL
);
353 whlp_contents_write(&state
, 1, rs2
.text
, contents_topic
);
359 * Put the copyright into the system section.
362 rdstringc rs
= {0, 0, NULL
};
363 for (p
= sourceform
; p
; p
= p
->next
) {
364 if (p
->type
== para_Copyright
)
365 whlp_rdaddwc(&rs
, p
->words
, &conf
, NULL
);
368 whlp_copyright(h
, rs
.text
);
375 /* ------------------------------------------------------------------
376 * Now we've done the contents page, we're ready to go through
377 * and do the main manual text. Ooh.
380 for (p
= sourceform
; p
; p
= p
->next
) switch (p
->type
) {
382 * Things we ignore because we've already processed them or
383 * aren't going to touch them in this pass.
387 case para_Biblio
: /* only touch BiblioCited */
404 * Chapter and section titles: start a new Help topic.
408 case para_UnnumberedChapter
:
412 if (!done_contents_topic
) {
416 * If this is the first section title we've seen, then
417 * we're currently still in the contents topic. We
418 * should therefore finish up the contents page by
419 * writing a nav menu.
421 for (p
= sourceform
; p
; p
= p
->next
) {
422 if (p
->type
== para_Chapter
||
423 p
->type
== para_Appendix
||
424 p
->type
== para_UnnumberedChapter
)
425 whlp_navmenu(&state
, p
, &conf
);
428 done_contents_topic
= TRUE
;
431 if (lastsect
&& lastsect
->child
) {
434 * Do a navigation menu for the previous section we
437 for (q
= lastsect
->child
; q
; q
= q
->sibling
)
438 whlp_navmenu(&state
, q
, &conf
);
441 rdstringc rs
= {0, 0, NULL
};
442 WHLP_TOPIC new_topic
, parent_topic
;
443 char *macro
, *topicid
;
444 charset_state cstate
= CHARSET_INIT_STATE
;
446 new_topic
= p
->private_data
;
447 whlp_browse_link(h
, state
.curr_topic
, new_topic
);
448 state
.curr_topic
= new_topic
;
451 whlp_rdaddwc(&rs
, p
->kwtext
, &conf
, &cstate
);
452 whlp_rdadds(&rs
, conf
.sectsuffix
, &conf
, &cstate
);
454 whlp_rdaddwc(&rs
, p
->words
, &conf
, &cstate
);
455 whlp_rdadds(&rs
, NULL
, &conf
, &cstate
);
457 if (p
->parent
== NULL
)
458 parent_topic
= contents_topic
;
460 parent_topic
= (WHLP_TOPIC
)p
->parent
->private_data
;
461 topicid
= whlp_topic_id(parent_topic
);
462 macro
= smalloc(100+strlen(topicid
));
464 "CBB(\"btn_up\",\"JI(`',`%s')\");EB(\"btn_up\")",
466 whlp_begin_topic(h
, new_topic
,
467 rs
.text ? rs
.text
: "",
473 * Output the .cnt entry.
475 * WinHelp has a bug involving having an internal
476 * node followed by a leaf at the same level: the
477 * leaf is output at the wrong level. We can mostly
478 * work around this by modifying the leaf level
479 * itself (see whlp_contents_write), but this
480 * doesn't work for top-level sections since we
481 * can't turn a level-1 leaf into a level-0 one. So
482 * for top-level leaf sections (Bibliography
483 * springs to mind), we output an internal node
484 * containing only the leaf for that section.
489 /* Count up the level. */
491 for (q
= p
; q
->parent
; q
= q
->parent
) i
++;
493 if (p
->child
|| !p
->parent
) {
495 * If p has children then it needs to be a
496 * folder; if it has no parent then it needs to
497 * be a folder to work around the bug.
499 whlp_contents_write(&state
, i
, rs
.text
, NULL
);
502 whlp_contents_write(&state
, i
, rs
.text
, new_topic
);
507 whlp_begin_para(h
, WHLP_PARA_NONSCROLL
);
508 state
.cstate
= charset_init_state
;
510 whlp_mkparagraph(&state
, FONT_TITLE
, p
->kwtext
, FALSE
, &conf
);
511 whlp_set_font(h
, FONT_TITLE
);
512 whlp_wtext(&state
, conf
.sectsuffix
);
514 whlp_mkparagraph(&state
, FONT_TITLE
, p
->words
, FALSE
, &conf
);
515 whlp_wtext(&state
, NULL
);
523 whlp_para_attr(h
, WHLP_PARA_SPACEBELOW
, 12);
524 whlp_para_attr(h
, WHLP_PARA_ALIGNMENT
, WHLP_ALIGN_CENTRE
);
525 whlp_begin_para(h
, WHLP_PARA_SCROLL
);
526 whlp_set_font(h
, FONT_RULE
);
527 state
.cstate
= charset_init_state
;
528 #define TEN L"\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"
529 #define TWENTY TEN TEN
530 #define FORTY TWENTY TWENTY
531 #define EIGHTY FORTY FORTY
532 state
.cstate
= charset_init_state
;
533 whlp_wtext(&state
, EIGHTY
);
534 whlp_wtext(&state
, NULL
);
544 case para_DescribedThing
:
545 case para_Description
:
546 case para_BiblioCited
:
548 case para_NumberedList
:
549 whlp_para_attr(h
, WHLP_PARA_SPACEBELOW
, 12);
550 if (p
->type
== para_Bullet
|| p
->type
== para_NumberedList
) {
551 whlp_para_attr(h
, WHLP_PARA_LEFTINDENT
, 72*nesting
+ 72);
552 whlp_para_attr(h
, WHLP_PARA_FIRSTLINEINDENT
, -36);
553 whlp_set_tabstop(h
, 72, WHLP_ALIGN_LEFT
);
554 whlp_begin_para(h
, WHLP_PARA_SCROLL
);
555 whlp_set_font(h
, FONT_NORMAL
);
556 state
.cstate
= charset_init_state
;
557 if (p
->type
== para_Bullet
) {
558 whlp_wtext(&state
, conf
.bullet
);
560 whlp_mkparagraph(&state
, FONT_NORMAL
, p
->kwtext
, FALSE
, &conf
);
561 whlp_wtext(&state
, conf
.listsuffix
);
563 whlp_wtext(&state
, NULL
);
566 whlp_para_attr(h
, WHLP_PARA_LEFTINDENT
,
567 72*nesting
+ (p
->type
==para_Description ?
72 : 0));
568 whlp_begin_para(h
, WHLP_PARA_SCROLL
);
571 state
.cstate
= charset_init_state
;
573 if (p
->type
== para_BiblioCited
) {
574 whlp_mkparagraph(&state
, FONT_NORMAL
, p
->kwtext
, FALSE
, &conf
);
575 whlp_wtext(&state
, L
" ");
578 whlp_mkparagraph(&state
, FONT_NORMAL
, p
->words
, FALSE
, &conf
);
579 whlp_wtext(&state
, NULL
);
585 * In a code paragraph, each individual word is a line. For
586 * Help files, we will have to output this as a set of
587 * paragraphs, all but the last of which don't set
592 wchar_t *t
, *e
, *tmp
;
594 for (w
= p
->words
; w
; w
= w
->next
) if (w
->type
== word_WeakCode
) {
596 if (w
->next
&& w
->next
->type
== word_Emph
) {
603 whlp_para_attr(h
, WHLP_PARA_SPACEBELOW
, 12);
605 whlp_para_attr(h
, WHLP_PARA_LEFTINDENT
, 72*nesting
);
606 whlp_begin_para(h
, WHLP_PARA_SCROLL
);
607 state
.cstate
= charset_init_state
;
608 while (e
&& *e
&& *t
) {
612 for (n
= 0; t
[n
] && e
[n
] && e
[n
] == ec
; n
++);
614 whlp_set_font(h
, FONT_ITAL_CODE
);
616 whlp_set_font(h
, FONT_BOLD_CODE
);
618 whlp_set_font(h
, FONT_CODE
);
619 tmp
= snewn(n
+1, wchar_t);
622 whlp_wtext(&state
, tmp
);
623 whlp_wtext(&state
, NULL
);
624 state
.cstate
= charset_init_state
;
629 whlp_set_font(h
, FONT_CODE
);
630 whlp_wtext(&state
, t
);
631 whlp_wtext(&state
, NULL
);
639 whlp_close(h
, conf
.filename
);
642 * Loop over the index entries, cleaning up our final text
645 for (i
= 0; (ie
= index234(idx
->entries
, i
)) != NULL
; i
++) {
646 sfree(ie
->backend_data
);
649 sfree(conf
.filename
);
653 static void whlp_contents_write(struct bk_whlp_state
*state
,
654 int level
, char *text
, WHLP_TOPIC topic
) {
656 * Horrifying bug in WinHelp. When dropping a section level or
657 * more without using a folder-type entry, WinHelp accidentally
658 * adds one to the section level. So we correct for that here.
660 if (state
->cnt_last_level
> level
&& topic
)
661 state
->cnt_workaround
= -1;
663 state
->cnt_workaround
= 0;
664 state
->cnt_last_level
= level
;
666 fprintf(state
->cntfp
, "%d ", level
+ state
->cnt_workaround
);
669 fputc('\\', state
->cntfp
);
670 fputc(*text
, state
->cntfp
);
674 fprintf(state
->cntfp
, "=%s", whlp_topic_id(topic
));
675 fputc('\n', state
->cntfp
);
678 static void whlp_navmenu(struct bk_whlp_state
*state
, paragraph
*p
,
680 whlp_begin_para(state
->h
, WHLP_PARA_SCROLL
);
681 whlp_start_hyperlink(state
->h
, (WHLP_TOPIC
)p
->private_data
);
682 state
->cstate
= charset_init_state
;
684 whlp_mkparagraph(state
, FONT_NORMAL
, p
->kwtext
, TRUE
, conf
);
685 whlp_set_font(state
->h
, FONT_NORMAL
);
686 whlp_wtext(state
, conf
->sectsuffix
);
688 whlp_mkparagraph(state
, FONT_NORMAL
, p
->words
, TRUE
, conf
);
689 whlp_wtext(state
, NULL
);
690 whlp_end_hyperlink(state
->h
);
691 whlp_end_para(state
->h
);
695 static void whlp_mkparagraph(struct bk_whlp_state
*state
,
696 int font
, word
*text
, int subsidiary
,
702 paragraph
*xref_target
= NULL
;
704 for (; text
; text
= text
->next
) switch (text
->type
) {
710 if (subsidiary
) break; /* disabled in subsidiary bits */
712 indextag
*tag
= index_findtag(state
->idx
, text
->text
);
716 for (i
= 0; i
< tag
->nrefs
; i
++)
717 whlp_index_term(state
->h
, tag
->refs
[i
]->backend_data
,
724 if (subsidiary
) break; /* disabled in subsidiary bits */
725 kwl
= kw_lookup(state
->keywords
, text
->text
);
726 assert(xref_target
== NULL
);
728 if (kwl
->para
->type
== para_NumberedList
) {
729 break; /* don't xref to numbered list items */
730 } else if (kwl
->para
->type
== para_BiblioCited
) {
732 * An xref to a bibliography item jumps to the section
735 if (kwl
->para
->parent
)
736 xref_target
= kwl
->para
->parent
;
740 xref_target
= kwl
->para
;
742 whlp_start_hyperlink(state
->h
,
743 (WHLP_TOPIC
)xref_target
->private_data
);
748 if (subsidiary
) break; /* disabled in subsidiary bits */
750 whlp_end_hyperlink(state
->h
);
758 case word_WhiteSpace
:
761 case word_WkCodeSpace
:
765 case word_WkCodeQuote
:
766 if (towordstyle(text
->type
) == word_Emph
)
767 newfont
= deffont
+ FONT_EMPH
;
768 else if (towordstyle(text
->type
) == word_Code
||
769 towordstyle(text
->type
) == word_WeakCode
)
770 newfont
= deffont
+ FONT_CODE
;
773 if (newfont
!= currfont
) {
775 whlp_set_font(state
->h
, newfont
);
777 if (removeattr(text
->type
) == word_Normal
) {
778 if (cvt_ok(conf
->charset
, text
->text
) || !text
->alt
)
779 whlp_wtext(state
, text
->text
);
781 whlp_mkparagraph(state
, deffont
, text
->alt
, FALSE
, conf
);
782 } else if (removeattr(text
->type
) == word_WhiteSpace
) {
783 whlp_wtext(state
, L
" ");
784 } else if (removeattr(text
->type
) == word_Quote
) {
786 quoteaux(text
->aux
) == quote_Open ?
787 conf
->lquote
: conf
->rquote
);
793 static void whlp_rdaddwc(rdstringc
*rs
, word
*text
, whlpconf
*conf
,
794 charset_state
*state
) {
795 charset_state ourstate
= CHARSET_INIT_STATE
;
800 for (; text
; text
= text
->next
) switch (text
->type
) {
813 case word_WhiteSpace
:
816 case word_WkCodeSpace
:
820 case word_WkCodeQuote
:
821 assert(text
->type
!= word_CodeQuote
&&
822 text
->type
!= word_WkCodeQuote
);
823 if (removeattr(text
->type
) == word_Normal
) {
824 if (cvt_ok(conf
->charset
, text
->text
) || !text
->alt
)
825 whlp_rdadds(rs
, text
->text
, conf
, state
);
827 whlp_rdaddwc(rs
, text
->alt
, conf
, state
);
828 } else if (removeattr(text
->type
) == word_WhiteSpace
) {
829 whlp_rdadds(rs
, L
" ", conf
, state
);
830 } else if (removeattr(text
->type
) == word_Quote
) {
831 whlp_rdadds(rs
, quoteaux(text
->aux
) == quote_Open ?
832 conf
->lquote
: conf
->rquote
, conf
, state
);
837 if (state
== &ourstate
)
838 whlp_rdadds(rs
, NULL
, conf
, state
);
841 static void whlp_rdadds(rdstringc
*rs
, const wchar_t *text
, whlpconf
*conf
,
842 charset_state
*state
)
844 charset_state ourstate
= CHARSET_INIT_STATE
;
845 int textlen
= text ?
ustrlen(text
) : 0;
852 while (textlen
> 0 &&
853 (ret
= charset_from_unicode(&text
, &textlen
, outbuf
,
855 conf
->charset
, state
, NULL
)) > 0) {
860 if (text
== NULL
|| state
== &ourstate
) {
861 if ((ret
= charset_from_unicode(NULL
, 0, outbuf
, lenof(outbuf
)-1,
862 conf
->charset
, state
, NULL
)) > 0) {
869 static void whlp_wtext(struct bk_whlp_state
*state
, const wchar_t *text
)
871 int textlen
= text ?
ustrlen(text
) : 0;
875 while (textlen
> 0 &&
876 (ret
= charset_from_unicode(&text
, &textlen
, outbuf
,
878 state
->charset
, &state
->cstate
,
881 whlp_text(state
->h
, outbuf
);
885 if ((ret
= charset_from_unicode(NULL
, 0, outbuf
, lenof(outbuf
)-1,
886 state
->charset
, &state
->cstate
,
889 whlp_text(state
->h
, outbuf
);