Line data Source code
1 : /* Copyright (C) 1989-2024 Free Software Foundation, Inc.
2 : Written by James Clark (jjc@jclark.com)
3 :
4 : This file is part of groff, the GNU roff typesetting system.
5 :
6 : groff is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU General Public License as published by the Free
8 : Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : #ifdef HAVE_CONFIG_H
20 : #include <config.h>
21 : #endif
22 :
23 : #include "refer.h"
24 : #include "token.h"
25 :
26 : #define TOKEN_TABLE_SIZE 1009
27 : // I believe in Icelandic thorn sorts after z.
28 : #define THORN_SORT_KEY "{"
29 :
30 : struct token_table_entry {
31 : const char *tok;
32 : token_info ti;
33 : token_table_entry();
34 : };
35 :
36 : token_table_entry token_table[TOKEN_TABLE_SIZE];
37 : int ntokens = 0;
38 :
39 4 : static void skip_name(const char **ptr, const char *end)
40 : {
41 4 : if (*ptr < end) {
42 4 : switch (*(*ptr)++) {
43 0 : case '(':
44 0 : if (*ptr < end) {
45 0 : *ptr += 1;
46 0 : if (*ptr < end)
47 0 : *ptr += 1;
48 : }
49 0 : break;
50 0 : case '[':
51 0 : while (*ptr < end)
52 0 : if (*(*ptr)++ == ']')
53 0 : break;
54 0 : break;
55 : }
56 : }
57 4 : }
58 :
59 189 : bool get_token(const char **ptr, const char *end)
60 : {
61 189 : if (*ptr >= end)
62 8 : return false;
63 181 : char c = *(*ptr)++;
64 181 : if (c == '\\' && *ptr < end) {
65 4 : switch (**ptr) {
66 0 : default:
67 0 : *ptr += 1;
68 0 : break;
69 0 : case '(':
70 : case '[':
71 0 : skip_name(ptr, end);
72 0 : break;
73 4 : case '*':
74 : case 'f':
75 4 : *ptr += 1;
76 4 : skip_name(ptr, end);
77 4 : break;
78 : }
79 : }
80 181 : return true;
81 : }
82 :
83 6060 : token_info::token_info()
84 6060 : : type(TOKEN_OTHER), sort_key(0), other_case(0)
85 : {
86 6060 : }
87 :
88 2118 : void token_info::set(token_type t, const char *sk, const char *oc)
89 : {
90 2118 : assert(oc == 0 || t == TOKEN_UPPER || t == TOKEN_LOWER);
91 2118 : type = t;
92 2118 : sort_key = sk;
93 2118 : other_case = oc;
94 2118 : }
95 :
96 0 : void token_info::sortify(const char *start, const char *end, string &result)
97 : const
98 : {
99 0 : if (sort_key)
100 0 : result += sort_key;
101 0 : else if (type == TOKEN_UPPER || type == TOKEN_LOWER) {
102 0 : for (; start < end; start++)
103 0 : if (csalpha(*start))
104 0 : result += cmlower(*start);
105 : }
106 0 : }
107 :
108 0 : int token_info::sortify_non_empty(const char *start, const char *end) const
109 : {
110 0 : if (sort_key)
111 0 : return *sort_key != '\0';
112 0 : if (type != TOKEN_UPPER && type != TOKEN_LOWER)
113 0 : return 0;
114 0 : for (; start < end; start++)
115 0 : if (csalpha(*start))
116 0 : return 1;
117 0 : return 0;
118 : }
119 :
120 :
121 0 : void token_info::lower_case(const char *start, const char *end,
122 : string &result) const
123 : {
124 0 : if (type != TOKEN_UPPER) {
125 0 : while (start < end)
126 0 : result += *start++;
127 : }
128 0 : else if (other_case)
129 0 : result += other_case;
130 : else {
131 0 : while (start < end)
132 0 : result += cmlower(*start++);
133 : }
134 0 : }
135 :
136 0 : void token_info::upper_case(const char *start, const char *end,
137 : string &result) const
138 : {
139 0 : if (type != TOKEN_LOWER) {
140 0 : while (start < end)
141 0 : result += *start++;
142 : }
143 0 : else if (other_case)
144 0 : result += other_case;
145 : else {
146 0 : while (start < end)
147 0 : result += cmupper(*start++);
148 : }
149 0 : }
150 :
151 6054 : token_table_entry::token_table_entry()
152 6054 : : tok(0)
153 : {
154 6054 : }
155 :
156 2112 : static void store_token(const char *tok, token_type typ,
157 : const char *sk = 0, const char *oc = 0)
158 : {
159 2112 : unsigned n = hash_string(tok, strlen(tok)) % TOKEN_TABLE_SIZE;
160 : for (;;) {
161 6330 : if (token_table[n].tok == 0) {
162 2100 : if (++ntokens == TOKEN_TABLE_SIZE)
163 0 : assert(0);
164 2100 : token_table[n].tok = tok;
165 2100 : break;
166 : }
167 4230 : if (strcmp(tok, token_table[n].tok) == 0)
168 12 : break;
169 4218 : if (n == 0)
170 0 : n = TOKEN_TABLE_SIZE - 1;
171 : else
172 4218 : --n;
173 : }
174 2112 : token_table[n].ti.set(typ, sk, oc);
175 2112 : }
176 :
177 :
178 : token_info default_token_info;
179 :
180 0 : const token_info *lookup_token(const char *start, const char *end)
181 : {
182 0 : unsigned n = hash_string(start, end - start) % TOKEN_TABLE_SIZE;
183 : for (;;) {
184 0 : if (token_table[n].tok == 0)
185 0 : break;
186 0 : if (strlen(token_table[n].tok) == size_t(end - start)
187 0 : && memcmp(token_table[n].tok, start, end - start) == 0)
188 0 : return &(token_table[n].ti);
189 0 : if (n == 0)
190 0 : n = TOKEN_TABLE_SIZE - 1;
191 : else
192 0 : --n;
193 : }
194 0 : return &default_token_info;
195 : }
196 :
197 6 : static void init_ascii()
198 : {
199 : const char *p;
200 162 : for (p = "abcdefghijklmnopqrstuvwxyz"; *p; p++) {
201 : char buf[2];
202 156 : buf[0] = *p;
203 156 : buf[1] = '\0';
204 156 : store_token(strsave(buf), TOKEN_LOWER);
205 156 : buf[0] = cmupper(buf[0]);
206 156 : store_token(strsave(buf), TOKEN_UPPER);
207 : }
208 66 : for (p = "0123456789"; *p; p++) {
209 : char buf[2];
210 60 : buf[0] = *p;
211 60 : buf[1] = '\0';
212 60 : const char *s = strsave(buf);
213 60 : store_token(s, TOKEN_OTHER, s);
214 : }
215 42 : for (p = ".,:;?!"; *p; p++) {
216 : char buf[2];
217 36 : buf[0] = *p;
218 36 : buf[1] = '\0';
219 36 : store_token(strsave(buf), TOKEN_PUNCT);
220 : }
221 6 : store_token("-", TOKEN_HYPHEN);
222 6 : }
223 :
224 732 : static void store_letter(const char *lower, const char *upper,
225 : const char *sort_key = 0)
226 : {
227 732 : store_token(lower, TOKEN_LOWER, sort_key, upper);
228 732 : store_token(upper, TOKEN_UPPER, sort_key, lower);
229 732 : }
230 :
231 180 : static void init_letter(unsigned char uc_code, unsigned char lc_code,
232 : const char *sort_key)
233 : {
234 : char lbuf[2];
235 180 : lbuf[0] = lc_code;
236 180 : lbuf[1] = 0;
237 : char ubuf[2];
238 180 : ubuf[0] = uc_code;
239 180 : ubuf[1] = 0;
240 180 : store_letter(strsave(lbuf), strsave(ubuf), sort_key);
241 180 : }
242 :
243 6 : static void init_latin1()
244 : {
245 6 : init_letter(0xc0, 0xe0, "a");
246 6 : init_letter(0xc1, 0xe1, "a");
247 6 : init_letter(0xc2, 0xe2, "a");
248 6 : init_letter(0xc3, 0xe3, "a");
249 6 : init_letter(0xc4, 0xe4, "a");
250 6 : init_letter(0xc5, 0xe5, "a");
251 6 : init_letter(0xc6, 0xe6, "ae");
252 6 : init_letter(0xc7, 0xe7, "c");
253 6 : init_letter(0xc8, 0xe8, "e");
254 6 : init_letter(0xc9, 0xe9, "e");
255 6 : init_letter(0xca, 0xea, "e");
256 6 : init_letter(0xcb, 0xeb, "e");
257 6 : init_letter(0xcc, 0xec, "i");
258 6 : init_letter(0xcd, 0xed, "i");
259 6 : init_letter(0xce, 0xee, "i");
260 6 : init_letter(0xcf, 0xef, "i");
261 :
262 6 : init_letter(0xd0, 0xf0, "d");
263 6 : init_letter(0xd1, 0xf1, "n");
264 6 : init_letter(0xd2, 0xf2, "o");
265 6 : init_letter(0xd3, 0xf3, "o");
266 6 : init_letter(0xd4, 0xf4, "o");
267 6 : init_letter(0xd5, 0xf5, "o");
268 6 : init_letter(0xd6, 0xf6, "o");
269 6 : init_letter(0xd8, 0xf8, "o");
270 6 : init_letter(0xd9, 0xf9, "u");
271 6 : init_letter(0xda, 0xfa, "u");
272 6 : init_letter(0xdb, 0xfb, "u");
273 6 : init_letter(0xdc, 0xfc, "u");
274 6 : init_letter(0xdd, 0xfd, "y");
275 6 : init_letter(0xde, 0xfe, THORN_SORT_KEY);
276 :
277 6 : store_token("\337", TOKEN_LOWER, "ss", "SS");
278 6 : store_token("\377", TOKEN_LOWER, "y", "Y");
279 6 : }
280 :
281 252 : static void init_two_char_letter(char l1, char l2, char u1, char u2,
282 : const char *sk = 0)
283 : {
284 : char buf[6];
285 252 : buf[0] = '\\';
286 252 : buf[1] = '(';
287 252 : buf[2] = l1;
288 252 : buf[3] = l2;
289 252 : buf[4] = '\0';
290 252 : const char *p = strsave(buf);
291 252 : buf[2] = u1;
292 252 : buf[3] = u2;
293 252 : store_letter(p, strsave(buf), sk);
294 252 : buf[1] = '[';
295 252 : buf[4] = ']';
296 252 : buf[5] = '\0';
297 252 : p = strsave(buf);
298 252 : buf[2] = l1;
299 252 : buf[3] = l2;
300 252 : store_letter(strsave(buf), p, sk);
301 :
302 252 : }
303 :
304 6 : static void init_special_chars()
305 : {
306 : const char *p;
307 36 : for (p = "':^`~"; *p; p++)
308 210 : for (const char *q = "aeiouy"; *q; q++) {
309 : // Use a variable to work around bug in gcc 2.0
310 180 : char c = cmupper(*q);
311 180 : init_two_char_letter(*p, *q, *p, c);
312 : }
313 48 : for (p = "/l/o~n,coeaeij"; *p; p += 2) {
314 : // Use variables to work around bug in gcc 2.0
315 42 : char c0 = cmupper(p[0]);
316 42 : char c1 = cmupper(p[1]);
317 42 : init_two_char_letter(p[0], p[1], c0, c1);
318 : }
319 6 : init_two_char_letter('v', 's', 'v', 'S', "s");
320 6 : init_two_char_letter('v', 'z', 'v', 'Z', "z");
321 6 : init_two_char_letter('o', 'a', 'o', 'A', "a");
322 6 : init_two_char_letter('T', 'p', 'T', 'P', THORN_SORT_KEY);
323 6 : init_two_char_letter('-', 'd', '-', 'D');
324 :
325 6 : store_token("\\(ss", TOKEN_LOWER, 0, "SS");
326 6 : store_token("\\[ss]", TOKEN_LOWER, 0, "SS");
327 :
328 6 : store_token("\\(Sd", TOKEN_LOWER, "d", "\\(-D");
329 6 : store_token("\\[Sd]", TOKEN_LOWER, "d", "\\[-D]");
330 6 : store_token("\\(hy", TOKEN_HYPHEN);
331 6 : store_token("\\[hy]", TOKEN_HYPHEN);
332 6 : store_token("\\(en", TOKEN_RANGE_SEP);
333 6 : store_token("\\[en]", TOKEN_RANGE_SEP);
334 6 : }
335 :
336 6 : static void init_strings()
337 : {
338 : char buf[6];
339 6 : buf[0] = '\\';
340 6 : buf[1] = '*';
341 84 : for (const char *p = "'`^^,:~v_o./;"; *p; p++) {
342 78 : buf[2] = *p;
343 78 : buf[3] = '\0';
344 78 : store_token(strsave(buf), TOKEN_ACCENT);
345 78 : buf[2] = '[';
346 78 : buf[3] = *p;
347 78 : buf[4] = ']';
348 78 : buf[5] = '\0';
349 78 : store_token(strsave(buf), TOKEN_ACCENT);
350 : }
351 :
352 : // -ms special letters
353 6 : store_letter("\\*(th", "\\*(Th", THORN_SORT_KEY);
354 6 : store_letter("\\*[th]", "\\*[Th]", THORN_SORT_KEY);
355 6 : store_letter("\\*(d-", "\\*(D-");
356 6 : store_letter("\\*[d-]", "\\*[D-]");
357 6 : store_letter("\\*(ae", "\\*(Ae", "ae");
358 6 : store_letter("\\*[ae]", "\\*[Ae]", "ae");
359 6 : store_letter("\\*(oe", "\\*(Oe", "oe");
360 6 : store_letter("\\*[oe]", "\\*[Oe]", "oe");
361 :
362 6 : store_token("\\*3", TOKEN_LOWER, "y", "Y");
363 6 : store_token("\\*8", TOKEN_LOWER, "ss", "SS");
364 6 : store_token("\\*q", TOKEN_LOWER, "o", "O");
365 6 : }
366 :
367 : struct token_initer {
368 : token_initer();
369 : };
370 :
371 : static token_initer the_token_initer;
372 :
373 6 : token_initer::token_initer()
374 : {
375 6 : init_ascii();
376 6 : init_latin1();
377 6 : init_special_chars();
378 6 : init_strings();
379 6 : default_token_info.set(TOKEN_OTHER);
380 6 : }
381 :
382 : // Local Variables:
383 : // fill-column: 72
384 : // mode: C++
385 : // End:
386 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|