On Level 2 implementations, use setpagedevice to select the correct page size.
[sgt/halibut] / bk_whlp.c
CommitLineData
d7482997 1/*
2 * Windows Help backend for Halibut
d7482997 3 */
4
5#include <stdio.h>
6#include <stdlib.h>
50d6b4bd 7#include <ctype.h>
d7482997 8#include <assert.h>
9
10#include "halibut.h"
11#include "winhelp.h"
12
13struct bk_whlp_state {
14 WHLP h;
15 indexdata *idx;
16 keywordlist *keywords;
17 WHLP_TOPIC curr_topic;
08e78486 18 int charset;
19 charset_state cstate;
d7482997 20 FILE *cntfp;
21 int cnt_last_level, cnt_workaround;
22};
23
08e78486 24typedef struct {
25 int charset;
26 wchar_t *bullet, *lquote, *rquote, *titlepage, *sectsuffix, *listsuffix;
27 char *filename;
28} whlpconf;
29
d7482997 30/*
31 * Indexes of fonts in our standard font descriptor set.
32 */
33enum {
34 FONT_NORMAL,
35 FONT_EMPH,
36 FONT_CODE,
4b3c5afb 37 FONT_ITAL_CODE,
38 FONT_BOLD_CODE,
d7482997 39 FONT_TITLE,
40 FONT_TITLE_EMPH,
41 FONT_TITLE_CODE,
42 FONT_RULE
43};
44
08e78486 45static void whlp_rdaddwc(rdstringc *rs, word *text, whlpconf *conf,
46 charset_state *state);
47static void whlp_rdadds(rdstringc *rs, const wchar_t *text, whlpconf *conf,
48 charset_state *state);
d7482997 49static void whlp_mkparagraph(struct bk_whlp_state *state,
08e78486 50 int font, word *text, int subsidiary,
51 whlpconf *conf);
52static void whlp_navmenu(struct bk_whlp_state *state, paragraph *p,
53 whlpconf *conf);
d7482997 54static void whlp_contents_write(struct bk_whlp_state *state,
55 int level, char *text, WHLP_TOPIC topic);
08e78486 56static void whlp_wtext(struct bk_whlp_state *state, const wchar_t *text);
d7482997 57
ba9c1487 58paragraph *whlp_config_filename(char *filename)
59{
e4ea58f8 60 return cmdline_cfg_simple("winhelp-filename", filename, NULL);
ba9c1487 61}
62
08e78486 63static 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
d7482997 146void whlp_backend(paragraph *sourceform, keywordlist *keywords,
43341922 147 indexdata *idx, void *unused) {
d7482997 148 WHLP h;
08e78486 149 char *cntname;
d7482997 150 paragraph *p, *lastsect;
151 struct bk_whlp_state state;
152 WHLP_TOPIC contents_topic;
153 int i;
7136a6c7 154 int nesting;
d7482997 155 indexentry *ie;
de967e1b 156 int done_contents_topic = FALSE;
08e78486 157 whlpconf conf;
d7482997 158
43341922 159 IGNORE(unused);
160
d7482997 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);
4b3c5afb 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);
d7482997 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
08e78486 188 conf = whlp_configure(sourceform);
189
190 state.charset = conf.charset;
d7482997 191
192 /*
50d6b4bd 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 {
08e78486 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')) {
50d6b4bd 203 char *newf;
f1530049 204 newf = snewn(len + 5, char);
08e78486 205 sprintf(newf, "%s.hlp", conf.filename);
206 sfree(conf.filename);
207 conf.filename = newf;
50d6b4bd 208 len = strlen(newf);
209 }
f1530049 210 cntname = snewn(len+1, char);
08e78486 211 sprintf(cntname, "%.*s.cnt", len-4, conf.filename);
50d6b4bd 212 }
213
214 state.cntfp = fopen(cntname, "wb");
215 state.cnt_last_level = -1; state.cnt_workaround = 0;
216
217 /*
d7482997 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) {
08e78486 230
231 rdstringc rs = { 0, 0, NULL };
d7482997 232 char *errstr;
233
08e78486 234 whlp_rdadds(&rs, (wchar_t *)p->private_data, &conf, NULL);
235
236 p->private_data = whlp_register_topic(h, rs.text, &errstr);
d7482997 237 if (!p->private_data) {
238 p->private_data = whlp_register_topic(h, NULL, NULL);
08e78486 239 error(err_winhelp_ctxclash, &p->fpos, rs.text, errstr);
d7482997 240 }
08e78486 241 sfree(rs.text);
d7482997 242 }
243 }
244
245 /*
246 * Loop over the index entries, preparing final text forms for
247 * each one.
248 */
7c570bf3 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
c770011a 261 * is inherently case-insensitive. Therefore, if two
7c570bf3 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 }
d7482997 306 }
307
308 whlp_prepare(h);
309
310 /* ------------------------------------------------------------------
8902e0ed 311 * Begin the contents page.
d7482997 312 */
313
314 whlp_begin_topic(h, contents_topic, "Contents", "DB(\"btn_up\")", NULL);
7a5bb1e5 315 state.curr_topic = contents_topic;
d7482997 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);
08e78486 326 state.cstate = charset_init_state;
327 whlp_mkparagraph(&state, FONT_TITLE, p->words, FALSE, &conf);
328 whlp_wtext(&state, NULL);
d7482997 329 whlp_end_para(h);
08e78486 330 whlp_rdaddwc(&rs, p->words, &conf, NULL);
d7482997 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 }
08e78486 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 }
d7482997 344 }
345
9057a0a8 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)
08e78486 353 whlp_rdaddwc(&rs, p->words, &conf, NULL);
9057a0a8 354 }
355 if (rs.text) {
356 whlp_copyright(h, rs.text);
357 sfree(rs.text);
358 }
359 }
360
d7482997 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 */
7136a6c7 367 nesting = 0;
d7482997 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:
d7482997 377 case para_NoCite:
378 case para_Title:
379 break;
380
7136a6c7 381 case para_LcontPush:
2614b01d 382 case para_QuotePush:
7136a6c7 383 nesting++;
384 break;
385 case para_LcontPop:
2614b01d 386 case para_QuotePop:
7136a6c7 387 assert(nesting > 0);
388 nesting--;
389 break;
390
d7482997 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:
8902e0ed 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
9057a0a8 407 * writing a nav menu.
8902e0ed 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)
08e78486 413 whlp_navmenu(&state, p, &conf);
8902e0ed 414 }
415
8902e0ed 416 done_contents_topic = TRUE;
417 }
418
d7482997 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)
08e78486 426 whlp_navmenu(&state, q, &conf);
d7482997 427 }
428 {
429 rdstringc rs = {0, 0, NULL};
430 WHLP_TOPIC new_topic, parent_topic;
431 char *macro, *topicid;
08e78486 432 charset_state cstate = CHARSET_INIT_STATE;
d7482997 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) {
08e78486 439 whlp_rdaddwc(&rs, p->kwtext, &conf, &cstate);
440 whlp_rdadds(&rs, conf.sectsuffix, &conf, &cstate);
d7482997 441 }
08e78486 442 whlp_rdaddwc(&rs, p->words, &conf, &cstate);
443 whlp_rdadds(&rs, NULL, &conf, &cstate);
444
d7482997 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);
08e78486 496 state.cstate = charset_init_state;
d7482997 497 if (p->kwtext) {
08e78486 498 whlp_mkparagraph(&state, FONT_TITLE, p->kwtext, FALSE, &conf);
d7482997 499 whlp_set_font(h, FONT_TITLE);
08e78486 500 whlp_wtext(&state, conf.sectsuffix);
d7482997 501 }
08e78486 502 whlp_mkparagraph(&state, FONT_TITLE, p->words, FALSE, &conf);
503 whlp_wtext(&state, NULL);
d7482997 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);
08e78486 515 state.cstate = charset_init_state;
516#define TEN L"\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0"
d7482997 517#define TWENTY TEN TEN
518#define FORTY TWENTY TWENTY
519#define EIGHTY FORTY FORTY
08e78486 520 state.cstate = charset_init_state;
521 whlp_wtext(&state, EIGHTY);
522 whlp_wtext(&state, NULL);
d7482997 523#undef TEN
524#undef TWENTY
525#undef FORTY
526#undef EIGHTY
527 whlp_end_para(h);
528 break;
529
530 case para_Normal:
9057a0a8 531 case para_Copyright:
7136a6c7 532 case para_DescribedThing:
533 case para_Description:
d7482997 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) {
7136a6c7 539 whlp_para_attr(h, WHLP_PARA_LEFTINDENT, 72*nesting + 72);
d7482997 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);
08e78486 544 state.cstate = charset_init_state;
d7482997 545 if (p->type == para_Bullet) {
08e78486 546 whlp_wtext(&state, conf.bullet);
d7482997 547 } else {
08e78486 548 whlp_mkparagraph(&state, FONT_NORMAL, p->kwtext, FALSE, &conf);
549 whlp_wtext(&state, conf.listsuffix);
d7482997 550 }
08e78486 551 whlp_wtext(&state, NULL);
d7482997 552 whlp_tab(h);
553 } else {
7136a6c7 554 whlp_para_attr(h, WHLP_PARA_LEFTINDENT,
555 72*nesting + (p->type==para_Description ? 72 : 0));
d7482997 556 whlp_begin_para(h, WHLP_PARA_SCROLL);
557 }
558
08e78486 559 state.cstate = charset_init_state;
560
d7482997 561 if (p->type == para_BiblioCited) {
08e78486 562 whlp_mkparagraph(&state, FONT_NORMAL, p->kwtext, FALSE, &conf);
563 whlp_wtext(&state, L" ");
d7482997 564 }
565
08e78486 566 whlp_mkparagraph(&state, FONT_NORMAL, p->words, FALSE, &conf);
567 whlp_wtext(&state, NULL);
d7482997 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;
08e78486 580 wchar_t *t, *e, *tmp;
4b3c5afb 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
d7482997 590 if (!w->next)
591 whlp_para_attr(h, WHLP_PARA_SPACEBELOW, 12);
4b3c5afb 592
7136a6c7 593 whlp_para_attr(h, WHLP_PARA_LEFTINDENT, 72*nesting);
d7482997 594 whlp_begin_para(h, WHLP_PARA_SCROLL);
08e78486 595 state.cstate = charset_init_state;
4b3c5afb 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);
f1530049 607 tmp = snewn(n+1, wchar_t);
08e78486 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);
4b3c5afb 614 t += n;
615 e += n;
616 }
d7482997 617 whlp_set_font(h, FONT_CODE);
08e78486 618 whlp_wtext(&state, t);
619 whlp_wtext(&state, NULL);
d7482997 620 whlp_end_para(h);
621 }
622 }
623 break;
624 }
625
626 fclose(state.cntfp);
08e78486 627 whlp_close(h, conf.filename);
d7482997 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 }
50d6b4bd 636
08e78486 637 sfree(conf.filename);
50d6b4bd 638 sfree(cntname);
d7482997 639}
640
641static 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
08e78486 666static void whlp_navmenu(struct bk_whlp_state *state, paragraph *p,
667 whlpconf *conf) {
23f85ecc 668 whlp_begin_para(state->h, WHLP_PARA_SCROLL);
d7482997 669 whlp_start_hyperlink(state->h, (WHLP_TOPIC)p->private_data);
08e78486 670 state->cstate = charset_init_state;
d7482997 671 if (p->kwtext) {
08e78486 672 whlp_mkparagraph(state, FONT_NORMAL, p->kwtext, TRUE, conf);
d7482997 673 whlp_set_font(state->h, FONT_NORMAL);
08e78486 674 whlp_wtext(state, conf->sectsuffix);
d7482997 675 }
08e78486 676 whlp_mkparagraph(state, FONT_NORMAL, p->words, TRUE, conf);
677 whlp_wtext(state, NULL);
d7482997 678 whlp_end_hyperlink(state->h);
679 whlp_end_para(state->h);
680
681}
682
683static void whlp_mkparagraph(struct bk_whlp_state *state,
08e78486 684 int font, word *text, int subsidiary,
685 whlpconf *conf) {
d7482997 686 keyword *kwl;
687 int deffont = font;
688 int currfont = -1;
689 int newfont;
d7482997 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);
7cf305b7 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 }
d7482997 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) {
08e78486 766 if (cvt_ok(conf->charset, text->text) || !text->alt)
767 whlp_wtext(state, text->text);
d7482997 768 else
08e78486 769 whlp_mkparagraph(state, deffont, text->alt, FALSE, conf);
d7482997 770 } else if (removeattr(text->type) == word_WhiteSpace) {
08e78486 771 whlp_wtext(state, L" ");
d7482997 772 } else if (removeattr(text->type) == word_Quote) {
08e78486 773 whlp_wtext(state,
774 quoteaux(text->aux) == quote_Open ?
775 conf->lquote : conf->rquote);
d7482997 776 }
777 break;
778 }
779}
780
08e78486 781static 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;
d7482997 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) {
08e78486 812 if (cvt_ok(conf->charset, text->text) || !text->alt)
813 whlp_rdadds(rs, text->text, conf, state);
d7482997 814 else
08e78486 815 whlp_rdaddwc(rs, text->alt, conf, state);
d7482997 816 } else if (removeattr(text->type) == word_WhiteSpace) {
08e78486 817 whlp_rdadds(rs, L" ", conf, state);
d7482997 818 } else if (removeattr(text->type) == word_Quote) {
08e78486 819 whlp_rdadds(rs, quoteaux(text->aux) == quote_Open ?
820 conf->lquote : conf->rquote, conf, state);
d7482997 821 }
822 break;
823 }
2223c2dd 824
08e78486 825 if (state == &ourstate)
826 whlp_rdadds(rs, NULL, conf, state);
827}
2223c2dd 828
08e78486 829static 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);
d7482997 846 }
2223c2dd 847
08e78486 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}
2223c2dd 856
08e78486 857static 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 }
2223c2dd 871
08e78486 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 }
d7482997 880}