1 | #ifndef lint |
---|
2 | static char Rcs_Id[] = |
---|
3 | "$Id: tree.c,v 1.1.1.1 1997-09-03 21:08:12 ghudson Exp $"; |
---|
4 | #endif |
---|
5 | |
---|
6 | /* |
---|
7 | * tree.c - a hash style dictionary for user's personal words |
---|
8 | * |
---|
9 | * Pace Willisson, 1983 |
---|
10 | * Hash support added by Geoff Kuenning, 1987 |
---|
11 | * |
---|
12 | * Copyright 1987, 1988, 1989, 1992, 1993, Geoff Kuenning, Granada Hills, CA |
---|
13 | * All rights reserved. |
---|
14 | * |
---|
15 | * Redistribution and use in source and binary forms, with or without |
---|
16 | * modification, are permitted provided that the following conditions |
---|
17 | * are met: |
---|
18 | * |
---|
19 | * 1. Redistributions of source code must retain the above copyright |
---|
20 | * notice, this list of conditions and the following disclaimer. |
---|
21 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
22 | * notice, this list of conditions and the following disclaimer in the |
---|
23 | * documentation and/or other materials provided with the distribution. |
---|
24 | * 3. All modifications to the source code must be clearly marked as |
---|
25 | * such. Binary redistributions based on modified source code |
---|
26 | * must be clearly marked as modified versions in the documentation |
---|
27 | * and/or other materials provided with the distribution. |
---|
28 | * 4. All advertising materials mentioning features or use of this software |
---|
29 | * must display the following acknowledgment: |
---|
30 | * This product includes software developed by Geoff Kuenning and |
---|
31 | * other unpaid contributors. |
---|
32 | * 5. The name of Geoff Kuenning may not be used to endorse or promote |
---|
33 | * products derived from this software without specific prior |
---|
34 | * written permission. |
---|
35 | * |
---|
36 | * THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND |
---|
37 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
38 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
39 | * ARE DISCLAIMED. IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE |
---|
40 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
41 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
42 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
43 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
44 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
45 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
46 | * SUCH DAMAGE. |
---|
47 | */ |
---|
48 | |
---|
49 | /* |
---|
50 | * $Log: not supported by cvs2svn $ |
---|
51 | * Revision 1.56 1995/01/08 23:23:49 geoff |
---|
52 | * Support PDICTHOME for DOS purposes. |
---|
53 | * |
---|
54 | * Revision 1.55 1994/10/25 05:46:27 geoff |
---|
55 | * Fix a comment that looked to some compilers like it might be nested. |
---|
56 | * |
---|
57 | * Revision 1.54 1994/01/25 07:12:15 geoff |
---|
58 | * Get rid of all old RCS log lines in preparation for the 3.1 release. |
---|
59 | * |
---|
60 | */ |
---|
61 | |
---|
62 | #include <ctype.h> |
---|
63 | #include <errno.h> |
---|
64 | #include "config.h" |
---|
65 | #include "ispell.h" |
---|
66 | #include "proto.h" |
---|
67 | #include "msgs.h" |
---|
68 | |
---|
69 | void treeinit P ((char * p, char * LibDict)); |
---|
70 | static FILE * trydict P ((char * dictname, char * home, |
---|
71 | char * prefix, char * suffix)); |
---|
72 | static void treeload P ((FILE * dictf)); |
---|
73 | void treeinsert P ((char * word, int wordlen, int keep)); |
---|
74 | static struct dent * tinsert P ((struct dent * proto)); |
---|
75 | struct dent * treelookup P ((ichar_t * word)); |
---|
76 | #if SORTPERSONAL != 0 |
---|
77 | static int pdictcmp P ((struct dent ** enta, struct dent **entb)); |
---|
78 | #endif /* SORTPERSONAL != 0 */ |
---|
79 | void treeoutput P ((void)); |
---|
80 | VOID * mymalloc P ((unsigned int size)); |
---|
81 | void myfree P ((VOID * ptr)); |
---|
82 | #ifdef REGEX_LOOKUP |
---|
83 | char * do_regex_lookup P ((char * expr, int whence)); |
---|
84 | #endif /* REGEX_LOOKUP */ |
---|
85 | |
---|
86 | static int cantexpand = 0; /* NZ if an expansion fails */ |
---|
87 | static struct dent * pershtab; /* Aux hash table for personal dict */ |
---|
88 | static int pershsize = 0; /* Space available in aux hash table */ |
---|
89 | static int hcount = 0; /* Number of items in hash table */ |
---|
90 | |
---|
91 | /* |
---|
92 | * Hash table sizes. Prime is probably a good idea, though in truth I |
---|
93 | * whipped the algorithm up on the spot rather than looking it up, so |
---|
94 | * who knows what's really best? If we overflow the table, we just |
---|
95 | * use a double-and-add-1 algorithm. |
---|
96 | */ |
---|
97 | static int goodsizes[] = |
---|
98 | { |
---|
99 | 53, 223, 907, 3631 |
---|
100 | }; |
---|
101 | |
---|
102 | static char personaldict[MAXPATHLEN]; |
---|
103 | static FILE * dictf; |
---|
104 | static newwords = 0; |
---|
105 | |
---|
106 | void treeinit (p, LibDict) |
---|
107 | char * p; /* Value specified in -p switch */ |
---|
108 | char * LibDict; /* Root of default dict name */ |
---|
109 | { |
---|
110 | int abspath; /* NZ if p is abs path name */ |
---|
111 | char * h; /* Home directory name */ |
---|
112 | char seconddict[MAXPATHLEN]; /* Name of secondary dict */ |
---|
113 | FILE * secondf; /* Access to second dict file */ |
---|
114 | |
---|
115 | /* |
---|
116 | ** If -p was not specified, try to get a default name from the |
---|
117 | ** environment. After this point, if p is null, the the value in |
---|
118 | ** personaldict is the only possible name for the personal dictionary. |
---|
119 | ** If p is non-null, then there is a possibility that we should |
---|
120 | ** prepend HOME to get the correct dictionary name. |
---|
121 | */ |
---|
122 | if (p == NULL) |
---|
123 | p = getenv (PDICTVAR); |
---|
124 | /* |
---|
125 | ** if p exists and begins with '/' we don't really need HOME, |
---|
126 | ** but it's not very likely that HOME isn't set anyway (on non-DOS |
---|
127 | ** systems). |
---|
128 | */ |
---|
129 | if ((h = getenv (HOME)) == NULL) |
---|
130 | { |
---|
131 | #ifdef PDICTHOME |
---|
132 | h = PDICTHOME; |
---|
133 | #else /* PDICTHOME */ |
---|
134 | return; |
---|
135 | #endif /* PDICTHOME */ |
---|
136 | } |
---|
137 | |
---|
138 | if (p == NULL) |
---|
139 | { |
---|
140 | /* |
---|
141 | * No -p and no PDICTVAR. We will use LibDict and DEFPAFF to |
---|
142 | * figure out the name of the personal dictionary and where it |
---|
143 | * is. The rules are as follows: |
---|
144 | * |
---|
145 | * (1) If there is a local dictionary and a HOME dictionary, |
---|
146 | * both are loaded, but changes are saved in the local one. |
---|
147 | * The dictionary to save changes in is named "personaldict". |
---|
148 | * (2) Dictionaries named after the affix file take precedence |
---|
149 | * over dictionaries with the default suffix (DEFPAFF). |
---|
150 | * (3) Dictionaries named with the new default names |
---|
151 | * (DEFPDICT/DEFPAFF) take precedence over the old ones |
---|
152 | * (OLDPDICT/OLDPAFF). |
---|
153 | * (4) Dictionaries aren't combined unless they follow the same |
---|
154 | * naming scheme. |
---|
155 | * (5) If no dictionary can be found, a new one is created in |
---|
156 | * the home directory, named after DEFPDICT and the affix |
---|
157 | * file. |
---|
158 | */ |
---|
159 | dictf = trydict (personaldict, (char *) NULL, DEFPDICT, LibDict); |
---|
160 | secondf = trydict (seconddict, h, DEFPDICT, LibDict); |
---|
161 | if (dictf == NULL && secondf == NULL) |
---|
162 | { |
---|
163 | dictf = trydict (personaldict, (char *) NULL, DEFPDICT, DEFPAFF); |
---|
164 | secondf = trydict (seconddict, h, DEFPDICT, DEFPAFF); |
---|
165 | } |
---|
166 | if (dictf == NULL && secondf == NULL) |
---|
167 | { |
---|
168 | dictf = trydict (personaldict, (char *) NULL, OLDPDICT, LibDict); |
---|
169 | secondf = trydict (seconddict, h, OLDPDICT, LibDict); |
---|
170 | } |
---|
171 | if (dictf == NULL && secondf == NULL) |
---|
172 | { |
---|
173 | dictf = trydict (personaldict, (char *) NULL, OLDPDICT, OLDPAFF); |
---|
174 | secondf = trydict (seconddict, h, OLDPDICT, OLDPAFF); |
---|
175 | } |
---|
176 | if (personaldict[0] == '\0') |
---|
177 | { |
---|
178 | if (seconddict[0] != '\0') |
---|
179 | (void) strcpy (personaldict, seconddict); |
---|
180 | else |
---|
181 | (void) sprintf (personaldict, "%s/%s%s", h, DEFPDICT, LibDict); |
---|
182 | } |
---|
183 | if (dictf != NULL) |
---|
184 | { |
---|
185 | treeload (dictf); |
---|
186 | (void) fclose (dictf); |
---|
187 | } |
---|
188 | if (secondf != NULL) |
---|
189 | { |
---|
190 | treeload (secondf); |
---|
191 | (void) fclose (secondf); |
---|
192 | } |
---|
193 | } |
---|
194 | else |
---|
195 | { |
---|
196 | /* |
---|
197 | ** Figure out if p is an absolute path name. Note that beginning |
---|
198 | ** with "./" and "../" is considered an absolute path, since this |
---|
199 | ** still means we can't prepend HOME. |
---|
200 | */ |
---|
201 | abspath = (*p == '/' || strncmp (p, "./", 2) == 0 |
---|
202 | || strncmp (p, "../", 3) == 0); |
---|
203 | if (abspath) |
---|
204 | { |
---|
205 | (void) strcpy (personaldict, p); |
---|
206 | if ((dictf = fopen (personaldict, "r")) != NULL) |
---|
207 | { |
---|
208 | treeload (dictf); |
---|
209 | (void) fclose (dictf); |
---|
210 | } |
---|
211 | } |
---|
212 | else |
---|
213 | { |
---|
214 | /* |
---|
215 | ** The user gave us a relative pathname. We will try it |
---|
216 | ** locally, and if that doesn't work, we'll try the home |
---|
217 | ** directory. If neither exists, it will be created in |
---|
218 | ** the home directory if words are added. |
---|
219 | */ |
---|
220 | (void) strcpy (personaldict, p); |
---|
221 | if ((dictf = fopen (personaldict, "r")) != NULL) |
---|
222 | { |
---|
223 | treeload (dictf); |
---|
224 | (void) fclose (dictf); |
---|
225 | } |
---|
226 | else if (!abspath) |
---|
227 | { |
---|
228 | /* Try the home */ |
---|
229 | (void) sprintf (personaldict, "%s/%s", h, p); |
---|
230 | if ((dictf = fopen (personaldict, "r")) != NULL) |
---|
231 | { |
---|
232 | treeload (dictf); |
---|
233 | (void) fclose (dictf); |
---|
234 | } |
---|
235 | } |
---|
236 | /* |
---|
237 | * If dictf is null, we couldn't open the dictionary |
---|
238 | * specified in the -p switch. Complain. |
---|
239 | */ |
---|
240 | if (dictf == NULL) |
---|
241 | { |
---|
242 | (void) fprintf (stderr, CANT_OPEN, p); |
---|
243 | perror (""); |
---|
244 | return; |
---|
245 | } |
---|
246 | } |
---|
247 | } |
---|
248 | |
---|
249 | if (!lflag && !aflag |
---|
250 | && access (personaldict, 2) < 0 && errno != ENOENT) |
---|
251 | { |
---|
252 | (void) fprintf (stderr, TREE_C_CANT_UPDATE, personaldict); |
---|
253 | (void) sleep ((unsigned) 2); |
---|
254 | } |
---|
255 | } |
---|
256 | |
---|
257 | /* |
---|
258 | * Try to open a dictionary. As a side effect, leaves the dictionary |
---|
259 | * name in "filename" if one is found, and leaves a null string there |
---|
260 | * otherwise. |
---|
261 | */ |
---|
262 | static FILE * trydict (filename, home, prefix, suffix) |
---|
263 | char * filename; /* Where to store the file name */ |
---|
264 | char * home; /* Home directory */ |
---|
265 | char * prefix; /* Prefix for dictionary */ |
---|
266 | char * suffix; /* Suffix for dictionary */ |
---|
267 | { |
---|
268 | FILE * dictf; /* Access to dictionary file */ |
---|
269 | |
---|
270 | if (home == NULL) |
---|
271 | (void) sprintf (filename, "%s%s", prefix, suffix); |
---|
272 | else |
---|
273 | (void) sprintf (filename, "%s/%s%s", home, prefix, suffix); |
---|
274 | dictf = fopen (filename, "r"); |
---|
275 | if (dictf == NULL) |
---|
276 | filename[0] = '\0'; |
---|
277 | return dictf; |
---|
278 | } |
---|
279 | |
---|
280 | static void treeload (loadfile) |
---|
281 | register FILE * loadfile; /* File to load words from */ |
---|
282 | { |
---|
283 | char buf[BUFSIZ]; /* Buffer for reading pers dict */ |
---|
284 | |
---|
285 | while (fgets (buf, sizeof buf, loadfile) != NULL) |
---|
286 | treeinsert (buf, sizeof buf, 1); |
---|
287 | newwords = 0; |
---|
288 | } |
---|
289 | |
---|
290 | void treeinsert (word, wordlen, keep) |
---|
291 | char * word; /* Word to insert - must be canonical */ |
---|
292 | int wordlen; /* Length of the word buffer */ |
---|
293 | int keep; |
---|
294 | { |
---|
295 | register int i; |
---|
296 | struct dent wordent; |
---|
297 | register struct dent * dp; |
---|
298 | struct dent * olddp; |
---|
299 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
300 | struct dent * newdp; |
---|
301 | #endif |
---|
302 | struct dent * oldhtab; |
---|
303 | int oldhsize; |
---|
304 | ichar_t nword[INPUTWORDLEN + MAXAFFIXLEN]; |
---|
305 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
306 | int isvariant; |
---|
307 | #endif |
---|
308 | |
---|
309 | /* |
---|
310 | * Expand hash table when it is MAXPCT % full. |
---|
311 | */ |
---|
312 | if (!cantexpand && (hcount * 100) / MAXPCT >= pershsize) |
---|
313 | { |
---|
314 | oldhsize = pershsize; |
---|
315 | oldhtab = pershtab; |
---|
316 | for (i = 0; i < sizeof goodsizes / sizeof (goodsizes[0]); i++) |
---|
317 | { |
---|
318 | if (goodsizes[i] > pershsize) |
---|
319 | break; |
---|
320 | } |
---|
321 | if (i >= sizeof goodsizes / sizeof goodsizes[0]) |
---|
322 | pershsize += pershsize + 1; |
---|
323 | else |
---|
324 | pershsize = goodsizes[i]; |
---|
325 | pershtab = |
---|
326 | (struct dent *) calloc ((unsigned) pershsize, sizeof (struct dent)); |
---|
327 | if (pershtab == NULL) |
---|
328 | { |
---|
329 | (void) fprintf (stderr, TREE_C_NO_SPACE); |
---|
330 | /* |
---|
331 | * Try to continue anyway, since our overflow |
---|
332 | * algorithm can handle an overfull (100%+) table, |
---|
333 | * and the malloc very likely failed because we |
---|
334 | * already have such a huge table, so small mallocs |
---|
335 | * for overflow entries will still work. |
---|
336 | */ |
---|
337 | if (oldhtab == NULL) |
---|
338 | exit (1); /* No old table, can't go on */ |
---|
339 | (void) fprintf (stderr, TREE_C_TRY_ANYWAY); |
---|
340 | cantexpand = 1; /* Suppress further messages */ |
---|
341 | pershsize = oldhsize; /* Put things back */ |
---|
342 | pershtab = oldhtab; /* ... */ |
---|
343 | newwords = 1; /* And pretend it worked */ |
---|
344 | } |
---|
345 | else |
---|
346 | { |
---|
347 | /* |
---|
348 | * Re-insert old entries into new table |
---|
349 | */ |
---|
350 | for (i = 0; i < oldhsize; i++) |
---|
351 | { |
---|
352 | dp = &oldhtab[i]; |
---|
353 | if (dp->flagfield & USED) |
---|
354 | { |
---|
355 | #ifdef NO_CAPITALIZATION_SUPPORT |
---|
356 | (void) tinsert (dp); |
---|
357 | #else |
---|
358 | newdp = tinsert (dp); |
---|
359 | isvariant = (dp->flagfield & MOREVARIANTS); |
---|
360 | #endif |
---|
361 | dp = dp->next; |
---|
362 | #ifdef NO_CAPITALIZATION_SUPPORT |
---|
363 | while (dp != NULL) |
---|
364 | { |
---|
365 | (void) tinsert (dp); |
---|
366 | olddp = dp; |
---|
367 | dp = dp->next; |
---|
368 | free ((char *) olddp); |
---|
369 | } |
---|
370 | #else |
---|
371 | while (dp != NULL) |
---|
372 | { |
---|
373 | if (isvariant) |
---|
374 | { |
---|
375 | isvariant = dp->flagfield & MOREVARIANTS; |
---|
376 | olddp = newdp->next; |
---|
377 | newdp->next = dp; |
---|
378 | newdp = dp; |
---|
379 | dp = dp->next; |
---|
380 | newdp->next = olddp; |
---|
381 | } |
---|
382 | else |
---|
383 | { |
---|
384 | isvariant = dp->flagfield & MOREVARIANTS; |
---|
385 | newdp = tinsert (dp); |
---|
386 | olddp = dp; |
---|
387 | dp = dp->next; |
---|
388 | free ((char *) olddp); |
---|
389 | } |
---|
390 | } |
---|
391 | #endif |
---|
392 | } |
---|
393 | } |
---|
394 | if (oldhtab != NULL) |
---|
395 | free ((char *) oldhtab); |
---|
396 | } |
---|
397 | } |
---|
398 | |
---|
399 | /* |
---|
400 | ** We're ready to do the insertion. Start by creating a sample |
---|
401 | ** entry for the word. |
---|
402 | */ |
---|
403 | if (makedent (word, wordlen, &wordent) < 0) |
---|
404 | return; /* Word must be too big or something */ |
---|
405 | if (keep) |
---|
406 | wordent.flagfield |= KEEP; |
---|
407 | /* |
---|
408 | ** Now see if word or a variant is already in the table. We use the |
---|
409 | ** capitalized version so we'll find the header, if any. |
---|
410 | **/ |
---|
411 | (void) strtoichar (nword, word, sizeof nword, 1); |
---|
412 | upcase (nword); |
---|
413 | if ((dp = lookup (nword, 1)) != NULL) |
---|
414 | { |
---|
415 | /* It exists. Combine caps and set the keep flag. */ |
---|
416 | if (combinecaps (dp, &wordent) < 0) |
---|
417 | { |
---|
418 | free (wordent.word); |
---|
419 | return; |
---|
420 | } |
---|
421 | } |
---|
422 | else |
---|
423 | { |
---|
424 | /* It's new. Insert the word. */ |
---|
425 | dp = tinsert (&wordent); |
---|
426 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
427 | if (captype (dp->flagfield) == FOLLOWCASE) |
---|
428 | (void) addvheader (dp); |
---|
429 | #endif |
---|
430 | } |
---|
431 | newwords |= keep; |
---|
432 | } |
---|
433 | |
---|
434 | static struct dent * tinsert (proto) |
---|
435 | struct dent * proto; /* Prototype entry to copy */ |
---|
436 | { |
---|
437 | ichar_t iword[INPUTWORDLEN + MAXAFFIXLEN]; |
---|
438 | register int hcode; |
---|
439 | register struct dent * hp; /* Next trial entry in hash table */ |
---|
440 | register struct dent * php; /* Prev. value of hp, for chaining */ |
---|
441 | |
---|
442 | if (strtoichar (iword, proto->word, sizeof iword, 1)) |
---|
443 | (void) fprintf (stderr, WORD_TOO_LONG (proto->word)); |
---|
444 | #ifdef NO_CAPITALIZATION_SUPPORT |
---|
445 | upcase (iword); |
---|
446 | #endif |
---|
447 | hcode = hash (iword, pershsize); |
---|
448 | php = NULL; |
---|
449 | hp = &pershtab[hcode]; |
---|
450 | if (hp->flagfield & USED) |
---|
451 | { |
---|
452 | while (hp != NULL) |
---|
453 | { |
---|
454 | php = hp; |
---|
455 | hp = hp->next; |
---|
456 | } |
---|
457 | hp = (struct dent *) calloc (1, sizeof (struct dent)); |
---|
458 | if (hp == NULL) |
---|
459 | { |
---|
460 | (void) fprintf (stderr, TREE_C_NO_SPACE); |
---|
461 | exit (1); |
---|
462 | } |
---|
463 | } |
---|
464 | *hp = *proto; |
---|
465 | if (php != NULL) |
---|
466 | php->next = hp; |
---|
467 | hp->next = NULL; |
---|
468 | return hp; |
---|
469 | } |
---|
470 | |
---|
471 | struct dent * treelookup (word) |
---|
472 | register ichar_t * word; |
---|
473 | { |
---|
474 | register int hcode; |
---|
475 | register struct dent * hp; |
---|
476 | char chword[INPUTWORDLEN + MAXAFFIXLEN]; |
---|
477 | |
---|
478 | if (pershsize <= 0) |
---|
479 | return NULL; |
---|
480 | (void) ichartostr (chword, word, sizeof chword, 1); |
---|
481 | hcode = hash (word, pershsize); |
---|
482 | hp = &pershtab[hcode]; |
---|
483 | while (hp != NULL && (hp->flagfield & USED)) |
---|
484 | { |
---|
485 | if (strcmp (chword, hp->word) == 0) |
---|
486 | break; |
---|
487 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
488 | while (hp->flagfield & MOREVARIANTS) |
---|
489 | hp = hp->next; |
---|
490 | #endif |
---|
491 | hp = hp->next; |
---|
492 | } |
---|
493 | if (hp != NULL && (hp->flagfield & USED)) |
---|
494 | return hp; |
---|
495 | else |
---|
496 | return NULL; |
---|
497 | } |
---|
498 | |
---|
499 | #if SORTPERSONAL != 0 |
---|
500 | /* Comparison routine for sorting the personal dictionary with qsort */ |
---|
501 | static int pdictcmp (enta, entb) |
---|
502 | struct dent ** enta; |
---|
503 | struct dent ** entb; |
---|
504 | { |
---|
505 | |
---|
506 | /* The parentheses around *enta and *entb below are NECESSARY! |
---|
507 | ** Otherwise the compiler reads it as *(enta->word), or |
---|
508 | ** enta->word[0], which is illegal (but pcc takes it and |
---|
509 | ** produces wrong code). |
---|
510 | **/ |
---|
511 | return casecmp ((*enta)->word, (*entb)->word, 1); |
---|
512 | } |
---|
513 | #endif |
---|
514 | |
---|
515 | void treeoutput () |
---|
516 | { |
---|
517 | register struct dent * cent; /* Current entry */ |
---|
518 | register struct dent * lent; /* Linked entry */ |
---|
519 | #if SORTPERSONAL != 0 |
---|
520 | int pdictsize; /* Number of entries to write */ |
---|
521 | struct dent ** sortlist; /* List of entries to be sorted */ |
---|
522 | register struct dent ** sortptr; /* Handy pointer into sortlist */ |
---|
523 | #endif |
---|
524 | register struct dent * ehtab; /* End of pershtab, for fast looping */ |
---|
525 | |
---|
526 | if (newwords == 0) |
---|
527 | return; |
---|
528 | |
---|
529 | if ((dictf = fopen (personaldict, "w")) == NULL) |
---|
530 | { |
---|
531 | (void) fprintf (stderr, CANT_CREATE, personaldict); |
---|
532 | return; |
---|
533 | } |
---|
534 | |
---|
535 | #if SORTPERSONAL != 0 |
---|
536 | /* |
---|
537 | ** If we are going to sort the personal dictionary, we must know |
---|
538 | ** how many items are going to be sorted. |
---|
539 | */ |
---|
540 | pdictsize = 0; |
---|
541 | if (hcount >= SORTPERSONAL) |
---|
542 | sortlist = NULL; |
---|
543 | else |
---|
544 | { |
---|
545 | for (cent = pershtab, ehtab = pershtab + pershsize; |
---|
546 | cent < ehtab; |
---|
547 | cent++) |
---|
548 | { |
---|
549 | for (lent = cent; lent != NULL; lent = lent->next) |
---|
550 | { |
---|
551 | if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP)) |
---|
552 | pdictsize++; |
---|
553 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
554 | while (lent->flagfield & MOREVARIANTS) |
---|
555 | lent = lent->next; |
---|
556 | #endif |
---|
557 | } |
---|
558 | } |
---|
559 | for (cent = hashtbl, ehtab = hashtbl + hashsize; |
---|
560 | cent < ehtab; |
---|
561 | cent++) |
---|
562 | { |
---|
563 | if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP)) |
---|
564 | { |
---|
565 | /* |
---|
566 | ** We only want to count variant headers |
---|
567 | ** and standalone entries. These happen |
---|
568 | ** to share the characteristics in the |
---|
569 | ** test below. This test will appear |
---|
570 | ** several more times in this routine. |
---|
571 | */ |
---|
572 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
573 | if (captype (cent->flagfield) != FOLLOWCASE |
---|
574 | && cent->word != NULL) |
---|
575 | #endif |
---|
576 | pdictsize++; |
---|
577 | } |
---|
578 | } |
---|
579 | sortlist = (struct dent **) malloc (pdictsize * sizeof (struct dent)); |
---|
580 | } |
---|
581 | if (sortlist == NULL) |
---|
582 | { |
---|
583 | #endif |
---|
584 | for (cent = pershtab, ehtab = pershtab + pershsize; |
---|
585 | cent < ehtab; |
---|
586 | cent++) |
---|
587 | { |
---|
588 | for (lent = cent; lent != NULL; lent = lent->next) |
---|
589 | { |
---|
590 | if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP)) |
---|
591 | { |
---|
592 | toutent (dictf, lent, 1); |
---|
593 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
594 | while (lent->flagfield & MOREVARIANTS) |
---|
595 | lent = lent->next; |
---|
596 | #endif |
---|
597 | } |
---|
598 | } |
---|
599 | } |
---|
600 | for (cent = hashtbl, ehtab = hashtbl + hashsize; |
---|
601 | cent < ehtab; |
---|
602 | cent++) |
---|
603 | { |
---|
604 | if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP)) |
---|
605 | { |
---|
606 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
607 | if (captype (cent->flagfield) != FOLLOWCASE |
---|
608 | && cent->word != NULL) |
---|
609 | #endif |
---|
610 | toutent (dictf, cent, 1); |
---|
611 | } |
---|
612 | } |
---|
613 | #if SORTPERSONAL != 0 |
---|
614 | return; |
---|
615 | } |
---|
616 | /* |
---|
617 | ** Produce dictionary in sorted order. We used to do this |
---|
618 | ** destructively, but that turns out to fail because in some modes |
---|
619 | ** the dictionary is written more than once. So we build an |
---|
620 | ** auxiliary pointer table (in sortlist) and sort that. This |
---|
621 | ** is faster anyway, though it uses more memory. |
---|
622 | */ |
---|
623 | sortptr = sortlist; |
---|
624 | for (cent = pershtab, ehtab = pershtab + pershsize; cent < ehtab; cent++) |
---|
625 | { |
---|
626 | for (lent = cent; lent != NULL; lent = lent->next) |
---|
627 | { |
---|
628 | if ((lent->flagfield & (USED | KEEP)) == (USED | KEEP)) |
---|
629 | { |
---|
630 | *sortptr++ = lent; |
---|
631 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
632 | while (lent->flagfield & MOREVARIANTS) |
---|
633 | lent = lent->next; |
---|
634 | #endif |
---|
635 | } |
---|
636 | } |
---|
637 | } |
---|
638 | for (cent = hashtbl, ehtab = hashtbl + hashsize; cent < ehtab; cent++) |
---|
639 | { |
---|
640 | if ((cent->flagfield & (USED | KEEP)) == (USED | KEEP)) |
---|
641 | { |
---|
642 | #ifndef NO_CAPITALIZATION_SUPPORT |
---|
643 | if (captype (cent->flagfield) != FOLLOWCASE |
---|
644 | && cent->word != NULL) |
---|
645 | #endif |
---|
646 | *sortptr++ = cent; |
---|
647 | } |
---|
648 | } |
---|
649 | /* Sort the list */ |
---|
650 | qsort ((char *) sortlist, (unsigned) pdictsize, |
---|
651 | sizeof (sortlist[0]), |
---|
652 | (int (*) P ((const void *, const void *))) pdictcmp); |
---|
653 | /* Write it out */ |
---|
654 | for (sortptr = sortlist; --pdictsize >= 0; ) |
---|
655 | toutent (dictf, *sortptr++, 1); |
---|
656 | free ((char *) sortlist); |
---|
657 | #endif |
---|
658 | |
---|
659 | newwords = 0; |
---|
660 | |
---|
661 | (void) fclose (dictf); |
---|
662 | } |
---|
663 | |
---|
664 | VOID * mymalloc (size) |
---|
665 | unsigned int size; |
---|
666 | { |
---|
667 | |
---|
668 | return malloc ((unsigned) size); |
---|
669 | } |
---|
670 | |
---|
671 | void myfree (ptr) |
---|
672 | VOID * ptr; |
---|
673 | { |
---|
674 | if (hashstrings != NULL && (char *) ptr >= hashstrings |
---|
675 | && (char *) ptr <= hashstrings + hashheader.stringsize) |
---|
676 | return; /* Can't free stuff in hashstrings */ |
---|
677 | free (ptr); |
---|
678 | } |
---|
679 | |
---|
680 | #ifdef REGEX_LOOKUP |
---|
681 | |
---|
682 | /* check the hashed dictionary for words matching the regex. return the */ |
---|
683 | /* a matching string if found else return NULL */ |
---|
684 | char * do_regex_lookup (expr, whence) |
---|
685 | char * expr; /* regular expression to use in the match */ |
---|
686 | int whence; /* 0 = start at the beg with new regx, else */ |
---|
687 | /* continue from cur point w/ old regex */ |
---|
688 | { |
---|
689 | static struct dent * curent; |
---|
690 | static int curindex; |
---|
691 | static struct dent * curpersent; |
---|
692 | static int curpersindex; |
---|
693 | static char * cmp_expr; |
---|
694 | char dummy[INPUTWORDLEN + MAXAFFIXLEN]; |
---|
695 | ichar_t * is; |
---|
696 | |
---|
697 | if (whence == 0) |
---|
698 | { |
---|
699 | is = strtosichar (expr, 0); |
---|
700 | upcase (is); |
---|
701 | expr = ichartosstr (is, 1); |
---|
702 | cmp_expr = REGCMP (expr); |
---|
703 | curent = hashtbl; |
---|
704 | curindex = 0; |
---|
705 | curpersent = pershtab; |
---|
706 | curpersindex = 0; |
---|
707 | } |
---|
708 | |
---|
709 | /* search the dictionary until the word is found or the words run out */ |
---|
710 | for ( ; curindex < hashsize; curent++, curindex++) |
---|
711 | { |
---|
712 | if (curent->word != NULL |
---|
713 | && REGEX (cmp_expr, curent->word, dummy) != NULL) |
---|
714 | { |
---|
715 | curindex++; |
---|
716 | /* Everybody's gotta write a wierd expression once in a while! */ |
---|
717 | return curent++->word; |
---|
718 | } |
---|
719 | } |
---|
720 | /* Try the personal dictionary too */ |
---|
721 | for ( ; curpersindex < pershsize; curpersent++, curpersindex++) |
---|
722 | { |
---|
723 | if ((curpersent->flagfield & USED) != 0 |
---|
724 | && curpersent->word != NULL |
---|
725 | && REGEX (cmp_expr, curpersent->word, dummy) != NULL) |
---|
726 | { |
---|
727 | curpersindex++; |
---|
728 | /* Everybody's gotta write a wierd expression once in a while! */ |
---|
729 | return curpersent++->word; |
---|
730 | } |
---|
731 | } |
---|
732 | return NULL; |
---|
733 | } |
---|
734 | #endif /* REGEX_LOOKUP */ |
---|