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 
kwcmp(void * av,void * bv)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 
kwfind(void * av,void * bv)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 
kw_lookup(keywordlist * kl,wchar_t * str)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  */
get_keywords(paragraph * source)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 		    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 
free_keywords(keywordlist * kl)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 
subst_keywords(paragraph * source,keywordlist * kl)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 		    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 		close->breaks = FALSE;
143 		close->aux = 0;
144 
145 		close->next = ptr->next;
146 		ptr->next = subst;
147 
148 		for (endptr = &ptr->next; *endptr; endptr = &(*endptr)->next)
149 		    (*endptr)->fpos = ptr->fpos;
150 
151 		*endptr = close;
152 		ptr = close;
153 	    }
154 	}
155     }
156 }
157