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
;
31 * Indexes of fonts in our standard font descriptor set.
45 static void whlp_rdaddwc(rdstringc
*rs
, word
*text
, whlpconf
*conf
,
46 charset_state
*state
);
47 static void whlp_rdadds(rdstringc
*rs
, const wchar_t *text
, whlpconf
*conf
,
48 charset_state
*state
);
49 static void whlp_mkparagraph(struct bk_whlp_state
*state
,
50 int font
, word
*text
, int subsidiary
,
52 static void whlp_navmenu(struct bk_whlp_state
*state
, paragraph
*p
,
54 static void whlp_contents_write(struct bk_whlp_state
*state
,
55 int level
, char *text
, WHLP_TOPIC topic
);
56 static void whlp_wtext(struct bk_whlp_state
*state
, const wchar_t *text
);
58 paragraph
*whlp_config_filename(char *filename
)
60 return cmdline_cfg_simple("winhelp-filename", filename
, NULL
);
63 static whlpconf
whlp_configure(paragraph
*source
) {
70 ret
.charset
= CS_CP1252
;
71 ret
.bullet
= L
"\x2022\0-\0\0";
72 ret
.lquote
= L
"\x2018\0\x2019\0\"\0\"\0\0";
73 ret
.rquote
= uadv(ret
.lquote
);
74 ret
.filename
= dupstr("output.hlp");
75 ret
.titlepage
= L
"Title page";
76 ret
.sectsuffix
= L
": ";
77 ret
.listsuffix
= L
".";
80 * Two-pass configuration so that we can pick up global config
81 * (e.g. `quotes') before having it overridden by specific
82 * config (`win-quotes'), irrespective of the order in which
85 for (p
= source
; p
; p
= p
->next
) {
86 if (p
->type
== para_Config
) {
87 if (!ustricmp(p
->keyword
, L
"quotes")) {
88 if (*uadv(p
->keyword
) && *uadv(uadv(p
->keyword
))) {
89 ret
.lquote
= uadv(p
->keyword
);
90 ret
.rquote
= uadv(ret
.lquote
);
96 for (p
= source
; p
; p
= p
->next
) {
97 p
->private_data
= NULL
;
98 if (p
->type
== para_Config
) {
100 * In principle we should support a `winhelp-charset'
101 * here. We don't, because my WinHelp output code
102 * doesn't know how to change character set. Once I
103 * find out, I'll support it.
105 if (p
->parent
&& !ustricmp(p
->keyword
, L
"winhelp-topic")) {
106 /* Store the topic name in the private_data field of the
107 * containing section. */
108 p
->parent
->private_data
= uadv(p
->keyword
);
109 } else if (!ustricmp(p
->keyword
, L
"winhelp-filename")) {
111 ret
.filename
= dupstr(adv(p
->origkeyword
));
112 } else if (!ustricmp(p
->keyword
, L
"winhelp-bullet")) {
113 ret
.bullet
= uadv(p
->keyword
);
114 } else if (!ustricmp(p
->keyword
, L
"winhelp-section-suffix")) {
115 ret
.sectsuffix
= uadv(p
->keyword
);
116 } else if (!ustricmp(p
->keyword
, L
"winhelp-list-suffix")) {
117 ret
.listsuffix
= uadv(p
->keyword
);
118 } else if (!ustricmp(p
->keyword
, L
"winhelp-contents-titlepage")) {
119 ret
.titlepage
= uadv(p
->keyword
);
120 } else if (!ustricmp(p
->keyword
, L
"winhelp-quotes")) {
121 if (*uadv(p
->keyword
) && *uadv(uadv(p
->keyword
))) {
122 ret
.lquote
= uadv(p
->keyword
);
123 ret
.rquote
= uadv(ret
.lquote
);
130 * Now process fallbacks on quote characters and bullets.
132 while (*uadv(ret
.rquote
) && *uadv(uadv(ret
.rquote
)) &&
133 (!cvt_ok(ret
.charset
, ret
.lquote
) ||
134 !cvt_ok(ret
.charset
, ret
.rquote
))) {
135 ret
.lquote
= uadv(ret
.rquote
);
136 ret
.rquote
= uadv(ret
.lquote
);
139 while (*ret
.bullet
&& *uadv(ret
.bullet
) &&
140 !cvt_ok(ret
.charset
, ret
.bullet
))
141 ret
.bullet
= uadv(ret
.bullet
);
146 void whlp_backend(paragraph
*sourceform
, keywordlist
*keywords
,
147 indexdata
*idx
, void *unused
) {
150 paragraph
*p
, *lastsect
;
151 struct bk_whlp_state state
;
152 WHLP_TOPIC contents_topic
;
156 int done_contents_topic
= FALSE
;
161 h
= state
.h
= whlp_new();
162 state
.keywords
= keywords
;
165 whlp_start_macro(h
, "CB(\"btn_about\",\"&About\",\"About()\")");
166 whlp_start_macro(h
, "CB(\"btn_up\",\"&Up\",\"Contents()\")");
167 whlp_start_macro(h
, "BrowseButtons()");
169 whlp_create_font(h
, "Times New Roman", WHLP_FONTFAM_SERIF
, 24,
171 whlp_create_font(h
, "Times New Roman", WHLP_FONTFAM_SERIF
, 24,
172 WHLP_FONT_ITALIC
, 0, 0, 0);
173 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_FIXED
, 24,
175 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_FIXED
, 24,
176 WHLP_FONT_ITALIC
, 0, 0, 0);
177 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_FIXED
, 24,
178 WHLP_FONT_BOLD
, 0, 0, 0);
179 whlp_create_font(h
, "Arial", WHLP_FONTFAM_SERIF
, 30,
180 WHLP_FONT_BOLD
, 0, 0, 0);
181 whlp_create_font(h
, "Arial", WHLP_FONTFAM_SERIF
, 30,
182 WHLP_FONT_BOLD
|WHLP_FONT_ITALIC
, 0, 0, 0);
183 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_FIXED
, 30,
184 WHLP_FONT_BOLD
, 0, 0, 0);
185 whlp_create_font(h
, "Courier New", WHLP_FONTFAM_SANS
, 18,
186 WHLP_FONT_STRIKEOUT
, 0, 0, 0);
188 conf
= whlp_configure(sourceform
);
190 state
.charset
= conf
.charset
;
193 * Ensure the output file name has a .hlp extension. This is
194 * required since we must create the .cnt file in parallel with
198 int len
= strlen(conf
.filename
);
199 if (len
< 4 || conf
.filename
[len
-4] != '.' ||
200 tolower(conf
.filename
[len
-3] != 'h') ||
201 tolower(conf
.filename
[len
-2] != 'l') ||
202 tolower(conf
.filename
[len
-1] != 'p')) {
204 newf
= snewn(len
+ 5, char);
205 sprintf(newf
, "%s.hlp", conf
.filename
);
206 sfree(conf
.filename
);
207 conf
.filename
= newf
;
210 cntname
= snewn(len
+1, char);
211 sprintf(cntname
, "%.*s.cnt", len
-4, conf
.filename
);
214 state
.cntfp
= fopen(cntname
, "wb");
216 error(err_cantopenw
, cntname
);
219 state
.cnt_last_level
= -1; state
.cnt_workaround
= 0;
222 * Loop over the source form registering WHLP_TOPICs for
226 contents_topic
= whlp_register_topic(h
, "Top", NULL
);
227 whlp_primary_topic(h
, contents_topic
);
228 for (p
= sourceform
; p
; p
= p
->next
) {
229 if (p
->type
== para_Chapter
||
230 p
->type
== para_Appendix
||
231 p
->type
== para_UnnumberedChapter
||
232 p
->type
== para_Heading
||
233 p
->type
== para_Subsect
) {
235 rdstringc rs
= { 0, 0, NULL
};
238 whlp_rdadds(&rs
, (wchar_t *)p
->private_data
, &conf
, NULL
);
240 p
->private_data
= whlp_register_topic(h
, rs
.text
, &errstr
);
241 if (!p
->private_data
) {
242 p
->private_data
= whlp_register_topic(h
, NULL
, NULL
);
243 error(err_winhelp_ctxclash
, &p
->fpos
, rs
.text
, errstr
);
250 * Loop over the index entries, preparing final text forms for
254 indexentry
*ie_prev
= NULL
;
257 for (i
= 0; (ie
= index234(idx
->entries
, i
)) != NULL
; i
++) {
258 rdstringc rs
= {0, 0, NULL
};
259 charset_state state
= CHARSET_INIT_STATE
;
260 whlp_rdaddwc(&rs
, ie
->text
, &conf
, &state
);
264 * It appears that Windows Help's index mechanism
265 * is inherently case-insensitive. Therefore, if two
266 * adjacent index terms compare equal apart from
267 * case, I'm going to append nonbreaking spaces to
268 * the end of the second one so that Windows will
269 * treat them as distinct.
271 * This is nasty because we're depending on our
272 * case-insensitive comparison having the same
273 * semantics as the Windows one :-/ but I see no
278 a
= ufroma_dup((char *)ie_prev
->backend_data
, conf
.charset
);
279 b
= ufroma_dup(rs
.text
, conf
.charset
);
280 if (!ustricmp(a
, b
)) {
282 for (j
= 0; j
< nspaces
; j
++)
283 whlp_rdadds(&rs
, L
"\xA0", &conf
, &state
);
285 * Add one to nspaces, so that if another term
286 * appears which is equivalent to the previous
287 * two it'll acquire one more space.
296 whlp_rdadds(&rs
, NULL
, &conf
, &state
);
298 ie
->backend_data
= rs
.text
;
301 * Only move ie_prev on if nspaces==1 (since when we
302 * have three or more adjacent terms differing only in
303 * case, we will want to compare with the _first_ of
304 * them because that won't have had any extra spaces
305 * added on which will foul up the comparison).
314 /* ------------------------------------------------------------------
315 * Begin the contents page.
318 whlp_begin_topic(h
, contents_topic
, "Contents", "DB(\"btn_up\")", NULL
);
319 state
.curr_topic
= contents_topic
;
322 * The manual title goes in the non-scroll region, and also
323 * goes into the system title slot.
326 rdstringc rs
= {0, 0, NULL
};
327 for (p
= sourceform
; p
; p
= p
->next
) {
328 if (p
->type
== para_Title
) {
329 whlp_begin_para(h
, WHLP_PARA_NONSCROLL
);
330 state
.cstate
= charset_init_state
;
331 whlp_mkparagraph(&state
, FONT_TITLE
, p
->words
, FALSE
, &conf
);
332 whlp_wtext(&state
, NULL
);
334 whlp_rdaddwc(&rs
, p
->words
, &conf
, NULL
);
338 whlp_title(h
, rs
.text
);
339 fprintf(state
.cntfp
, ":Title %s\r\n", rs
.text
);
343 rdstringc rs2
= {0,0,NULL
};
344 whlp_rdadds(&rs2
, conf
.titlepage
, &conf
, NULL
);
345 whlp_contents_write(&state
, 1, rs2
.text
, contents_topic
);
351 * Put the copyright into the system section.
354 rdstringc rs
= {0, 0, NULL
};
355 for (p
= sourceform
; p
; p
= p
->next
) {
356 if (p
->type
== para_Copyright
)
357 whlp_rdaddwc(&rs
, p
->words
, &conf
, NULL
);
360 whlp_copyright(h
, rs
.text
);
367 /* ------------------------------------------------------------------
368 * Now we've done the contents page, we're ready to go through
369 * and do the main manual text. Ooh.
372 for (p
= sourceform
; p
; p
= p
->next
) switch (p
->type
) {
374 * Things we ignore because we've already processed them or
375 * aren't going to touch them in this pass.
379 case para_Biblio
: /* only touch BiblioCited */
396 * Chapter and section titles: start a new Help topic.
400 case para_UnnumberedChapter
:
404 if (!done_contents_topic
) {
408 * If this is the first section title we've seen, then
409 * we're currently still in the contents topic. We
410 * should therefore finish up the contents page by
411 * writing a nav menu.
413 for (p
= sourceform
; p
; p
= p
->next
) {
414 if (p
->type
== para_Chapter
||
415 p
->type
== para_Appendix
||
416 p
->type
== para_UnnumberedChapter
)
417 whlp_navmenu(&state
, p
, &conf
);
420 done_contents_topic
= TRUE
;
423 if (lastsect
&& lastsect
->child
) {
426 * Do a navigation menu for the previous section we
429 for (q
= lastsect
->child
; q
; q
= q
->sibling
)
430 whlp_navmenu(&state
, q
, &conf
);
433 rdstringc rs
= {0, 0, NULL
};
434 WHLP_TOPIC new_topic
, parent_topic
;
435 char *macro
, *topicid
;
436 charset_state cstate
= CHARSET_INIT_STATE
;
438 new_topic
= p
->private_data
;
439 whlp_browse_link(h
, state
.curr_topic
, new_topic
);
440 state
.curr_topic
= new_topic
;
443 whlp_rdaddwc(&rs
, p
->kwtext
, &conf
, &cstate
);
444 whlp_rdadds(&rs
, conf
.sectsuffix
, &conf
, &cstate
);
446 whlp_rdaddwc(&rs
, p
->words
, &conf
, &cstate
);
447 whlp_rdadds(&rs
, NULL
, &conf
, &cstate
);
449 if (p
->parent
== NULL
)
450 parent_topic
= contents_topic
;
452 parent_topic
= (WHLP_TOPIC
)p
->parent
->private_data
;
453 topicid
= whlp_topic_id(parent_topic
);
454 macro
= smalloc(100+strlen(topicid
));
456 "CBB(\"btn_up\",\"JI(`',`%s')\");EB(\"btn_up\")",
458 whlp_begin_topic(h
, new_topic
,
459 rs
.text ? rs
.text
: "",
465 * Output the .cnt entry.
467 * WinHelp has a bug involving having an internal
468 * node followed by a leaf at the same level: the
469 * leaf is output at the wrong level. We can mostly
470 * work around this by modifying the leaf level
471 * itself (see whlp_contents_write), but this
472 * doesn't work for top-level sections since we
473 * can't turn a level-1 leaf into a level-0 one. So
474 * for top-level leaf sections (Bibliography
475 * springs to mind), we output an internal node
476 * containing only the leaf for that section.
481 /* Count up the level. */
483 for (q
= p
; q
->parent
; q
= q
->parent
) i
++;
485 if (p
->child
|| !p
->parent
) {
487 * If p has children then it needs to be a
488 * folder; if it has no parent then it needs to
489 * be a folder to work around the bug.
491 whlp_contents_write(&state
, i
, rs
.text
, NULL
);
494 whlp_contents_write(&state
, i
, rs
.text
, new_topic
);
499 whlp_begin_para(h
, WHLP_PARA_NONSCROLL
);
500 state
.cstate
= charset_init_state
;
502 whlp_mkparagraph(&state
, FONT_TITLE
, p
->kwtext
, FALSE
, &conf
);
503 whlp_set_font(h
, FONT_TITLE
);
504 whlp_wtext(&state
, conf
.sectsuffix
);
506 whlp_mkparagraph(&state
, FONT_TITLE
, p
->words
, FALSE
, &conf
);
507 whlp_wtext(&state
, NULL
);
515 whlp_para_attr(h
, WHLP_PARA_SPACEBELOW
, 12);
516 whlp_para_attr(h
, WHLP_PARA_ALIGNMENT
, WHLP_ALIGN_CENTRE
);
517 whlp_begin_para(h
, WHLP_PARA_SCROLL
);
518 whlp_set_font(h
, FONT_RULE
);
519 state
.cstate
= charset_init_state
;
520 #define TEN L"\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"
521 #define TWENTY TEN TEN
522 #define FORTY TWENTY TWENTY
523 #define EIGHTY FORTY FORTY
524 state
.cstate
= charset_init_state
;
525 whlp_wtext(&state
, EIGHTY
);
526 whlp_wtext(&state
, NULL
);
536 case para_DescribedThing
:
537 case para_Description
:
538 case para_BiblioCited
:
540 case para_NumberedList
:
541 whlp_para_attr(h
, WHLP_PARA_SPACEBELOW
, 12);
542 if (p
->type
== para_Bullet
|| p
->type
== para_NumberedList
) {
543 whlp_para_attr(h
, WHLP_PARA_LEFTINDENT
, 72*nesting
+ 72);
544 whlp_para_attr(h
, WHLP_PARA_FIRSTLINEINDENT
, -36);
545 whlp_set_tabstop(h
, 72, WHLP_ALIGN_LEFT
);
546 whlp_begin_para(h
, WHLP_PARA_SCROLL
);
547 whlp_set_font(h
, FONT_NORMAL
);
548 state
.cstate
= charset_init_state
;
549 if (p
->type
== para_Bullet
) {
550 whlp_wtext(&state
, conf
.bullet
);
552 whlp_mkparagraph(&state
, FONT_NORMAL
, p
->kwtext
, FALSE
, &conf
);
553 whlp_wtext(&state
, conf
.listsuffix
);
555 whlp_wtext(&state
, NULL
);
558 whlp_para_attr(h
, WHLP_PARA_LEFTINDENT
,
559 72*nesting
+ (p
->type
==para_Description ?
72 : 0));
560 whlp_begin_para(h
, WHLP_PARA_SCROLL
);
563 state
.cstate
= charset_init_state
;
565 if (p
->type
== para_BiblioCited
) {
566 whlp_mkparagraph(&state
, FONT_NORMAL
, p
->kwtext
, FALSE
, &conf
);
567 whlp_wtext(&state
, L
" ");
570 whlp_mkparagraph(&state
, FONT_NORMAL
, p
->words
, FALSE
, &conf
);
571 whlp_wtext(&state
, NULL
);
577 * In a code paragraph, each individual word is a line. For
578 * Help files, we will have to output this as a set of
579 * paragraphs, all but the last of which don't set
584 wchar_t *t
, *e
, *tmp
;
586 for (w
= p
->words
; w
; w
= w
->next
) if (w
->type
== word_WeakCode
) {
588 if (w
->next
&& w
->next
->type
== word_Emph
) {
595 whlp_para_attr(h
, WHLP_PARA_SPACEBELOW
, 12);
597 whlp_para_attr(h
, WHLP_PARA_LEFTINDENT
, 72*nesting
);
598 whlp_begin_para(h
, WHLP_PARA_SCROLL
);
599 state
.cstate
= charset_init_state
;
600 while (e
&& *e
&& *t
) {
604 for (n
= 0; t
[n
] && e
[n
] && e
[n
] == ec
; n
++);
606 whlp_set_font(h
, FONT_ITAL_CODE
);
608 whlp_set_font(h
, FONT_BOLD_CODE
);
610 whlp_set_font(h
, FONT_CODE
);
611 tmp
= snewn(n
+1, wchar_t);
614 whlp_wtext(&state
, tmp
);
615 whlp_wtext(&state
, NULL
);
616 state
.cstate
= charset_init_state
;
621 whlp_set_font(h
, FONT_CODE
);
622 whlp_wtext(&state
, t
);
623 whlp_wtext(&state
, NULL
);
631 whlp_close(h
, conf
.filename
);
634 * Loop over the index entries, cleaning up our final text
637 for (i
= 0; (ie
= index234(idx
->entries
, i
)) != NULL
; i
++) {
638 sfree(ie
->backend_data
);
641 sfree(conf
.filename
);
645 static void whlp_contents_write(struct bk_whlp_state
*state
,
646 int level
, char *text
, WHLP_TOPIC topic
) {
648 * Horrifying bug in WinHelp. When dropping a section level or
649 * more without using a folder-type entry, WinHelp accidentally
650 * adds one to the section level. So we correct for that here.
652 if (state
->cnt_last_level
> level
&& topic
)
653 state
->cnt_workaround
= -1;
655 state
->cnt_workaround
= 0;
656 state
->cnt_last_level
= level
;
658 fprintf(state
->cntfp
, "%d ", level
+ state
->cnt_workaround
);
661 fputc('\\', state
->cntfp
);
662 fputc(*text
, state
->cntfp
);
666 fprintf(state
->cntfp
, "=%s", whlp_topic_id(topic
));
667 fputc('\n', state
->cntfp
);
670 static void whlp_navmenu(struct bk_whlp_state
*state
, paragraph
*p
,
672 whlp_begin_para(state
->h
, WHLP_PARA_SCROLL
);
673 whlp_start_hyperlink(state
->h
, (WHLP_TOPIC
)p
->private_data
);
674 state
->cstate
= charset_init_state
;
676 whlp_mkparagraph(state
, FONT_NORMAL
, p
->kwtext
, TRUE
, conf
);
677 whlp_set_font(state
->h
, FONT_NORMAL
);
678 whlp_wtext(state
, conf
->sectsuffix
);
680 whlp_mkparagraph(state
, FONT_NORMAL
, p
->words
, TRUE
, conf
);
681 whlp_wtext(state
, NULL
);
682 whlp_end_hyperlink(state
->h
);
683 whlp_end_para(state
->h
);
687 static void whlp_mkparagraph(struct bk_whlp_state
*state
,
688 int font
, word
*text
, int subsidiary
,
694 paragraph
*xref_target
= NULL
;
696 for (; text
; text
= text
->next
) switch (text
->type
) {
702 if (subsidiary
) break; /* disabled in subsidiary bits */
704 indextag
*tag
= index_findtag(state
->idx
, text
->text
);
708 for (i
= 0; i
< tag
->nrefs
; i
++)
709 whlp_index_term(state
->h
, tag
->refs
[i
]->backend_data
,
716 if (subsidiary
) break; /* disabled in subsidiary bits */
717 kwl
= kw_lookup(state
->keywords
, text
->text
);
718 assert(xref_target
== NULL
);
720 if (kwl
->para
->type
== para_NumberedList
) {
721 break; /* don't xref to numbered list items */
722 } else if (kwl
->para
->type
== para_BiblioCited
) {
724 * An xref to a bibliography item jumps to the section
727 if (kwl
->para
->parent
)
728 xref_target
= kwl
->para
->parent
;
732 xref_target
= kwl
->para
;
734 whlp_start_hyperlink(state
->h
,
735 (WHLP_TOPIC
)xref_target
->private_data
);
740 if (subsidiary
) break; /* disabled in subsidiary bits */
742 whlp_end_hyperlink(state
->h
);
750 case word_WhiteSpace
:
753 case word_WkCodeSpace
:
757 case word_WkCodeQuote
:
758 if (towordstyle(text
->type
) == word_Emph
)
759 newfont
= deffont
+ FONT_EMPH
;
760 else if (towordstyle(text
->type
) == word_Code
||
761 towordstyle(text
->type
) == word_WeakCode
)
762 newfont
= deffont
+ FONT_CODE
;
765 if (newfont
!= currfont
) {
767 whlp_set_font(state
->h
, newfont
);
769 if (removeattr(text
->type
) == word_Normal
) {
770 if (cvt_ok(conf
->charset
, text
->text
) || !text
->alt
)
771 whlp_wtext(state
, text
->text
);
773 whlp_mkparagraph(state
, deffont
, text
->alt
, FALSE
, conf
);
774 } else if (removeattr(text
->type
) == word_WhiteSpace
) {
775 whlp_wtext(state
, L
" ");
776 } else if (removeattr(text
->type
) == word_Quote
) {
778 quoteaux(text
->aux
) == quote_Open ?
779 conf
->lquote
: conf
->rquote
);
785 static void whlp_rdaddwc(rdstringc
*rs
, word
*text
, whlpconf
*conf
,
786 charset_state
*state
) {
787 charset_state ourstate
= CHARSET_INIT_STATE
;
792 for (; text
; text
= text
->next
) switch (text
->type
) {
805 case word_WhiteSpace
:
808 case word_WkCodeSpace
:
812 case word_WkCodeQuote
:
813 assert(text
->type
!= word_CodeQuote
&&
814 text
->type
!= word_WkCodeQuote
);
815 if (removeattr(text
->type
) == word_Normal
) {
816 if (cvt_ok(conf
->charset
, text
->text
) || !text
->alt
)
817 whlp_rdadds(rs
, text
->text
, conf
, state
);
819 whlp_rdaddwc(rs
, text
->alt
, conf
, state
);
820 } else if (removeattr(text
->type
) == word_WhiteSpace
) {
821 whlp_rdadds(rs
, L
" ", conf
, state
);
822 } else if (removeattr(text
->type
) == word_Quote
) {
823 whlp_rdadds(rs
, quoteaux(text
->aux
) == quote_Open ?
824 conf
->lquote
: conf
->rquote
, conf
, state
);
829 if (state
== &ourstate
)
830 whlp_rdadds(rs
, NULL
, conf
, state
);
833 static void whlp_rdadds(rdstringc
*rs
, const wchar_t *text
, whlpconf
*conf
,
834 charset_state
*state
)
836 charset_state ourstate
= CHARSET_INIT_STATE
;
837 int textlen
= text ?
ustrlen(text
) : 0;
844 while (textlen
> 0 &&
845 (ret
= charset_from_unicode(&text
, &textlen
, outbuf
,
847 conf
->charset
, state
, NULL
)) > 0) {
852 if (text
== NULL
|| state
== &ourstate
) {
853 if ((ret
= charset_from_unicode(NULL
, 0, outbuf
, lenof(outbuf
)-1,
854 conf
->charset
, state
, NULL
)) > 0) {
861 static void whlp_wtext(struct bk_whlp_state
*state
, const wchar_t *text
)
863 int textlen
= text ?
ustrlen(text
) : 0;
867 while (textlen
> 0 &&
868 (ret
= charset_from_unicode(&text
, &textlen
, outbuf
,
870 state
->charset
, &state
->cstate
,
873 whlp_text(state
->h
, outbuf
);
877 if ((ret
= charset_from_unicode(NULL
, 0, outbuf
, lenof(outbuf
)-1,
878 state
->charset
, &state
->cstate
,
881 whlp_text(state
->h
, outbuf
);