Bug in utoi(), which made it ignore a leading minus sign when
[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 = snew(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 = snew(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 = sresize(kl->looseends, kl->looseendssize,
86 word *);
87 }
88 kl->looseends[kl->nlooseends++] = source->kwtext;
89 }
90 }
91
92 number_free(n);
93
94 if (errors) {
95 free_keywords(kl);
96 return NULL;
97 }
98
99 return kl;
100 }
101
102 void free_keywords(keywordlist *kl) {
103 keyword *kw;
104 while (kl->nlooseends)
105 free_word_list(kl->looseends[--kl->nlooseends]);
106 sfree(kl->looseends);
107 while ( (kw = index234(kl->keys, 0)) != NULL) {
108 delpos234(kl->keys, 0);
109 free_word_list(kw->text);
110 sfree(kw);
111 }
112 freetree234(kl->keys);
113 sfree(kl);
114 }
115
116 void subst_keywords(paragraph *source, keywordlist *kl) {
117 for (; source; source = source->next) {
118 word *ptr;
119 for (ptr = source->words; ptr; ptr = ptr->next) {
120 if (ptr->type == word_UpperXref ||
121 ptr->type == word_LowerXref) {
122 keyword *kw;
123 word **endptr, *close, *subst;
124
125 kw = kw_lookup(kl, ptr->text);
126 if (!kw) {
127 error(err_nosuchkw, &ptr->fpos, ptr->text);
128 subst = NULL;
129 } else
130 subst = dup_word_list(kw->text);
131
132 if (subst && ptr->type == word_LowerXref &&
133 kw->para->type != para_Biblio &&
134 kw->para->type != para_BiblioCited)
135 ustrlow(subst->text);
136
137 close = snew(word);
138 close->text = NULL;
139 close->alt = NULL;
140 close->type = word_XrefEnd;
141 close->fpos = ptr->fpos;
142
143 close->next = ptr->next;
144 ptr->next = subst;
145
146 for (endptr = &ptr->next; *endptr; endptr = &(*endptr)->next)
147 (*endptr)->fpos = ptr->fpos;
148
149 *endptr = close;
150 ptr = close;
151 }
152 }
153 }
154 }