Failed to initialise state.curr_topic early enough, causing
[sgt/halibut] / bk_whlp.c
1 /*
2 * Windows Help backend for Halibut
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <ctype.h>
8 #include <assert.h>
9
10 #include "halibut.h"
11 #include "winhelp.h"
12
13 struct bk_whlp_state {
14 WHLP h;
15 indexdata *idx;
16 keywordlist *keywords;
17 WHLP_TOPIC curr_topic;
18 int charset;
19 charset_state cstate;
20 FILE *cntfp;
21 int cnt_last_level, cnt_workaround;
22 };
23
24 typedef struct {
25 int charset;
26 wchar_t *bullet, *lquote, *rquote, *titlepage, *sectsuffix, *listsuffix;
27 char *filename;
28 } whlpconf;
29
30 /*
31 * Indexes of fonts in our standard font descriptor set.
32 */
33 enum {
34 FONT_NORMAL,
35 FONT_EMPH,
36 FONT_CODE,
37 FONT_ITAL_CODE,
38 FONT_BOLD_CODE,
39 FONT_TITLE,
40 FONT_TITLE_EMPH,
41 FONT_TITLE_CODE,
42 FONT_RULE
43 };
44
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,
51 whlpconf *conf);
52 static void whlp_navmenu(struct bk_whlp_state *state, paragraph *p,
53 whlpconf *conf);
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);
57
58 paragraph *whlp_config_filename(char *filename)
59 {
60 return cmdline_cfg_simple("winhelp-filename", filename, NULL);
61 }
62
63 static whlpconf whlp_configure(paragraph *source) {
64 paragraph *p;
65 whlpconf ret;
66
67 /*
68 * Defaults.
69 */
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".";
78
79 /*
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
83 * they occur.
84 */
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);
91 }
92 }
93 }
94 }
95
96 for (p = source; p; p = p->next) {
97 p->private_data = NULL;
98 if (p->type == para_Config) {
99 /*
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.
104 */
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")) {
110 sfree(ret.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);
124 }
125 }
126 }
127 }
128
129 /*
130 * Now process fallbacks on quote characters and bullets.
131 */
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);
137 }
138
139 while (*ret.bullet && *uadv(ret.bullet) &&
140 !cvt_ok(ret.charset, ret.bullet))
141 ret.bullet = uadv(ret.bullet);
142
143 return ret;
144 }
145
146 void whlp_backend(paragraph *sourceform, keywordlist *keywords,
147 indexdata *idx, void *unused) {
148 WHLP h;
149 char *cntname;
150 paragraph *p, *lastsect;
151 struct bk_whlp_state state;
152 WHLP_TOPIC contents_topic;
153 int i;
154 int nesting;
155 indexentry *ie;
156 int done_contents_topic = FALSE;
157 whlpconf conf;
158
159 IGNORE(unused);
160
161 h = state.h = whlp_new();
162 state.keywords = keywords;
163 state.idx = idx;
164
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()");
168
169 whlp_create_font(h, "Times New Roman", WHLP_FONTFAM_SERIF, 24,
170 0, 0, 0, 0);
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,
174 0, 0, 0, 0);
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);
187
188 conf = whlp_configure(sourceform);
189
190 state.charset = conf.charset;
191
192 /*
193 * Ensure the output file name has a .hlp extension. This is
194 * required since we must create the .cnt file in parallel with
195 * it.
196 */
197 {
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')) {
203 char *newf;
204 newf = snewn(len + 5, char);
205 sprintf(newf, "%s.hlp", conf.filename);
206 sfree(conf.filename);
207 conf.filename = newf;
208 len = strlen(newf);
209 }
210 cntname = snewn(len+1, char);
211 sprintf(cntname, "%.*s.cnt", len-4, conf.filename);
212 }
213
214 state.cntfp = fopen(cntname, "wb");
215 state.cnt_last_level = -1; state.cnt_workaround = 0;
216
217 /*
218 * Loop over the source form registering WHLP_TOPICs for
219 * everything.
220 */
221
222 contents_topic = whlp_register_topic(h, "Top", NULL);
223 whlp_primary_topic(h, contents_topic);
224 for (p = sourceform; p; p = p->next) {
225 if (p->type == para_Chapter ||
226 p->type == para_Appendix ||
227 p->type == para_UnnumberedChapter ||
228 p->type == para_Heading ||
229 p->type == para_Subsect) {
230
231 rdstringc rs = { 0, 0, NULL };
232 char *errstr;
233
234 whlp_rdadds(&rs, (wchar_t *)p->private_data, &conf, NULL);
235
236 p->private_data = whlp_register_topic(h, rs.text, &errstr);
237 if (!p->private_data) {
238 p->private_data = whlp_register_topic(h, NULL, NULL);
239 error(err_winhelp_ctxclash, &p->fpos, rs.text, errstr);
240 }
241 sfree(rs.text);
242 }
243 }
244
245 /*
246 * Loop over the index entries, preparing final text forms for
247 * each one.
248 */
249 {
250 indexentry *ie_prev = NULL;
251 int nspaces = 1;
252
253 for (i = 0; (ie = index234(idx->entries, i)) != NULL; i++) {
254 rdstringc rs = {0, 0, NULL};
255 charset_state state = CHARSET_INIT_STATE;
256 whlp_rdaddwc(&rs, ie->text, &conf, &state);
257
258 if (ie_prev) {
259 /*
260 * It appears that Windows Help's index mechanism
261 * is inherently case-insensitive. Therefore, if two
262 * adjacent index terms compare equal apart from
263 * case, I'm going to append nonbreaking spaces to
264 * the end of the second one so that Windows will
265 * treat them as distinct.
266 *
267 * This is nasty because we're depending on our
268 * case-insensitive comparison having the same
269 * semantics as the Windows one :-/ but I see no
270 * alternative.
271 */
272 wchar_t *a, *b;
273
274 a = ufroma_dup((char *)ie_prev->backend_data, conf.charset);
275 b = ufroma_dup(rs.text, conf.charset);
276 if (!ustricmp(a, b)) {
277 int j;
278 for (j = 0; j < nspaces; j++)
279 whlp_rdadds(&rs, L"\xA0", &conf, &state);
280 /*
281 * Add one to nspaces, so that if another term
282 * appears which is equivalent to the previous
283 * two it'll acquire one more space.
284 */
285 nspaces++;
286 } else
287 nspaces = 1;
288 sfree(a);
289 sfree(b);
290 }
291
292 whlp_rdadds(&rs, NULL, &conf, &state);
293
294 ie->backend_data = rs.text;
295
296 /*
297 * Only move ie_prev on if nspaces==1 (since when we
298 * have three or more adjacent terms differing only in
299 * case, we will want to compare with the _first_ of
300 * them because that won't have had any extra spaces
301 * added on which will foul up the comparison).
302 */
303 if (nspaces == 1)
304 ie_prev = ie;
305 }
306 }
307
308 whlp_prepare(h);
309
310 /* ------------------------------------------------------------------
311 * Begin the contents page.
312 */
313
314 whlp_begin_topic(h, contents_topic, "Contents", "DB(\"btn_up\")", NULL);
315 state.curr_topic = contents_topic;
316
317 /*
318 * The manual title goes in the non-scroll region, and also
319 * goes into the system title slot.
320 */
321 {
322 rdstringc rs = {0, 0, NULL};
323 for (p = sourceform; p; p = p->next) {
324 if (p->type == para_Title) {
325 whlp_begin_para(h, WHLP_PARA_NONSCROLL);
326 state.cstate = charset_init_state;
327 whlp_mkparagraph(&state, FONT_TITLE, p->words, FALSE, &conf);
328 whlp_wtext(&state, NULL);
329 whlp_end_para(h);
330 whlp_rdaddwc(&rs, p->words, &conf, NULL);
331 }
332 }
333 if (rs.text) {
334 whlp_title(h, rs.text);
335 fprintf(state.cntfp, ":Title %s\r\n", rs.text);
336 sfree(rs.text);
337 }
338 {
339 rdstringc rs2 = {0,0,NULL};
340 whlp_rdadds(&rs2, conf.titlepage, &conf, NULL);
341 whlp_contents_write(&state, 1, rs2.text, contents_topic);
342 sfree(rs2.text);
343 }
344 }
345
346 /*
347 * Put the copyright into the system section.
348 */
349 {
350 rdstringc rs = {0, 0, NULL};
351 for (p = sourceform; p; p = p->next) {
352 if (p->type == para_Copyright)
353 whlp_rdaddwc(&rs, p->words, &conf, NULL);
354 }
355 if (rs.text) {
356 whlp_copyright(h, rs.text);
357 sfree(rs.text);
358 }
359 }
360
361 lastsect = NULL;
362
363 /* ------------------------------------------------------------------
364 * Now we've done the contents page, we're ready to go through
365 * and do the main manual text. Ooh.
366 */
367 nesting = 0;
368 for (p = sourceform; p; p = p->next) switch (p->type) {
369 /*
370 * Things we ignore because we've already processed them or
371 * aren't going to touch them in this pass.
372 */
373 case para_IM:
374 case para_BR:
375 case para_Biblio: /* only touch BiblioCited */
376 case para_VersionID:
377 case para_NoCite:
378 case para_Title:
379 break;
380
381 case para_LcontPush:
382 case para_QuotePush:
383 nesting++;
384 break;
385 case para_LcontPop:
386 case para_QuotePop:
387 assert(nesting > 0);
388 nesting--;
389 break;
390
391 /*
392 * Chapter and section titles: start a new Help topic.
393 */
394 case para_Chapter:
395 case para_Appendix:
396 case para_UnnumberedChapter:
397 case para_Heading:
398 case para_Subsect:
399
400 if (!done_contents_topic) {
401 paragraph *p;
402
403 /*
404 * If this is the first section title we've seen, then
405 * we're currently still in the contents topic. We
406 * should therefore finish up the contents page by
407 * writing a nav menu.
408 */
409 for (p = sourceform; p; p = p->next) {
410 if (p->type == para_Chapter ||
411 p->type == para_Appendix ||
412 p->type == para_UnnumberedChapter)
413 whlp_navmenu(&state, p, &conf);
414 }
415
416 done_contents_topic = TRUE;
417 }
418
419 if (lastsect && lastsect->child) {
420 paragraph *q;
421 /*
422 * Do a navigation menu for the previous section we
423 * were in.
424 */
425 for (q = lastsect->child; q; q = q->sibling)
426 whlp_navmenu(&state, q, &conf);
427 }
428 {
429 rdstringc rs = {0, 0, NULL};
430 WHLP_TOPIC new_topic, parent_topic;
431 char *macro, *topicid;
432 charset_state cstate = CHARSET_INIT_STATE;
433
434 new_topic = p->private_data;
435 whlp_browse_link(h, state.curr_topic, new_topic);
436 state.curr_topic = new_topic;
437
438 if (p->kwtext) {
439 whlp_rdaddwc(&rs, p->kwtext, &conf, &cstate);
440 whlp_rdadds(&rs, conf.sectsuffix, &conf, &cstate);
441 }
442 whlp_rdaddwc(&rs, p->words, &conf, &cstate);
443 whlp_rdadds(&rs, NULL, &conf, &cstate);
444
445 if (p->parent == NULL)
446 parent_topic = contents_topic;
447 else
448 parent_topic = (WHLP_TOPIC)p->parent->private_data;
449 topicid = whlp_topic_id(parent_topic);
450 macro = smalloc(100+strlen(topicid));
451 sprintf(macro,
452 "CBB(\"btn_up\",\"JI(`',`%s')\");EB(\"btn_up\")",
453 topicid);
454 whlp_begin_topic(h, new_topic,
455 rs.text ? rs.text : "",
456 macro, NULL);
457 sfree(macro);
458
459 {
460 /*
461 * Output the .cnt entry.
462 *
463 * WinHelp has a bug involving having an internal
464 * node followed by a leaf at the same level: the
465 * leaf is output at the wrong level. We can mostly
466 * work around this by modifying the leaf level
467 * itself (see whlp_contents_write), but this
468 * doesn't work for top-level sections since we
469 * can't turn a level-1 leaf into a level-0 one. So
470 * for top-level leaf sections (Bibliography
471 * springs to mind), we output an internal node
472 * containing only the leaf for that section.
473 */
474 int i;
475 paragraph *q;
476
477 /* Count up the level. */
478 i = 1;
479 for (q = p; q->parent; q = q->parent) i++;
480
481 if (p->child || !p->parent) {
482 /*
483 * If p has children then it needs to be a
484 * folder; if it has no parent then it needs to
485 * be a folder to work around the bug.
486 */
487 whlp_contents_write(&state, i, rs.text, NULL);
488 i++;
489 }
490 whlp_contents_write(&state, i, rs.text, new_topic);
491 }
492
493 sfree(rs.text);
494
495 whlp_begin_para(h, WHLP_PARA_NONSCROLL);
496 state.cstate = charset_init_state;
497 if (p->kwtext) {
498 whlp_mkparagraph(&state, FONT_TITLE, p->kwtext, FALSE, &conf);
499 whlp_set_font(h, FONT_TITLE);
500 whlp_wtext(&state, conf.sectsuffix);
501 }
502 whlp_mkparagraph(&state, FONT_TITLE, p->words, FALSE, &conf);
503 whlp_wtext(&state, NULL);
504 whlp_end_para(h);
505
506 lastsect = p;
507 }
508 break;
509
510 case para_Rule:
511 whlp_para_attr(h, WHLP_PARA_SPACEBELOW, 12);
512 whlp_para_attr(h, WHLP_PARA_ALIGNMENT, WHLP_ALIGN_CENTRE);
513 whlp_begin_para(h, WHLP_PARA_SCROLL);
514 whlp_set_font(h, FONT_RULE);
515 state.cstate = charset_init_state;
516 #define TEN L"\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"
517 #define TWENTY TEN TEN
518 #define FORTY TWENTY TWENTY
519 #define EIGHTY FORTY FORTY
520 state.cstate = charset_init_state;
521 whlp_wtext(&state, EIGHTY);
522 whlp_wtext(&state, NULL);
523 #undef TEN
524 #undef TWENTY
525 #undef FORTY
526 #undef EIGHTY
527 whlp_end_para(h);
528 break;
529
530 case para_Normal:
531 case para_Copyright:
532 case para_DescribedThing:
533 case para_Description:
534 case para_BiblioCited:
535 case para_Bullet:
536 case para_NumberedList:
537 whlp_para_attr(h, WHLP_PARA_SPACEBELOW, 12);
538 if (p->type == para_Bullet || p->type == para_NumberedList) {
539 whlp_para_attr(h, WHLP_PARA_LEFTINDENT, 72*nesting + 72);
540 whlp_para_attr(h, WHLP_PARA_FIRSTLINEINDENT, -36);
541 whlp_set_tabstop(h, 72, WHLP_ALIGN_LEFT);
542 whlp_begin_para(h, WHLP_PARA_SCROLL);
543 whlp_set_font(h, FONT_NORMAL);
544 state.cstate = charset_init_state;
545 if (p->type == para_Bullet) {
546 whlp_wtext(&state, conf.bullet);
547 } else {
548 whlp_mkparagraph(&state, FONT_NORMAL, p->kwtext, FALSE, &conf);
549 whlp_wtext(&state, conf.listsuffix);
550 }
551 whlp_wtext(&state, NULL);
552 whlp_tab(h);
553 } else {
554 whlp_para_attr(h, WHLP_PARA_LEFTINDENT,
555 72*nesting + (p->type==para_Description ? 72 : 0));
556 whlp_begin_para(h, WHLP_PARA_SCROLL);
557 }
558
559 state.cstate = charset_init_state;
560
561 if (p->type == para_BiblioCited) {
562 whlp_mkparagraph(&state, FONT_NORMAL, p->kwtext, FALSE, &conf);
563 whlp_wtext(&state, L" ");
564 }
565
566 whlp_mkparagraph(&state, FONT_NORMAL, p->words, FALSE, &conf);
567 whlp_wtext(&state, NULL);
568 whlp_end_para(h);
569 break;
570
571 case para_Code:
572 /*
573 * In a code paragraph, each individual word is a line. For
574 * Help files, we will have to output this as a set of
575 * paragraphs, all but the last of which don't set
576 * SPACEBELOW.
577 */
578 {
579 word *w;
580 wchar_t *t, *e, *tmp;
581
582 for (w = p->words; w; w = w->next) if (w->type == word_WeakCode) {
583 t = w->text;
584 if (w->next && w->next->type == word_Emph) {
585 w = w->next;
586 e = w->text;
587 } else
588 e = NULL;
589
590 if (!w->next)
591 whlp_para_attr(h, WHLP_PARA_SPACEBELOW, 12);
592
593 whlp_para_attr(h, WHLP_PARA_LEFTINDENT, 72*nesting);
594 whlp_begin_para(h, WHLP_PARA_SCROLL);
595 state.cstate = charset_init_state;
596 while (e && *e && *t) {
597 int n;
598 int ec = *e;
599
600 for (n = 0; t[n] && e[n] && e[n] == ec; n++);
601 if (ec == 'i')
602 whlp_set_font(h, FONT_ITAL_CODE);
603 else if (ec == 'b')
604 whlp_set_font(h, FONT_BOLD_CODE);
605 else
606 whlp_set_font(h, FONT_CODE);
607 tmp = snewn(n+1, wchar_t);
608 ustrncpy(tmp, t, n);
609 tmp[n] = L'\0';
610 whlp_wtext(&state, tmp);
611 whlp_wtext(&state, NULL);
612 state.cstate = charset_init_state;
613 sfree(tmp);
614 t += n;
615 e += n;
616 }
617 whlp_set_font(h, FONT_CODE);
618 whlp_wtext(&state, t);
619 whlp_wtext(&state, NULL);
620 whlp_end_para(h);
621 }
622 }
623 break;
624 }
625
626 fclose(state.cntfp);
627 whlp_close(h, conf.filename);
628
629 /*
630 * Loop over the index entries, cleaning up our final text
631 * forms.
632 */
633 for (i = 0; (ie = index234(idx->entries, i)) != NULL; i++) {
634 sfree(ie->backend_data);
635 }
636
637 sfree(conf.filename);
638 sfree(cntname);
639 }
640
641 static void whlp_contents_write(struct bk_whlp_state *state,
642 int level, char *text, WHLP_TOPIC topic) {
643 /*
644 * Horrifying bug in WinHelp. When dropping a section level or
645 * more without using a folder-type entry, WinHelp accidentally
646 * adds one to the section level. So we correct for that here.
647 */
648 if (state->cnt_last_level > level && topic)
649 state->cnt_workaround = -1;
650 else if (!topic)
651 state->cnt_workaround = 0;
652 state->cnt_last_level = level;
653
654 fprintf(state->cntfp, "%d ", level + state->cnt_workaround);
655 while (*text) {
656 if (*text == '=')
657 fputc('\\', state->cntfp);
658 fputc(*text, state->cntfp);
659 text++;
660 }
661 if (topic)
662 fprintf(state->cntfp, "=%s", whlp_topic_id(topic));
663 fputc('\n', state->cntfp);
664 }
665
666 static void whlp_navmenu(struct bk_whlp_state *state, paragraph *p,
667 whlpconf *conf) {
668 whlp_begin_para(state->h, WHLP_PARA_SCROLL);
669 whlp_start_hyperlink(state->h, (WHLP_TOPIC)p->private_data);
670 state->cstate = charset_init_state;
671 if (p->kwtext) {
672 whlp_mkparagraph(state, FONT_NORMAL, p->kwtext, TRUE, conf);
673 whlp_set_font(state->h, FONT_NORMAL);
674 whlp_wtext(state, conf->sectsuffix);
675 }
676 whlp_mkparagraph(state, FONT_NORMAL, p->words, TRUE, conf);
677 whlp_wtext(state, NULL);
678 whlp_end_hyperlink(state->h);
679 whlp_end_para(state->h);
680
681 }
682
683 static void whlp_mkparagraph(struct bk_whlp_state *state,
684 int font, word *text, int subsidiary,
685 whlpconf *conf) {
686 keyword *kwl;
687 int deffont = font;
688 int currfont = -1;
689 int newfont;
690 paragraph *xref_target = NULL;
691
692 for (; text; text = text->next) switch (text->type) {
693 case word_HyperLink:
694 case word_HyperEnd:
695 break;
696
697 case word_IndexRef:
698 if (subsidiary) break; /* disabled in subsidiary bits */
699 {
700 indextag *tag = index_findtag(state->idx, text->text);
701 int i;
702 if (!tag)
703 break;
704 for (i = 0; i < tag->nrefs; i++)
705 whlp_index_term(state->h, tag->refs[i]->backend_data,
706 state->curr_topic);
707 }
708 break;
709
710 case word_UpperXref:
711 case word_LowerXref:
712 if (subsidiary) break; /* disabled in subsidiary bits */
713 kwl = kw_lookup(state->keywords, text->text);
714 assert(xref_target == NULL);
715 if (kwl) {
716 if (kwl->para->type == para_NumberedList) {
717 break; /* don't xref to numbered list items */
718 } else if (kwl->para->type == para_BiblioCited) {
719 /*
720 * An xref to a bibliography item jumps to the section
721 * containing it.
722 */
723 if (kwl->para->parent)
724 xref_target = kwl->para->parent;
725 else
726 break;
727 } else {
728 xref_target = kwl->para;
729 }
730 whlp_start_hyperlink(state->h,
731 (WHLP_TOPIC)xref_target->private_data);
732 }
733 break;
734
735 case word_XrefEnd:
736 if (subsidiary) break; /* disabled in subsidiary bits */
737 if (xref_target)
738 whlp_end_hyperlink(state->h);
739 xref_target = NULL;
740 break;
741
742 case word_Normal:
743 case word_Emph:
744 case word_Code:
745 case word_WeakCode:
746 case word_WhiteSpace:
747 case word_EmphSpace:
748 case word_CodeSpace:
749 case word_WkCodeSpace:
750 case word_Quote:
751 case word_EmphQuote:
752 case word_CodeQuote:
753 case word_WkCodeQuote:
754 if (towordstyle(text->type) == word_Emph)
755 newfont = deffont + FONT_EMPH;
756 else if (towordstyle(text->type) == word_Code ||
757 towordstyle(text->type) == word_WeakCode)
758 newfont = deffont + FONT_CODE;
759 else
760 newfont = deffont;
761 if (newfont != currfont) {
762 currfont = newfont;
763 whlp_set_font(state->h, newfont);
764 }
765 if (removeattr(text->type) == word_Normal) {
766 if (cvt_ok(conf->charset, text->text) || !text->alt)
767 whlp_wtext(state, text->text);
768 else
769 whlp_mkparagraph(state, deffont, text->alt, FALSE, conf);
770 } else if (removeattr(text->type) == word_WhiteSpace) {
771 whlp_wtext(state, L" ");
772 } else if (removeattr(text->type) == word_Quote) {
773 whlp_wtext(state,
774 quoteaux(text->aux) == quote_Open ?
775 conf->lquote : conf->rquote);
776 }
777 break;
778 }
779 }
780
781 static void whlp_rdaddwc(rdstringc *rs, word *text, whlpconf *conf,
782 charset_state *state) {
783 charset_state ourstate = CHARSET_INIT_STATE;
784
785 if (!state)
786 state = &ourstate;
787
788 for (; text; text = text->next) switch (text->type) {
789 case word_HyperLink:
790 case word_HyperEnd:
791 case word_UpperXref:
792 case word_LowerXref:
793 case word_XrefEnd:
794 case word_IndexRef:
795 break;
796
797 case word_Normal:
798 case word_Emph:
799 case word_Code:
800 case word_WeakCode:
801 case word_WhiteSpace:
802 case word_EmphSpace:
803 case word_CodeSpace:
804 case word_WkCodeSpace:
805 case word_Quote:
806 case word_EmphQuote:
807 case word_CodeQuote:
808 case word_WkCodeQuote:
809 assert(text->type != word_CodeQuote &&
810 text->type != word_WkCodeQuote);
811 if (removeattr(text->type) == word_Normal) {
812 if (cvt_ok(conf->charset, text->text) || !text->alt)
813 whlp_rdadds(rs, text->text, conf, state);
814 else
815 whlp_rdaddwc(rs, text->alt, conf, state);
816 } else if (removeattr(text->type) == word_WhiteSpace) {
817 whlp_rdadds(rs, L" ", conf, state);
818 } else if (removeattr(text->type) == word_Quote) {
819 whlp_rdadds(rs, quoteaux(text->aux) == quote_Open ?
820 conf->lquote : conf->rquote, conf, state);
821 }
822 break;
823 }
824
825 if (state == &ourstate)
826 whlp_rdadds(rs, NULL, conf, state);
827 }
828
829 static void whlp_rdadds(rdstringc *rs, const wchar_t *text, whlpconf *conf,
830 charset_state *state)
831 {
832 charset_state ourstate = CHARSET_INIT_STATE;
833 int textlen = text ? ustrlen(text) : 0;
834 char outbuf[256];
835 int ret;
836
837 if (!state)
838 state = &ourstate;
839
840 while (textlen > 0 &&
841 (ret = charset_from_unicode(&text, &textlen, outbuf,
842 lenof(outbuf)-1,
843 conf->charset, state, NULL)) > 0) {
844 outbuf[ret] = '\0';
845 rdaddsc(rs, outbuf);
846 }
847
848 if (text == NULL || state == &ourstate) {
849 if ((ret = charset_from_unicode(NULL, 0, outbuf, lenof(outbuf)-1,
850 conf->charset, state, NULL)) > 0) {
851 outbuf[ret] = '\0';
852 rdaddsc(rs, outbuf);
853 }
854 }
855 }
856
857 static void whlp_wtext(struct bk_whlp_state *state, const wchar_t *text)
858 {
859 int textlen = text ? ustrlen(text) : 0;
860 char outbuf[256];
861 int ret;
862
863 while (textlen > 0 &&
864 (ret = charset_from_unicode(&text, &textlen, outbuf,
865 lenof(outbuf)-1,
866 state->charset, &state->cstate,
867 NULL)) > 0) {
868 outbuf[ret] = '\0';
869 whlp_text(state->h, outbuf);
870 }
871
872 if (text == NULL) {
873 if ((ret = charset_from_unicode(NULL, 0, outbuf, lenof(outbuf)-1,
874 state->charset, &state->cstate,
875 NULL)) > 0) {
876 outbuf[ret] = '\0';
877 whlp_text(state->h, outbuf);
878 }
879 }
880 }