Oops, nearly forgot. Nesting one numbered list inside another should
[sgt/halibut] / keywords.c
1 /*
2 * keywords.c: keep track of all cross-reference keywords
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <assert.h>
8 #include "halibut.h"
9
10 static int kwcmp(void *av, void *bv)
11 {
12 const keyword *a = (const keyword *)av;
13 const keyword *b = (const keyword *)bv;
14 return ustrcmp(a->key, b->key);
15 }
16
17 static int kwfind(void *av, void *bv)
18 {
19 wchar_t *a = (wchar_t *)av;
20 const keyword *b = (const keyword *)bv;
21 return ustrcmp(a, b->key);
22 }
23
24 keyword *kw_lookup(keywordlist *kl, wchar_t *str) {
25 return find234(kl->keys, str, kwfind);
26 }
27
28 /*
29 * This function reads through source form and collects the
30 * keywords. They get collected in a heap, sorted by Unicode
31 * collation, last at the top (so that we can Heapsort them when we
32 * finish).
33 */
34 keywordlist *get_keywords(paragraph *source) {
35 int errors = FALSE;
36 keywordlist *kl = mknew(keywordlist);
37 numberstate *n = number_init();
38 int prevpara = para_NotParaType;
39
40 number_cfg(n, source);
41
42 kl->size = 0;
43 kl->keys = newtree234(kwcmp);
44 kl->nlooseends = kl->looseendssize = 0;
45 kl->looseends = NULL;
46 for (; source; source = source->next) {
47 wchar_t *p, *q;
48 p = q = source->keyword;
49
50 /*
51 * Look for the section type override (`example',
52 * `question' or whatever - to replace `chapter' or
53 * `section' on a per-section basis).
54 */
55 if (q) {
56 q = uadv(q); /* point q at the word beyond */
57 if (!*q) q = NULL;
58 }
59
60 /*
61 * Number the chapter / section / list-item / whatever.
62 * This also sets up the `parent', `child' and `sibling'
63 * links.
64 */
65 source->kwtext = number_mktext(n, source, q, &prevpara, &errors);
66
67 if (p && *p) {
68 if (source->kwtext || source->type == para_Biblio) {
69 keyword *kw, *ret;
70
71 kw = mknew(keyword);
72 kw->key = p;
73 kw->text = source->kwtext;
74 kw->para = source;
75 ret = add234(kl->keys, kw);
76 if (ret != kw) {
77 error(err_multikw, &source->fpos, &ret->para->fpos, p);
78 sfree(kw);
79 /* FIXME: what happens to kw->text? Does it leak? */
80 }
81 }
82 } else {
83 if (kl->nlooseends >= kl->looseendssize) {
84 kl->looseendssize = kl->nlooseends + 32;
85 kl->looseends = resize(kl->looseends, kl->looseendssize);
86 }
87 kl->looseends[kl->nlooseends++] = source->kwtext;
88 }
89 }
90
91 number_free(n);
92
93 if (errors) {
94 free_keywords(kl);
95 return NULL;
96 }
97
98 return kl;
99 }
100
101 void free_keywords(keywordlist *kl) {
102 keyword *kw;
103 while (kl->nlooseends)
104 free_word_list(kl->looseends[--kl->nlooseends]);
105 sfree(kl->looseends);
106 while ( (kw = index234(kl->keys, 0)) != NULL) {
107 delpos234(kl->keys, 0);
108 free_word_list(kw->text);
109 sfree(kw);
110 }
111 freetree234(kl->keys);
112 sfree(kl);
113 }
114
115 void subst_keywords(paragraph *source, keywordlist *kl) {
116 for (; source; source = source->next) {
117 word *ptr;
118 for (ptr = source->words; ptr; ptr = ptr->next) {
119 if (ptr->type == word_UpperXref ||
120 ptr->type == word_LowerXref) {
121 keyword *kw;
122 word **endptr, *close, *subst;
123
124 kw = kw_lookup(kl, ptr->text);
125 if (!kw) {
126 error(err_nosuchkw, &ptr->fpos, ptr->text);
127 subst = NULL;
128 } else
129 subst = dup_word_list(kw->text);
130
131 if (subst && ptr->type == word_LowerXref &&
132 kw->para->type != para_Biblio &&
133 kw->para->type != para_BiblioCited)
134 ustrlow(subst->text);
135
136 close = mknew(word);
137 close->text = NULL;
138 close->alt = NULL;
139 close->type = word_XrefEnd;
140 close->fpos = ptr->fpos;
141
142 close->next = ptr->next;
143 ptr->next = subst;
144
145 for (endptr = &ptr->next; *endptr; endptr = &(*endptr)->next)
146 (*endptr)->fpos = ptr->fpos;
147
148 *endptr = close;
149 ptr = close;
150 }
151 }
152 }
153 }