1 : /******* BEGIN LICENSE BLOCK *******
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Initial Developers of the Original Code are Kevin Hendricks (MySpell)
15 : * and László Németh (Hunspell). Portions created by the Initial Developers
16 : * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved.
17 : *
18 : * Contributor(s): Kevin Hendricks (kevin.hendricks@sympatico.ca)
19 : * David Einstein (deinst@world.std.com)
20 : * László Németh (nemethl@gyorsposta.hu)
21 : * Caolan McNamara (caolanm@redhat.com)
22 : * Davide Prina
23 : * Giuseppe Modugno
24 : * Gianluca Turconi
25 : * Simon Brouwer
26 : * Noll Janos
27 : * Biro Arpad
28 : * Goldman Eleonora
29 : * Sarlos Tamas
30 : * Bencsath Boldizsar
31 : * Halacsy Peter
32 : * Dvornik Laszlo
33 : * Gefferth Andras
34 : * Nagy Viktor
35 : * Varga Daniel
36 : * Chris Halls
37 : * Rene Engelhard
38 : * Bram Moolenaar
39 : * Dafydd Jones
40 : * Harri Pitkanen
41 : * Andras Timar
42 : * Tor Lillqvist
43 : *
44 : * Alternatively, the contents of this file may be used under the terms of
45 : * either the GNU General Public License Version 2 or later (the "GPL"), or
46 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
47 : * in which case the provisions of the GPL or the LGPL are applicable instead
48 : * of those above. If you wish to allow use of your version of this file only
49 : * under the terms of either the GPL or the LGPL, and not to allow others to
50 : * use your version of this file under the terms of the MPL, indicate your
51 : * decision by deleting the provisions above and replace them with the notice
52 : * and other provisions required by the GPL or the LGPL. If you do not delete
53 : * the provisions above, a recipient may use your version of this file under
54 : * the terms of any one of the MPL, the GPL or the LGPL.
55 : *
56 : ******* END LICENSE BLOCK *******/
57 :
58 : #include <stdlib.h>
59 : #include <string.h>
60 : #include <stdio.h>
61 :
62 : #include "hunspell.hxx"
63 : #include "hunspell.h"
64 : #ifndef MOZILLA_CLIENT
65 : # include "config.h"
66 : #endif
67 : #include "csutil.hxx"
68 :
69 110 : Hunspell::Hunspell(const char * affpath, const char * dpath, const char * key)
70 : {
71 110 : encoding = NULL;
72 110 : csconv = NULL;
73 110 : utf8 = 0;
74 110 : complexprefixes = 0;
75 110 : affixpath = mystrdup(affpath);
76 110 : maxdic = 0;
77 :
78 : /* first set up the hash manager */
79 110 : pHMgr[0] = new HashMgr(dpath, affpath, key);
80 110 : if (pHMgr[0]) maxdic = 1;
81 :
82 : /* next set up the affix manager */
83 : /* it needs access to the hash manager lookup methods */
84 110 : pAMgr = new AffixMgr(affpath, pHMgr, &maxdic, key);
85 :
86 : /* get the preferred try string and the dictionary */
87 : /* encoding from the Affix Manager for that dictionary */
88 110 : char * try_string = pAMgr->get_try_string();
89 110 : encoding = pAMgr->get_encoding();
90 110 : langnum = pAMgr->get_langnum();
91 110 : utf8 = pAMgr->get_utf8();
92 110 : if (!utf8)
93 82 : csconv = get_current_cs(encoding);
94 110 : complexprefixes = pAMgr->get_complexprefixes();
95 110 : wordbreak = pAMgr->get_breaktable();
96 :
97 : /* and finally set up the suggestion manager */
98 110 : pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr);
99 110 : if (try_string) free(try_string);
100 110 : }
101 :
102 110 : Hunspell::~Hunspell()
103 : {
104 110 : if (pSMgr) delete pSMgr;
105 110 : if (pAMgr) delete pAMgr;
106 220 : for (int i = 0; i < maxdic; i++) delete pHMgr[i];
107 110 : maxdic = 0;
108 110 : pSMgr = NULL;
109 110 : pAMgr = NULL;
110 : #ifdef MOZILLA_CLIENT
111 110 : delete [] csconv;
112 : #endif
113 110 : csconv= NULL;
114 110 : if (encoding) free(encoding);
115 110 : encoding = NULL;
116 110 : if (affixpath) free(affixpath);
117 110 : affixpath = NULL;
118 110 : }
119 :
120 : // load extra dictionaries
121 0 : int Hunspell::add_dic(const char * dpath, const char * key) {
122 0 : if (maxdic == MAXDIC || !affixpath) return 1;
123 0 : pHMgr[maxdic] = new HashMgr(dpath, affixpath, key);
124 0 : if (pHMgr[maxdic]) maxdic++; else return 1;
125 0 : return 0;
126 : }
127 :
128 : // make a copy of src at destination while removing all leading
129 : // blanks and removing any trailing periods after recording
130 : // their presence with the abbreviation flag
131 : // also since already going through character by character,
132 : // set the capitalization type
133 : // return the length of the "cleaned" (and UTF-8 encoded) word
134 :
135 1083 : int Hunspell::cleanword2(char * dest, const char * src,
136 : w_char * dest_utf, int * nc, int * pcaptype, int * pabbrev)
137 : {
138 1083 : unsigned char * p = (unsigned char *) dest;
139 1083 : const unsigned char * q = (const unsigned char * ) src;
140 :
141 : // first skip over any leading blanks
142 1083 : while ((*q != '\0') && (*q == ' ')) q++;
143 :
144 : // now strip off any trailing periods (recording their presence)
145 1083 : *pabbrev = 0;
146 1083 : int nl = strlen((const char *)q);
147 2188 : while ((nl > 0) && (*(q+nl-1)=='.')) {
148 22 : nl--;
149 22 : (*pabbrev)++;
150 : }
151 :
152 : // if no characters are left it can't be capitalized
153 1083 : if (nl <= 0) {
154 2 : *pcaptype = NOCAP;
155 2 : *p = '\0';
156 2 : return 0;
157 : }
158 :
159 1081 : strncpy(dest, (char *) q, nl);
160 1081 : *(dest + nl) = '\0';
161 1081 : nl = strlen(dest);
162 1081 : if (utf8) {
163 236 : *nc = u8_u16(dest_utf, MAXWORDLEN, dest);
164 : // don't check too long words
165 236 : if (*nc >= MAXWORDLEN) return 0;
166 236 : if (*nc == -1) { // big Unicode character (non BMP area)
167 5 : *pcaptype = NOCAP;
168 5 : return nl;
169 : }
170 231 : *pcaptype = get_captype_utf8(dest_utf, *nc, langnum);
171 : } else {
172 845 : *pcaptype = get_captype(dest, nl, csconv);
173 845 : *nc = nl;
174 : }
175 1076 : return nl;
176 : }
177 :
178 0 : int Hunspell::cleanword(char * dest, const char * src,
179 : int * pcaptype, int * pabbrev)
180 : {
181 0 : unsigned char * p = (unsigned char *) dest;
182 0 : const unsigned char * q = (const unsigned char * ) src;
183 0 : int firstcap = 0;
184 :
185 : // first skip over any leading blanks
186 0 : while ((*q != '\0') && (*q == ' ')) q++;
187 :
188 : // now strip off any trailing periods (recording their presence)
189 0 : *pabbrev = 0;
190 0 : int nl = strlen((const char *)q);
191 0 : while ((nl > 0) && (*(q+nl-1)=='.')) {
192 0 : nl--;
193 0 : (*pabbrev)++;
194 : }
195 :
196 : // if no characters are left it can't be capitalized
197 0 : if (nl <= 0) {
198 0 : *pcaptype = NOCAP;
199 0 : *p = '\0';
200 0 : return 0;
201 : }
202 :
203 : // now determine the capitalization type of the first nl letters
204 0 : int ncap = 0;
205 0 : int nneutral = 0;
206 0 : int nc = 0;
207 :
208 0 : if (!utf8) {
209 0 : while (nl > 0) {
210 0 : nc++;
211 0 : if (csconv[(*q)].ccase) ncap++;
212 0 : if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++;
213 0 : *p++ = *q++;
214 0 : nl--;
215 : }
216 : // remember to terminate the destination string
217 0 : *p = '\0';
218 0 : firstcap = csconv[(unsigned char)(*dest)].ccase;
219 : } else {
220 : unsigned short idx;
221 : w_char t[MAXWORDLEN];
222 0 : nc = u8_u16(t, MAXWORDLEN, src);
223 0 : for (int i = 0; i < nc; i++) {
224 0 : idx = (t[i].h << 8) + t[i].l;
225 0 : unsigned short low = unicodetolower(idx, langnum);
226 0 : if (idx != low) ncap++;
227 0 : if (unicodetoupper(idx, langnum) == low) nneutral++;
228 : }
229 0 : u16_u8(dest, MAXWORDUTF8LEN, t, nc);
230 0 : if (ncap) {
231 0 : idx = (t[0].h << 8) + t[0].l;
232 0 : firstcap = (idx != unicodetolower(idx, langnum));
233 : }
234 : }
235 :
236 : // now finally set the captype
237 0 : if (ncap == 0) {
238 0 : *pcaptype = NOCAP;
239 0 : } else if ((ncap == 1) && firstcap) {
240 0 : *pcaptype = INITCAP;
241 0 : } else if ((ncap == nc) || ((ncap + nneutral) == nc)){
242 0 : *pcaptype = ALLCAP;
243 0 : } else if ((ncap > 1) && firstcap) {
244 0 : *pcaptype = HUHINITCAP;
245 : } else {
246 0 : *pcaptype = HUHCAP;
247 : }
248 0 : return strlen(dest);
249 : }
250 :
251 0 : void Hunspell::mkallcap(char * p)
252 : {
253 0 : if (utf8) {
254 : w_char u[MAXWORDLEN];
255 0 : int nc = u8_u16(u, MAXWORDLEN, p);
256 : unsigned short idx;
257 0 : for (int i = 0; i < nc; i++) {
258 0 : idx = (u[i].h << 8) + u[i].l;
259 0 : if (idx != unicodetoupper(idx, langnum)) {
260 0 : u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);
261 0 : u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);
262 : }
263 : }
264 0 : u16_u8(p, MAXWORDUTF8LEN, u, nc);
265 : } else {
266 0 : while (*p != '\0') {
267 0 : *p = csconv[((unsigned char) *p)].cupper;
268 0 : p++;
269 : }
270 : }
271 0 : }
272 :
273 0 : int Hunspell::mkallcap2(char * p, w_char * u, int nc)
274 : {
275 0 : if (utf8) {
276 : unsigned short idx;
277 0 : for (int i = 0; i < nc; i++) {
278 0 : idx = (u[i].h << 8) + u[i].l;
279 0 : unsigned short up = unicodetoupper(idx, langnum);
280 0 : if (idx != up) {
281 0 : u[i].h = (unsigned char) (up >> 8);
282 0 : u[i].l = (unsigned char) (up & 0x00FF);
283 : }
284 : }
285 0 : u16_u8(p, MAXWORDUTF8LEN, u, nc);
286 0 : return strlen(p);
287 : } else {
288 0 : while (*p != '\0') {
289 0 : *p = csconv[((unsigned char) *p)].cupper;
290 0 : p++;
291 : }
292 : }
293 0 : return nc;
294 : }
295 :
296 :
297 0 : void Hunspell::mkallsmall(char * p)
298 : {
299 0 : while (*p != '\0') {
300 0 : *p = csconv[((unsigned char) *p)].clower;
301 0 : p++;
302 : }
303 0 : }
304 :
305 145 : int Hunspell::mkallsmall2(char * p, w_char * u, int nc)
306 : {
307 145 : if (utf8) {
308 : unsigned short idx;
309 288 : for (int i = 0; i < nc; i++) {
310 256 : idx = (u[i].h << 8) + u[i].l;
311 256 : unsigned short low = unicodetolower(idx, langnum);
312 256 : if (idx != low) {
313 104 : u[i].h = (unsigned char) (low >> 8);
314 104 : u[i].l = (unsigned char) (low & 0x00FF);
315 : }
316 : }
317 32 : u16_u8(p, MAXWORDUTF8LEN, u, nc);
318 32 : return strlen(p);
319 : } else {
320 1446 : while (*p != '\0') {
321 1220 : *p = csconv[((unsigned char) *p)].clower;
322 1220 : p++;
323 : }
324 : }
325 113 : return nc;
326 : }
327 :
328 : // convert UTF-8 sharp S codes to latin 1
329 30 : char * Hunspell::sharps_u8_l1(char * dest, char * source) {
330 30 : char * p = dest;
331 30 : *p = *source;
332 441 : for (p++, source++; *(source - 1); p++, source++) {
333 411 : *p = *source;
334 411 : if (*source == '\x9F') *--p = '\xDF';
335 : }
336 30 : return dest;
337 : }
338 :
339 : // recursive search for right ss - sharp s permutations
340 132 : hentry * Hunspell::spellsharps(char * base, char * pos, int n,
341 : int repnum, char * tmp, int * info, char **root) {
342 132 : pos = strstr(pos, "ss");
343 132 : if (pos && (n < MAXSHARPS)) {
344 60 : *pos = '\xC3';
345 60 : *(pos + 1) = '\x9F';
346 60 : hentry * h = spellsharps(base, pos + 2, n + 1, repnum + 1, tmp, info, root);
347 60 : if (h) return h;
348 50 : *pos = 's';
349 50 : *(pos + 1) = 's';
350 50 : h = spellsharps(base, pos + 2, n + 1, repnum, tmp, info, root);
351 50 : if (h) return h;
352 72 : } else if (repnum > 0) {
353 60 : if (utf8) return checkword(base, info, root);
354 30 : return checkword(sharps_u8_l1(tmp, base), info, root);
355 : }
356 54 : return NULL;
357 : }
358 :
359 85 : int Hunspell::is_keepcase(const hentry * rv) {
360 51 : return pAMgr && rv->astr && pAMgr->get_keepcase() &&
361 136 : TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen);
362 : }
363 :
364 : /* insert a word to the beginning of the suggestion array and return ns */
365 0 : int Hunspell::insert_sug(char ***slst, char * word, int ns) {
366 0 : char * dup = mystrdup(word);
367 0 : if (!dup) return ns;
368 0 : if (ns == MAXSUGGESTION) {
369 0 : ns--;
370 0 : free((*slst)[ns]);
371 : }
372 0 : for (int k = ns; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
373 0 : (*slst)[0] = dup;
374 0 : return ns + 1;
375 : }
376 :
377 1083 : int Hunspell::spell(const char * word, int * info, char ** root)
378 : {
379 1083 : struct hentry * rv=NULL;
380 : // need larger vector. For example, Turkish capital letter I converted a
381 : // 2-byte UTF-8 character (dotless i) by mkallsmall.
382 : char cw[MAXWORDUTF8LEN];
383 : char wspace[MAXWORDUTF8LEN];
384 : w_char unicw[MAXWORDLEN];
385 : // Hunspell supports XML input of the simplified API (see manual)
386 1083 : if (strcmp(word, SPELL_XML) == 0) return 1;
387 1083 : int nc = strlen(word);
388 1083 : int wl2 = 0;
389 1083 : if (utf8) {
390 236 : if (nc >= MAXWORDUTF8LEN) return 0;
391 : } else {
392 847 : if (nc >= MAXWORDLEN) return 0;
393 : }
394 1083 : int captype = 0;
395 1083 : int abbv = 0;
396 1083 : int wl = 0;
397 :
398 : // input conversion
399 1083 : RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
400 1083 : if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
401 1080 : else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
402 :
403 1083 : int info2 = 0;
404 1083 : if (wl == 0 || maxdic == 0) return 1;
405 1081 : if (root) *root = NULL;
406 :
407 : // allow numbers with dots, dashes and commas (but forbid double separators: "..", "--" etc.)
408 : enum { NBEGIN, NNUM, NSEP };
409 1081 : int nstate = NBEGIN;
410 : int i;
411 :
412 1395 : for (i = 0; (i < wl); i++) {
413 1375 : if ((cw[i] <= '9') && (cw[i] >= '0')) {
414 297 : nstate = NNUM;
415 1078 : } else if ((cw[i] == ',') || (cw[i] == '.') || (cw[i] == '-')) {
416 23 : if ((nstate == NSEP) || (i == 0)) break;
417 17 : nstate = NSEP;
418 1055 : } else break;
419 : }
420 1081 : if ((i == wl) && (nstate == NNUM)) return 1;
421 1061 : if (!info) info = &info2; else *info = 0;
422 :
423 1061 : switch(captype) {
424 : case HUHCAP:
425 : case HUHINITCAP:
426 69 : *info += SPELL_ORIGCAP;
427 : case NOCAP: {
428 909 : rv = checkword(cw, info, root);
429 909 : if ((abbv) && !(rv)) {
430 2 : memcpy(wspace,cw,wl);
431 2 : *(wspace+wl) = '.';
432 2 : *(wspace+wl+1) = '\0';
433 2 : rv = checkword(wspace, info, root);
434 : }
435 909 : break;
436 : }
437 : case ALLCAP: {
438 36 : *info += SPELL_ORIGCAP;
439 36 : rv = checkword(cw, info, root);
440 36 : if (rv) break;
441 27 : if (abbv) {
442 5 : memcpy(wspace,cw,wl);
443 5 : *(wspace+wl) = '.';
444 5 : *(wspace+wl+1) = '\0';
445 5 : rv = checkword(wspace, info, root);
446 5 : if (rv) break;
447 : }
448 : // Spec. prefix handling for Catalan, French, Italian:
449 : // prefixes separated by apostrophe (SANT'ELIA -> Sant'+Elia).
450 27 : if (pAMgr && strchr(cw, '\'')) {
451 2 : wl = mkallsmall2(cw, unicw, nc);
452 : //There are no really sane circumstances where this could fail,
453 : //but anyway...
454 2 : if (char * apostrophe = strchr(cw, '\'')) {
455 2 : if (utf8) {
456 : w_char tmpword[MAXWORDLEN];
457 0 : *apostrophe = '\0';
458 0 : wl2 = u8_u16(tmpword, MAXWORDLEN, cw);
459 0 : *apostrophe = '\'';
460 0 : if (wl2 < nc) {
461 0 : mkinitcap2(apostrophe + 1, unicw + wl2 + 1, nc - wl2 - 1);
462 0 : rv = checkword(cw, info, root);
463 0 : if (rv) break;
464 : }
465 : } else {
466 2 : mkinitcap2(apostrophe + 1, unicw, nc);
467 2 : rv = checkword(cw, info, root);
468 2 : if (rv) break;
469 : }
470 : }
471 2 : mkinitcap2(cw, unicw, nc);
472 2 : rv = checkword(cw, info, root);
473 2 : if (rv) break;
474 : }
475 27 : if (pAMgr && pAMgr->get_checksharps() && strstr(cw, "SS")) {
476 : char tmpword[MAXWORDUTF8LEN];
477 10 : wl = mkallsmall2(cw, unicw, nc);
478 10 : memcpy(wspace,cw,(wl+1));
479 10 : rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
480 10 : if (!rv) {
481 8 : wl2 = mkinitcap2(cw, unicw, nc);
482 8 : rv = spellsharps(cw, cw, 0, 0, tmpword, info, root);
483 : }
484 10 : if ((abbv) && !(rv)) {
485 2 : *(wspace+wl) = '.';
486 2 : *(wspace+wl+1) = '\0';
487 2 : rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
488 2 : if (!rv) {
489 2 : memcpy(wspace, cw, wl2);
490 2 : *(wspace+wl2) = '.';
491 2 : *(wspace+wl2+1) = '\0';
492 2 : rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
493 : }
494 : }
495 10 : if (rv) break;
496 : }
497 : }
498 : case INITCAP: {
499 133 : *info += SPELL_ORIGCAP;
500 133 : wl = mkallsmall2(cw, unicw, nc);
501 133 : memcpy(wspace,cw,(wl+1));
502 133 : wl2 = mkinitcap2(cw, unicw, nc);
503 133 : if (captype == INITCAP) *info += SPELL_INITCAP;
504 133 : rv = checkword(cw, info, root);
505 133 : if (captype == INITCAP) *info -= SPELL_INITCAP;
506 : // forbid bad capitalization
507 : // (for example, ijs -> Ijs instead of IJs in Dutch)
508 : // use explicit forms in dic: Ijs/F (F = FORBIDDENWORD flag)
509 133 : if (*info & SPELL_FORBIDDEN) {
510 2 : rv = NULL;
511 2 : break;
512 : }
513 131 : if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL;
514 131 : if (rv) break;
515 :
516 73 : rv = checkword(wspace, info, root);
517 73 : if (abbv && !rv) {
518 :
519 4 : *(wspace+wl) = '.';
520 4 : *(wspace+wl+1) = '\0';
521 4 : rv = checkword(wspace, info, root);
522 4 : if (!rv) {
523 2 : memcpy(wspace, cw, wl2);
524 2 : *(wspace+wl2) = '.';
525 2 : *(wspace+wl2+1) = '\0';
526 2 : if (captype == INITCAP) *info += SPELL_INITCAP;
527 2 : rv = checkword(wspace, info, root);
528 2 : if (captype == INITCAP) *info -= SPELL_INITCAP;
529 2 : if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL;
530 2 : break;
531 : }
532 : }
533 81 : if (rv && is_keepcase(rv) &&
534 : ((captype == ALLCAP) ||
535 : // if CHECKSHARPS: KEEPCASE words with \xDF are allowed
536 : // in INITCAP form, too.
537 5 : !(pAMgr->get_checksharps() &&
538 1 : ((utf8 && strstr(wspace, "\xC3\x9F")) ||
539 8 : (!utf8 && strchr(wspace, '\xDF')))))) rv = NULL;
540 71 : break;
541 : }
542 : }
543 :
544 1061 : if (rv) {
545 557 : if (pAMgr && pAMgr->get_warn() && rv->astr &&
546 1 : TESTAFF(rv->astr, pAMgr->get_warn(), rv->alen)) {
547 1 : *info += SPELL_WARN;
548 1 : if (pAMgr->get_forbidwarn()) return 0;
549 1 : return HUNSPELL_OK_WARN;
550 : }
551 555 : return HUNSPELL_OK;
552 : }
553 :
554 : // recursive breaking at break points
555 505 : if (wordbreak) {
556 : char * s;
557 : char r;
558 501 : int nbr = 0;
559 501 : wl = strlen(cw);
560 501 : int numbreak = pAMgr ? pAMgr->get_numbreak() : 0;
561 :
562 : // calculate break points for recursion limit
563 1957 : for (int j = 0; j < numbreak; j++) {
564 1456 : s = cw;
565 1504 : do {
566 1504 : s = (char *) strstr(s, wordbreak[j]);
567 1504 : if (s) {
568 48 : nbr++;
569 48 : s++;
570 : }
571 : } while (s);
572 : }
573 501 : if (nbr >= 10) return 0;
574 :
575 : // check boundary patterns (^begin and end$)
576 1954 : for (int j = 0; j < numbreak; j++) {
577 1455 : int plen = strlen(wordbreak[j]);
578 1455 : if (plen == 1 || plen > wl) continue;
579 947 : if (wordbreak[j][0] == '^' && strncmp(cw, wordbreak[j] + 1, plen - 1) == 0
580 2 : && spell(cw + plen - 1)) return 1;
581 1401 : if (wordbreak[j][plen - 1] == '$' &&
582 456 : strncmp(cw + wl - plen + 1, wordbreak[j], plen - 1) == 0) {
583 5 : r = cw[wl - plen + 1];
584 5 : cw[wl - plen + 1] = '\0';
585 5 : if (spell(cw)) return 1;
586 4 : cw[wl - plen + 1] = r;
587 : }
588 : }
589 :
590 : // other patterns
591 1935 : for (int j = 0; j < numbreak; j++) {
592 1443 : int plen = strlen(wordbreak[j]);
593 1443 : s=(char *) strstr(cw, wordbreak[j]);
594 1443 : if (s && (s > cw) && (s < cw + wl - plen)) {
595 30 : if (!spell(s + plen)) continue;
596 17 : r = *s;
597 17 : *s = '\0';
598 : // examine 2 sides of the break point
599 17 : if (spell(cw)) return 1;
600 10 : *s = r;
601 :
602 : // LANG_hu: spec. dash rule
603 10 : if (langnum == LANG_hu && strcmp(wordbreak[j], "-") == 0) {
604 0 : r = s[1];
605 0 : s[1] = '\0';
606 0 : if (spell(cw)) return 1; // check the first part with dash
607 0 : s[1] = r;
608 : }
609 : // end of LANG speficic region
610 :
611 : }
612 : }
613 : }
614 :
615 496 : return 0;
616 : }
617 :
618 1228 : struct hentry * Hunspell::checkword(const char * w, int * info, char ** root)
619 : {
620 1228 : struct hentry * he = NULL;
621 : int len, i;
622 : char w2[MAXWORDUTF8LEN];
623 : const char * word;
624 :
625 1228 : char * ignoredchars = pAMgr->get_ignore();
626 1228 : if (ignoredchars != NULL) {
627 15 : strcpy(w2, w);
628 15 : if (utf8) {
629 : int ignoredchars_utf16_len;
630 10 : unsigned short * ignoredchars_utf16 = pAMgr->get_ignore_utf16(&ignoredchars_utf16_len);
631 10 : remove_ignored_chars_utf(w2, ignoredchars_utf16, ignoredchars_utf16_len);
632 : } else {
633 5 : remove_ignored_chars(w2,ignoredchars);
634 : }
635 15 : word = w2;
636 1213 : } else word = w;
637 :
638 1228 : len = strlen(word);
639 :
640 1228 : if (!len)
641 0 : return NULL;
642 :
643 : // word reversing wrapper for complex prefixes
644 1228 : if (complexprefixes) {
645 12 : if (word != w2) {
646 12 : strcpy(w2, word);
647 12 : word = w2;
648 : }
649 12 : if (utf8) reverseword_utf(w2); else reverseword(w2);
650 : }
651 :
652 : // look word in hash table
653 2450 : for (i = 0; (i < maxdic) && !he; i ++) {
654 1228 : he = (pHMgr[i])->lookup(word);
655 :
656 : // check forbidden and onlyincompound words
657 1228 : if ((he) && (he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
658 6 : if (info) *info += SPELL_FORBIDDEN;
659 : // LANG_hu section: set dash information for suggestions
660 6 : if (langnum == LANG_hu) {
661 0 : if (pAMgr->get_compoundflag() &&
662 0 : TESTAFF(he->astr, pAMgr->get_compoundflag(), he->alen)) {
663 0 : if (info) *info += SPELL_COMPOUND;
664 : }
665 : }
666 6 : return NULL;
667 : }
668 :
669 : // he = next not needaffix, onlyincompound homonym or onlyupcase word
670 2810 : while (he && (he->astr) &&
671 146 : ((pAMgr->get_needaffix() && TESTAFF(he->astr, pAMgr->get_needaffix(), he->alen)) ||
672 183 : (pAMgr->get_onlyincompound() && TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) ||
673 16 : (info && (*info & SPELL_INITCAP) && TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen))
674 21 : )) he = he->next_homonym;
675 : }
676 :
677 : // check with affixes
678 1222 : if (!he && pAMgr) {
679 : // try stripping off affixes */
680 992 : he = pAMgr->affix_check(word, len, 0);
681 :
682 : // check compound restriction and onlyupcase
683 1142 : if (he && he->astr && (
684 144 : (pAMgr->get_onlyincompound() &&
685 2 : TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) ||
686 : (info && (*info & SPELL_INITCAP) &&
687 4 : TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen)))) {
688 0 : he = NULL;
689 : }
690 :
691 992 : if (he) {
692 144 : if ((he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
693 2 : if (info) *info += SPELL_FORBIDDEN;
694 2 : return NULL;
695 : }
696 142 : if (root) {
697 0 : *root = mystrdup(he->word);
698 0 : if (*root && complexprefixes) {
699 0 : if (utf8) reverseword_utf(*root); else reverseword(*root);
700 : }
701 : }
702 : // try check compound word
703 848 : } else if (pAMgr->get_compound()) {
704 470 : he = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 0, info);
705 : // LANG_hu section: `moving rule' with last dash
706 470 : if ((!he) && (langnum == LANG_hu) && (word[len-1] == '-')) {
707 0 : char * dup = mystrdup(word);
708 0 : if (!dup) return NULL;
709 0 : dup[len-1] = '\0';
710 0 : he = pAMgr->compound_check(dup, len-1, -5, 0, 100, 0, NULL, 1, 0, info);
711 0 : free(dup);
712 : }
713 : // end of LANG speficic region
714 470 : if (he) {
715 190 : if (root) {
716 0 : *root = mystrdup(he->word);
717 0 : if (*root && complexprefixes) {
718 0 : if (utf8) reverseword_utf(*root); else reverseword(*root);
719 : }
720 : }
721 190 : if (info) *info += SPELL_COMPOUND;
722 : }
723 : }
724 :
725 : }
726 :
727 1220 : return he;
728 : }
729 :
730 0 : int Hunspell::suggest(char*** slst, const char * word)
731 : {
732 0 : int onlycmpdsug = 0;
733 : char cw[MAXWORDUTF8LEN];
734 : char wspace[MAXWORDUTF8LEN];
735 0 : if (!pSMgr || maxdic == 0) return 0;
736 : w_char unicw[MAXWORDLEN];
737 0 : *slst = NULL;
738 : // process XML input of the simplified API (see manual)
739 0 : if (strncmp(word, SPELL_XML, sizeof(SPELL_XML) - 3) == 0) {
740 0 : return spellml(slst, word);
741 : }
742 0 : int nc = strlen(word);
743 0 : if (utf8) {
744 0 : if (nc >= MAXWORDUTF8LEN) return 0;
745 : } else {
746 0 : if (nc >= MAXWORDLEN) return 0;
747 : }
748 0 : int captype = 0;
749 0 : int abbv = 0;
750 0 : int wl = 0;
751 :
752 : // input conversion
753 0 : RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
754 0 : if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
755 0 : else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
756 :
757 0 : if (wl == 0) return 0;
758 0 : int ns = 0;
759 0 : int capwords = 0;
760 :
761 : // check capitalized form for FORCEUCASE
762 0 : if (pAMgr && captype == NOCAP && pAMgr->get_forceucase()) {
763 0 : int info = SPELL_ORIGCAP;
764 : char ** wlst;
765 0 : if (checkword(cw, &info, NULL)) {
766 0 : if (*slst) {
767 0 : wlst = *slst;
768 : } else {
769 0 : wlst = (char **) malloc(MAXSUGGESTION * sizeof(char *));
770 0 : if (wlst == NULL) return -1;
771 0 : *slst = wlst;
772 0 : for (int i = 0; i < MAXSUGGESTION; i++) {
773 0 : wlst[i] = NULL;
774 : }
775 : }
776 0 : wlst[0] = mystrdup(cw);
777 0 : mkinitcap(wlst[0]);
778 0 : return 1;
779 : }
780 : }
781 :
782 0 : switch(captype) {
783 : case NOCAP: {
784 0 : ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
785 0 : break;
786 : }
787 :
788 : case INITCAP: {
789 0 : capwords = 1;
790 0 : ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
791 0 : if (ns == -1) break;
792 0 : memcpy(wspace,cw,(wl+1));
793 0 : mkallsmall2(wspace, unicw, nc);
794 0 : ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
795 0 : break;
796 : }
797 : case HUHINITCAP:
798 0 : capwords = 1;
799 : case HUHCAP: {
800 0 : ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
801 0 : if (ns != -1) {
802 : int prevns;
803 : // something.The -> something. The
804 0 : char * dot = strchr(cw, '.');
805 0 : if (dot && (dot > cw)) {
806 : int captype_;
807 0 : if (utf8) {
808 : w_char w_[MAXWORDLEN];
809 0 : int wl_ = u8_u16(w_, MAXWORDLEN, dot + 1);
810 0 : captype_ = get_captype_utf8(w_, wl_, langnum);
811 0 : } else captype_ = get_captype(dot+1, strlen(dot+1), csconv);
812 0 : if (captype_ == INITCAP) {
813 0 : char * st = mystrdup(cw);
814 0 : if (st) st = (char *) realloc(st, wl + 2);
815 0 : if (st) {
816 0 : st[(dot - cw) + 1] = ' ';
817 0 : strcpy(st + (dot - cw) + 2, dot + 1);
818 0 : ns = insert_sug(slst, st, ns);
819 0 : free(st);
820 : }
821 : }
822 : }
823 0 : if (captype == HUHINITCAP) {
824 : // TheOpenOffice.org -> The OpenOffice.org
825 0 : memcpy(wspace,cw,(wl+1));
826 0 : mkinitsmall2(wspace, unicw, nc);
827 0 : ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
828 : }
829 0 : memcpy(wspace,cw,(wl+1));
830 0 : mkallsmall2(wspace, unicw, nc);
831 0 : if (spell(wspace)) ns = insert_sug(slst, wspace, ns);
832 0 : prevns = ns;
833 0 : ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
834 0 : if (captype == HUHINITCAP) {
835 0 : mkinitcap2(wspace, unicw, nc);
836 0 : if (spell(wspace)) ns = insert_sug(slst, wspace, ns);
837 0 : ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
838 : }
839 : // aNew -> "a New" (instead of "a new")
840 0 : for (int j = prevns; j < ns; j++) {
841 0 : char * space = strchr((*slst)[j],' ');
842 0 : if (space) {
843 0 : int slen = strlen(space + 1);
844 : // different case after space (need capitalisation)
845 0 : if ((slen < wl) && strcmp(cw + wl - slen, space + 1)) {
846 : w_char w[MAXWORDLEN];
847 0 : int wc = 0;
848 0 : char * r = (*slst)[j];
849 0 : if (utf8) wc = u8_u16(w, MAXWORDLEN, space + 1);
850 0 : mkinitcap2(space + 1, w, wc);
851 : // set as first suggestion
852 0 : for (int k = j; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
853 0 : (*slst)[0] = r;
854 : }
855 : }
856 : }
857 : }
858 0 : break;
859 : }
860 :
861 : case ALLCAP: {
862 0 : memcpy(wspace, cw, (wl+1));
863 0 : mkallsmall2(wspace, unicw, nc);
864 0 : ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
865 0 : if (ns == -1) break;
866 0 : if (pAMgr && pAMgr->get_keepcase() && spell(wspace))
867 0 : ns = insert_sug(slst, wspace, ns);
868 0 : mkinitcap2(wspace, unicw, nc);
869 0 : ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
870 0 : for (int j=0; j < ns; j++) {
871 0 : mkallcap((*slst)[j]);
872 0 : if (pAMgr && pAMgr->get_checksharps()) {
873 : char * pos;
874 0 : if (utf8) {
875 0 : pos = strstr((*slst)[j], "\xC3\x9F");
876 0 : while (pos) {
877 0 : *pos = 'S';
878 0 : *(pos+1) = 'S';
879 0 : pos = strstr(pos+2, "\xC3\x9F");
880 : }
881 : } else {
882 0 : pos = strchr((*slst)[j], '\xDF');
883 0 : while (pos) {
884 0 : (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 2);
885 0 : mystrrep((*slst)[j], "\xDF", "SS");
886 0 : pos = strchr((*slst)[j], '\xDF');
887 : }
888 : }
889 : }
890 : }
891 0 : break;
892 : }
893 : }
894 :
895 : // LANG_hu section: replace '-' with ' ' in Hungarian
896 0 : if (langnum == LANG_hu) {
897 0 : for (int j=0; j < ns; j++) {
898 0 : char * pos = strchr((*slst)[j],'-');
899 0 : if (pos) {
900 : int info;
901 : char w[MAXWORDUTF8LEN];
902 0 : *pos = '\0';
903 0 : strcpy(w, (*slst)[j]);
904 0 : strcat(w, pos + 1);
905 0 : spell(w, &info, NULL);
906 0 : if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
907 0 : *pos = ' ';
908 0 : } else *pos = '-';
909 : }
910 : }
911 : }
912 : // END OF LANG_hu section
913 :
914 : // try ngram approach since found nothing or only compound words
915 0 : if (pAMgr && (ns == 0 || onlycmpdsug) && (pAMgr->get_maxngramsugs() != 0) && (*slst)) {
916 0 : switch(captype) {
917 : case NOCAP: {
918 0 : ns = pSMgr->ngsuggest(*slst, cw, ns, pHMgr, maxdic);
919 0 : break;
920 : }
921 : case HUHINITCAP:
922 0 : capwords = 1;
923 : case HUHCAP: {
924 0 : memcpy(wspace,cw,(wl+1));
925 0 : mkallsmall2(wspace, unicw, nc);
926 0 : ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
927 0 : break;
928 : }
929 : case INITCAP: {
930 0 : capwords = 1;
931 0 : memcpy(wspace,cw,(wl+1));
932 0 : mkallsmall2(wspace, unicw, nc);
933 0 : ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
934 0 : break;
935 : }
936 : case ALLCAP: {
937 0 : memcpy(wspace,cw,(wl+1));
938 0 : mkallsmall2(wspace, unicw, nc);
939 0 : int oldns = ns;
940 0 : ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
941 0 : for (int j = oldns; j < ns; j++)
942 0 : mkallcap((*slst)[j]);
943 0 : break;
944 : }
945 : }
946 : }
947 :
948 : // try dash suggestion (Afo-American -> Afro-American)
949 0 : if (char * pos = strchr(cw, '-')) {
950 0 : char * ppos = cw;
951 0 : int nodashsug = 1;
952 0 : char ** nlst = NULL;
953 0 : int nn = 0;
954 0 : int last = 0;
955 0 : if (*slst) {
956 0 : for (int j = 0; j < ns && nodashsug == 1; j++) {
957 0 : if (strchr((*slst)[j], '-')) nodashsug = 0;
958 : }
959 : }
960 0 : while (nodashsug && !last) {
961 0 : if (*pos == '\0') last = 1; else *pos = '\0';
962 0 : if (!spell(ppos)) {
963 0 : nn = suggest(&nlst, ppos);
964 0 : for (int j = nn - 1; j >= 0; j--) {
965 0 : strncpy(wspace, cw, ppos - cw);
966 0 : strcpy(wspace + (ppos - cw), nlst[j]);
967 0 : if (!last) {
968 0 : strcat(wspace, "-");
969 0 : strcat(wspace, pos + 1);
970 : }
971 0 : ns = insert_sug(slst, wspace, ns);
972 0 : free(nlst[j]);
973 : }
974 0 : if (nlst != NULL) free(nlst);
975 0 : nodashsug = 0;
976 : }
977 0 : if (!last) {
978 0 : *pos = '-';
979 0 : ppos = pos + 1;
980 0 : pos = strchr(ppos, '-');
981 : }
982 0 : if (!pos) pos = cw + strlen(cw);
983 : }
984 : }
985 :
986 : // word reversing wrapper for complex prefixes
987 0 : if (complexprefixes) {
988 0 : for (int j = 0; j < ns; j++) {
989 0 : if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
990 : }
991 : }
992 :
993 : // capitalize
994 0 : if (capwords) for (int j=0; j < ns; j++) {
995 0 : mkinitcap((*slst)[j]);
996 : }
997 :
998 : // expand suggestions with dot(s)
999 0 : if (abbv && pAMgr && pAMgr->get_sugswithdots()) {
1000 0 : for (int j = 0; j < ns; j++) {
1001 0 : (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);
1002 0 : strcat((*slst)[j], word + strlen(word) - abbv);
1003 : }
1004 : }
1005 :
1006 : // remove bad capitalized and forbidden forms
1007 0 : if (pAMgr && (pAMgr->get_keepcase() || pAMgr->get_forbiddenword())) {
1008 0 : switch (captype) {
1009 : case INITCAP:
1010 : case ALLCAP: {
1011 0 : int l = 0;
1012 0 : for (int j=0; j < ns; j++) {
1013 0 : if (!strchr((*slst)[j],' ') && !spell((*slst)[j])) {
1014 : char s[MAXSWUTF8L];
1015 : w_char w[MAXSWL];
1016 : int len;
1017 0 : if (utf8) {
1018 0 : len = u8_u16(w, MAXSWL, (*slst)[j]);
1019 : } else {
1020 0 : strcpy(s, (*slst)[j]);
1021 0 : len = strlen(s);
1022 : }
1023 0 : mkallsmall2(s, w, len);
1024 0 : free((*slst)[j]);
1025 0 : if (spell(s)) {
1026 0 : (*slst)[l] = mystrdup(s);
1027 0 : if ((*slst)[l]) l++;
1028 : } else {
1029 0 : mkinitcap2(s, w, len);
1030 0 : if (spell(s)) {
1031 0 : (*slst)[l] = mystrdup(s);
1032 0 : if ((*slst)[l]) l++;
1033 : }
1034 : }
1035 : } else {
1036 0 : (*slst)[l] = (*slst)[j];
1037 0 : l++;
1038 : }
1039 : }
1040 0 : ns = l;
1041 : }
1042 : }
1043 : }
1044 :
1045 : // remove duplications
1046 0 : int l = 0;
1047 0 : for (int j = 0; j < ns; j++) {
1048 0 : (*slst)[l] = (*slst)[j];
1049 0 : for (int k = 0; k < l; k++) {
1050 0 : if (strcmp((*slst)[k], (*slst)[j]) == 0) {
1051 0 : free((*slst)[j]);
1052 0 : l--;
1053 0 : break;
1054 : }
1055 : }
1056 0 : l++;
1057 : }
1058 0 : ns = l;
1059 :
1060 : // output conversion
1061 0 : rl = (pAMgr) ? pAMgr->get_oconvtable() : NULL;
1062 0 : for (int j = 0; rl && j < ns; j++) {
1063 0 : if (rl->conv((*slst)[j], wspace)) {
1064 0 : free((*slst)[j]);
1065 0 : (*slst)[j] = mystrdup(wspace);
1066 : }
1067 : }
1068 :
1069 : // if suggestions removed by nosuggest, onlyincompound parameters
1070 0 : if (l == 0 && *slst) {
1071 0 : free(*slst);
1072 0 : *slst = NULL;
1073 : }
1074 0 : return l;
1075 : }
1076 :
1077 0 : void Hunspell::free_list(char *** slst, int n) {
1078 0 : freelist(slst, n);
1079 0 : }
1080 :
1081 220 : char * Hunspell::get_dic_encoding()
1082 : {
1083 220 : return encoding;
1084 : }
1085 :
1086 : #ifdef HUNSPELL_EXPERIMENTAL
1087 : // XXX need UTF-8 support
1088 : int Hunspell::suggest_auto(char*** slst, const char * word)
1089 : {
1090 : char cw[MAXWORDUTF8LEN];
1091 : char wspace[MAXWORDUTF8LEN];
1092 : if (!pSMgr || maxdic == 0) return 0;
1093 : int wl = strlen(word);
1094 : if (utf8) {
1095 : if (wl >= MAXWORDUTF8LEN) return 0;
1096 : } else {
1097 : if (wl >= MAXWORDLEN) return 0;
1098 : }
1099 : int captype = 0;
1100 : int abbv = 0;
1101 : wl = cleanword(cw, word, &captype, &abbv);
1102 : if (wl == 0) return 0;
1103 : int ns = 0;
1104 : *slst = NULL; // HU, nsug in pSMgr->suggest
1105 :
1106 : switch(captype) {
1107 : case NOCAP: {
1108 : ns = pSMgr->suggest_auto(slst, cw, ns);
1109 : if (ns>0) break;
1110 : break;
1111 : }
1112 :
1113 : case INITCAP: {
1114 : memcpy(wspace,cw,(wl+1));
1115 : mkallsmall(wspace);
1116 : ns = pSMgr->suggest_auto(slst, wspace, ns);
1117 : for (int j=0; j < ns; j++)
1118 : mkinitcap((*slst)[j]);
1119 : ns = pSMgr->suggest_auto(slst, cw, ns);
1120 : break;
1121 :
1122 : }
1123 :
1124 : case HUHINITCAP:
1125 : case HUHCAP: {
1126 : ns = pSMgr->suggest_auto(slst, cw, ns);
1127 : if (ns == 0) {
1128 : memcpy(wspace,cw,(wl+1));
1129 : mkallsmall(wspace);
1130 : ns = pSMgr->suggest_auto(slst, wspace, ns);
1131 : }
1132 : break;
1133 : }
1134 :
1135 : case ALLCAP: {
1136 : memcpy(wspace,cw,(wl+1));
1137 : mkallsmall(wspace);
1138 : ns = pSMgr->suggest_auto(slst, wspace, ns);
1139 :
1140 : mkinitcap(wspace);
1141 : ns = pSMgr->suggest_auto(slst, wspace, ns);
1142 :
1143 : for (int j=0; j < ns; j++)
1144 : mkallcap((*slst)[j]);
1145 : break;
1146 : }
1147 : }
1148 :
1149 : // word reversing wrapper for complex prefixes
1150 : if (complexprefixes) {
1151 : for (int j = 0; j < ns; j++) {
1152 : if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
1153 : }
1154 : }
1155 :
1156 : // expand suggestions with dot(s)
1157 : if (abbv && pAMgr && pAMgr->get_sugswithdots()) {
1158 : for (int j = 0; j < ns; j++) {
1159 : (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);
1160 : strcat((*slst)[j], word + strlen(word) - abbv);
1161 : }
1162 : }
1163 :
1164 : // LANG_hu section: replace '-' with ' ' in Hungarian
1165 : if (langnum == LANG_hu) {
1166 : for (int j=0; j < ns; j++) {
1167 : char * pos = strchr((*slst)[j],'-');
1168 : if (pos) {
1169 : int info;
1170 : char w[MAXWORDUTF8LEN];
1171 : *pos = '\0';
1172 : strcpy(w, (*slst)[j]);
1173 : strcat(w, pos + 1);
1174 : spell(w, &info, NULL);
1175 : if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
1176 : *pos = ' ';
1177 : } else *pos = '-';
1178 : }
1179 : }
1180 : }
1181 : // END OF LANG_hu section
1182 : return ns;
1183 : }
1184 : #endif
1185 :
1186 0 : int Hunspell::stem(char*** slst, char ** desc, int n)
1187 : {
1188 : char result[MAXLNLEN];
1189 : char result2[MAXLNLEN];
1190 0 : *slst = NULL;
1191 0 : if (n == 0) return 0;
1192 0 : *result2 = '\0';
1193 0 : for (int i = 0; i < n; i++) {
1194 0 : *result = '\0';
1195 : // add compound word parts (except the last one)
1196 0 : char * s = (char *) desc[i];
1197 0 : char * part = strstr(s, MORPH_PART);
1198 0 : if (part) {
1199 0 : char * nextpart = strstr(part + 1, MORPH_PART);
1200 0 : while (nextpart) {
1201 0 : copy_field(result + strlen(result), part, MORPH_PART);
1202 0 : part = nextpart;
1203 0 : nextpart = strstr(part + 1, MORPH_PART);
1204 : }
1205 0 : s = part;
1206 : }
1207 :
1208 : char **pl;
1209 : char tok[MAXLNLEN];
1210 0 : strcpy(tok, s);
1211 0 : char * alt = strstr(tok, " | ");
1212 0 : while (alt) {
1213 0 : alt[1] = MSEP_ALT;
1214 0 : alt = strstr(alt, " | ");
1215 : }
1216 0 : int pln = line_tok(tok, &pl, MSEP_ALT);
1217 0 : for (int k = 0; k < pln; k++) {
1218 : // add derivational suffixes
1219 0 : if (strstr(pl[k], MORPH_DERI_SFX)) {
1220 : // remove inflectional suffixes
1221 0 : char * is = strstr(pl[k], MORPH_INFL_SFX);
1222 0 : if (is) *is = '\0';
1223 0 : char * sg = pSMgr->suggest_gen(&(pl[k]), 1, pl[k]);
1224 0 : if (sg) {
1225 : char ** gen;
1226 0 : int genl = line_tok(sg, &gen, MSEP_REC);
1227 0 : free(sg);
1228 0 : for (int j = 0; j < genl; j++) {
1229 0 : sprintf(result2 + strlen(result2), "%c%s%s",
1230 0 : MSEP_REC, result, gen[j]);
1231 : }
1232 0 : freelist(&gen, genl);
1233 : }
1234 : } else {
1235 0 : sprintf(result2 + strlen(result2), "%c%s", MSEP_REC, result);
1236 0 : if (strstr(pl[k], MORPH_SURF_PFX)) {
1237 0 : copy_field(result2 + strlen(result2), pl[k], MORPH_SURF_PFX);
1238 : }
1239 0 : copy_field(result2 + strlen(result2), pl[k], MORPH_STEM);
1240 : }
1241 : }
1242 0 : freelist(&pl, pln);
1243 : }
1244 0 : int sln = line_tok(result2, slst, MSEP_REC);
1245 0 : return uniqlist(*slst, sln);
1246 :
1247 : }
1248 :
1249 0 : int Hunspell::stem(char*** slst, const char * word)
1250 : {
1251 : char ** pl;
1252 0 : int pln = analyze(&pl, word);
1253 0 : int pln2 = stem(slst, pl, pln);
1254 0 : freelist(&pl, pln);
1255 0 : return pln2;
1256 : }
1257 :
1258 : #ifdef HUNSPELL_EXPERIMENTAL
1259 : int Hunspell::suggest_pos_stems(char*** slst, const char * word)
1260 : {
1261 : char cw[MAXWORDUTF8LEN];
1262 : char wspace[MAXWORDUTF8LEN];
1263 : if (! pSMgr || maxdic == 0) return 0;
1264 : int wl = strlen(word);
1265 : if (utf8) {
1266 : if (wl >= MAXWORDUTF8LEN) return 0;
1267 : } else {
1268 : if (wl >= MAXWORDLEN) return 0;
1269 : }
1270 : int captype = 0;
1271 : int abbv = 0;
1272 : wl = cleanword(cw, word, &captype, &abbv);
1273 : if (wl == 0) return 0;
1274 :
1275 : int ns = 0; // ns=0 = normalized input
1276 :
1277 : *slst = NULL; // HU, nsug in pSMgr->suggest
1278 :
1279 : switch(captype) {
1280 : case HUHCAP:
1281 : case NOCAP: {
1282 : ns = pSMgr->suggest_pos_stems(slst, cw, ns);
1283 :
1284 : if ((abbv) && (ns == 0)) {
1285 : memcpy(wspace,cw,wl);
1286 : *(wspace+wl) = '.';
1287 : *(wspace+wl+1) = '\0';
1288 : ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
1289 : }
1290 :
1291 : break;
1292 : }
1293 :
1294 : case INITCAP: {
1295 :
1296 : ns = pSMgr->suggest_pos_stems(slst, cw, ns);
1297 :
1298 : if (ns == 0 || ((*slst)[0][0] == '#')) {
1299 : memcpy(wspace,cw,(wl+1));
1300 : mkallsmall(wspace);
1301 : ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
1302 : }
1303 :
1304 : break;
1305 :
1306 : }
1307 :
1308 : case ALLCAP: {
1309 : ns = pSMgr->suggest_pos_stems(slst, cw, ns);
1310 : if (ns != 0) break;
1311 :
1312 : memcpy(wspace,cw,(wl+1));
1313 : mkallsmall(wspace);
1314 : ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
1315 :
1316 : if (ns == 0) {
1317 : mkinitcap(wspace);
1318 : ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
1319 : }
1320 : break;
1321 : }
1322 : }
1323 :
1324 : return ns;
1325 : }
1326 : #endif // END OF HUNSPELL_EXPERIMENTAL CODE
1327 :
1328 0 : const char * Hunspell::get_wordchars()
1329 : {
1330 0 : return pAMgr->get_wordchars();
1331 : }
1332 :
1333 0 : unsigned short * Hunspell::get_wordchars_utf16(int * len)
1334 : {
1335 0 : return pAMgr->get_wordchars_utf16(len);
1336 : }
1337 :
1338 0 : void Hunspell::mkinitcap(char * p)
1339 : {
1340 0 : if (!utf8) {
1341 0 : if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
1342 : } else {
1343 : int len;
1344 : w_char u[MAXWORDLEN];
1345 0 : len = u8_u16(u, MAXWORDLEN, p);
1346 0 : unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
1347 0 : u[0].h = (unsigned char) (i >> 8);
1348 0 : u[0].l = (unsigned char) (i & 0x00FF);
1349 0 : u16_u8(p, MAXWORDUTF8LEN, u, len);
1350 : }
1351 0 : }
1352 :
1353 145 : int Hunspell::mkinitcap2(char * p, w_char * u, int nc)
1354 : {
1355 145 : if (!utf8) {
1356 114 : if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
1357 31 : } else if (nc > 0) {
1358 31 : unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
1359 31 : u[0].h = (unsigned char) (i >> 8);
1360 31 : u[0].l = (unsigned char) (i & 0x00FF);
1361 31 : u16_u8(p, MAXWORDUTF8LEN, u, nc);
1362 31 : return strlen(p);
1363 : }
1364 114 : return nc;
1365 : }
1366 :
1367 0 : int Hunspell::mkinitsmall2(char * p, w_char * u, int nc)
1368 : {
1369 0 : if (!utf8) {
1370 0 : if (*p != '\0') *p = csconv[((unsigned char)*p)].clower;
1371 0 : } else if (nc > 0) {
1372 0 : unsigned short i = unicodetolower((u[0].h << 8) + u[0].l, langnum);
1373 0 : u[0].h = (unsigned char) (i >> 8);
1374 0 : u[0].l = (unsigned char) (i & 0x00FF);
1375 0 : u16_u8(p, MAXWORDUTF8LEN, u, nc);
1376 0 : return strlen(p);
1377 : }
1378 0 : return nc;
1379 : }
1380 :
1381 0 : int Hunspell::add(const char * word)
1382 : {
1383 0 : if (pHMgr[0]) return (pHMgr[0])->add(word);
1384 0 : return 0;
1385 : }
1386 :
1387 0 : int Hunspell::add_with_affix(const char * word, const char * example)
1388 : {
1389 0 : if (pHMgr[0]) return (pHMgr[0])->add_with_affix(word, example);
1390 0 : return 0;
1391 : }
1392 :
1393 0 : int Hunspell::remove(const char * word)
1394 : {
1395 0 : if (pHMgr[0]) return (pHMgr[0])->remove(word);
1396 0 : return 0;
1397 : }
1398 :
1399 0 : const char * Hunspell::get_version()
1400 : {
1401 0 : return pAMgr->get_version();
1402 : }
1403 :
1404 0 : struct cs_info * Hunspell::get_csconv()
1405 : {
1406 0 : return csconv;
1407 : }
1408 :
1409 0 : void Hunspell::cat_result(char * result, char * st)
1410 : {
1411 0 : if (st) {
1412 0 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1413 0 : mystrcat(result, st, MAXLNLEN);
1414 0 : free(st);
1415 : }
1416 0 : }
1417 :
1418 0 : int Hunspell::analyze(char*** slst, const char * word)
1419 : {
1420 : char cw[MAXWORDUTF8LEN];
1421 : char wspace[MAXWORDUTF8LEN];
1422 : w_char unicw[MAXWORDLEN];
1423 0 : int wl2 = 0;
1424 0 : *slst = NULL;
1425 0 : if (! pSMgr || maxdic == 0) return 0;
1426 0 : int nc = strlen(word);
1427 0 : if (utf8) {
1428 0 : if (nc >= MAXWORDUTF8LEN) return 0;
1429 : } else {
1430 0 : if (nc >= MAXWORDLEN) return 0;
1431 : }
1432 0 : int captype = 0;
1433 0 : int abbv = 0;
1434 0 : int wl = 0;
1435 :
1436 : // input conversion
1437 0 : RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
1438 0 : if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
1439 0 : else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
1440 :
1441 0 : if (wl == 0) {
1442 0 : if (abbv) {
1443 0 : for (wl = 0; wl < abbv; wl++) cw[wl] = '.';
1444 0 : cw[wl] = '\0';
1445 0 : abbv = 0;
1446 0 : } else return 0;
1447 : }
1448 :
1449 : char result[MAXLNLEN];
1450 0 : char * st = NULL;
1451 :
1452 0 : *result = '\0';
1453 :
1454 0 : int n = 0;
1455 0 : int n2 = 0;
1456 0 : int n3 = 0;
1457 :
1458 : // test numbers
1459 : // LANG_hu section: set dash information for suggestions
1460 0 : if (langnum == LANG_hu) {
1461 0 : while ((n < wl) &&
1462 0 : (((cw[n] <= '9') && (cw[n] >= '0')) || (((cw[n] == '.') || (cw[n] == ',')) && (n > 0)))) {
1463 0 : n++;
1464 0 : if ((cw[n] == '.') || (cw[n] == ',')) {
1465 0 : if (((n2 == 0) && (n > 3)) ||
1466 0 : ((n2 > 0) && ((cw[n-1] == '.') || (cw[n-1] == ',')))) break;
1467 0 : n2++;
1468 0 : n3 = n;
1469 : }
1470 : }
1471 :
1472 0 : if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return 0;
1473 0 : if ((n == wl) || ((n>0) && ((cw[n]=='%') || (cw[n]=='\xB0')) && checkword(cw+n, NULL, NULL))) {
1474 0 : mystrcat(result, cw, MAXLNLEN);
1475 0 : result[n - 1] = '\0';
1476 0 : if (n == wl) cat_result(result, pSMgr->suggest_morph(cw + n - 1));
1477 : else {
1478 0 : char sign = cw[n];
1479 0 : cw[n] = '\0';
1480 0 : cat_result(result, pSMgr->suggest_morph(cw + n - 1));
1481 0 : mystrcat(result, "+", MAXLNLEN); // XXX SPEC. MORPHCODE
1482 0 : cw[n] = sign;
1483 0 : cat_result(result, pSMgr->suggest_morph(cw + n));
1484 : }
1485 0 : return line_tok(result, slst, MSEP_REC);
1486 : }
1487 : }
1488 : // END OF LANG_hu section
1489 :
1490 0 : switch(captype) {
1491 : case HUHCAP:
1492 : case HUHINITCAP:
1493 : case NOCAP: {
1494 0 : cat_result(result, pSMgr->suggest_morph(cw));
1495 0 : if (abbv) {
1496 0 : memcpy(wspace,cw,wl);
1497 0 : *(wspace+wl) = '.';
1498 0 : *(wspace+wl+1) = '\0';
1499 0 : cat_result(result, pSMgr->suggest_morph(wspace));
1500 : }
1501 0 : break;
1502 : }
1503 : case INITCAP: {
1504 0 : wl = mkallsmall2(cw, unicw, nc);
1505 0 : memcpy(wspace,cw,(wl+1));
1506 0 : wl2 = mkinitcap2(cw, unicw, nc);
1507 0 : cat_result(result, pSMgr->suggest_morph(wspace));
1508 0 : cat_result(result, pSMgr->suggest_morph(cw));
1509 0 : if (abbv) {
1510 0 : *(wspace+wl) = '.';
1511 0 : *(wspace+wl+1) = '\0';
1512 0 : cat_result(result, pSMgr->suggest_morph(wspace));
1513 :
1514 0 : memcpy(wspace, cw, wl2);
1515 0 : *(wspace+wl2) = '.';
1516 0 : *(wspace+wl2+1) = '\0';
1517 :
1518 0 : cat_result(result, pSMgr->suggest_morph(wspace));
1519 : }
1520 0 : break;
1521 : }
1522 : case ALLCAP: {
1523 0 : cat_result(result, pSMgr->suggest_morph(cw));
1524 0 : if (abbv) {
1525 0 : memcpy(wspace,cw,wl);
1526 0 : *(wspace+wl) = '.';
1527 0 : *(wspace+wl+1) = '\0';
1528 0 : cat_result(result, pSMgr->suggest_morph(cw));
1529 : }
1530 0 : wl = mkallsmall2(cw, unicw, nc);
1531 0 : memcpy(wspace,cw,(wl+1));
1532 0 : wl2 = mkinitcap2(cw, unicw, nc);
1533 :
1534 0 : cat_result(result, pSMgr->suggest_morph(wspace));
1535 0 : cat_result(result, pSMgr->suggest_morph(cw));
1536 0 : if (abbv) {
1537 0 : *(wspace+wl) = '.';
1538 0 : *(wspace+wl+1) = '\0';
1539 0 : cat_result(result, pSMgr->suggest_morph(wspace));
1540 :
1541 0 : memcpy(wspace, cw, wl2);
1542 0 : *(wspace+wl2) = '.';
1543 0 : *(wspace+wl2+1) = '\0';
1544 :
1545 0 : cat_result(result, pSMgr->suggest_morph(wspace));
1546 : }
1547 0 : break;
1548 : }
1549 : }
1550 :
1551 0 : if (*result) {
1552 : // word reversing wrapper for complex prefixes
1553 0 : if (complexprefixes) {
1554 0 : if (utf8) reverseword_utf(result); else reverseword(result);
1555 : }
1556 0 : return line_tok(result, slst, MSEP_REC);
1557 : }
1558 :
1559 : // compound word with dash (HU) I18n
1560 0 : char * dash = NULL;
1561 0 : int nresult = 0;
1562 : // LANG_hu section: set dash information for suggestions
1563 0 : if (langnum == LANG_hu) dash = (char *) strchr(cw,'-');
1564 0 : if ((langnum == LANG_hu) && dash) {
1565 0 : *dash='\0';
1566 : // examine 2 sides of the dash
1567 0 : if (dash[1] == '\0') { // base word ending with dash
1568 0 : if (spell(cw)) {
1569 0 : char * p = pSMgr->suggest_morph(cw);
1570 0 : if (p) {
1571 0 : int ret = line_tok(p, slst, MSEP_REC);
1572 0 : free(p);
1573 0 : return ret;
1574 : }
1575 :
1576 : }
1577 0 : } else if ((dash[1] == 'e') && (dash[2] == '\0')) { // XXX (HU) -e hat.
1578 0 : if (spell(cw) && (spell("-e"))) {
1579 0 : st = pSMgr->suggest_morph(cw);
1580 0 : if (st) {
1581 0 : mystrcat(result, st, MAXLNLEN);
1582 0 : free(st);
1583 : }
1584 0 : mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE
1585 0 : st = pSMgr->suggest_morph("-e");
1586 0 : if (st) {
1587 0 : mystrcat(result, st, MAXLNLEN);
1588 0 : free(st);
1589 : }
1590 0 : return line_tok(result, slst, MSEP_REC);
1591 : }
1592 : } else {
1593 : // first word ending with dash: word- XXX ???
1594 0 : char r2 = *(dash + 1);
1595 0 : dash[0]='-';
1596 0 : dash[1]='\0';
1597 0 : nresult = spell(cw);
1598 0 : dash[1] = r2;
1599 0 : dash[0]='\0';
1600 0 : if (nresult && spell(dash+1) && ((strlen(dash+1) > 1) ||
1601 0 : ((dash[1] > '0') && (dash[1] < '9')))) {
1602 0 : st = pSMgr->suggest_morph(cw);
1603 0 : if (st) {
1604 0 : mystrcat(result, st, MAXLNLEN);
1605 0 : free(st);
1606 0 : mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE
1607 : }
1608 0 : st = pSMgr->suggest_morph(dash+1);
1609 0 : if (st) {
1610 0 : mystrcat(result, st, MAXLNLEN);
1611 0 : free(st);
1612 : }
1613 0 : return line_tok(result, slst, MSEP_REC);
1614 : }
1615 : }
1616 : // affixed number in correct word
1617 0 : if (nresult && (dash > cw) && (((*(dash-1)<='9') &&
1618 0 : (*(dash-1)>='0')) || (*(dash-1)=='.'))) {
1619 0 : *dash='-';
1620 0 : n = 1;
1621 0 : if (*(dash - n) == '.') n++;
1622 : // search first not a number character to left from dash
1623 0 : while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {
1624 0 : n++;
1625 : }
1626 0 : if ((dash - n) < cw) n--;
1627 : // numbers: valami1000000-hoz
1628 : // examine 100000-hoz, 10000-hoz 1000-hoz, 10-hoz,
1629 : // 56-hoz, 6-hoz
1630 0 : for(; n >= 1; n--) {
1631 0 : if ((*(dash - n) >= '0') && (*(dash - n) <= '9') && checkword(dash - n, NULL, NULL)) {
1632 0 : mystrcat(result, cw, MAXLNLEN);
1633 0 : result[dash - cw - n] = '\0';
1634 0 : st = pSMgr->suggest_morph(dash - n);
1635 0 : if (st) {
1636 0 : mystrcat(result, st, MAXLNLEN);
1637 0 : free(st);
1638 : }
1639 0 : return line_tok(result, slst, MSEP_REC);
1640 : }
1641 : }
1642 : }
1643 : }
1644 0 : return 0;
1645 : }
1646 :
1647 0 : int Hunspell::generate(char*** slst, const char * word, char ** pl, int pln)
1648 : {
1649 0 : *slst = NULL;
1650 0 : if (!pSMgr || !pln) return 0;
1651 : char **pl2;
1652 0 : int pl2n = analyze(&pl2, word);
1653 0 : int captype = 0;
1654 0 : int abbv = 0;
1655 : char cw[MAXWORDUTF8LEN];
1656 0 : cleanword(cw, word, &captype, &abbv);
1657 : char result[MAXLNLEN];
1658 0 : *result = '\0';
1659 :
1660 0 : for (int i = 0; i < pln; i++) {
1661 0 : cat_result(result, pSMgr->suggest_gen(pl2, pl2n, pl[i]));
1662 : }
1663 0 : freelist(&pl2, pl2n);
1664 :
1665 0 : if (*result) {
1666 : // allcap
1667 0 : if (captype == ALLCAP) mkallcap(result);
1668 :
1669 : // line split
1670 0 : int linenum = line_tok(result, slst, MSEP_REC);
1671 :
1672 : // capitalize
1673 0 : if (captype == INITCAP || captype == HUHINITCAP) {
1674 0 : for (int j=0; j < linenum; j++) mkinitcap((*slst)[j]);
1675 : }
1676 :
1677 : // temporary filtering of prefix related errors (eg.
1678 : // generate("undrinkable", "eats") --> "undrinkables" and "*undrinks")
1679 :
1680 0 : int r = 0;
1681 0 : for (int j=0; j < linenum; j++) {
1682 0 : if (!spell((*slst)[j])) {
1683 0 : free((*slst)[j]);
1684 0 : (*slst)[j] = NULL;
1685 : } else {
1686 0 : if (r < j) (*slst)[r] = (*slst)[j];
1687 0 : r++;
1688 : }
1689 : }
1690 0 : if (r > 0) return r;
1691 0 : free(*slst);
1692 0 : *slst = NULL;
1693 : }
1694 0 : return 0;
1695 : }
1696 :
1697 0 : int Hunspell::generate(char*** slst, const char * word, const char * pattern)
1698 : {
1699 : char **pl;
1700 0 : int pln = analyze(&pl, pattern);
1701 0 : int n = generate(slst, word, pl, pln);
1702 0 : freelist(&pl, pln);
1703 0 : return uniqlist(*slst, n);
1704 : }
1705 :
1706 : // minimal XML parser functions
1707 0 : int Hunspell::get_xml_par(char * dest, const char * par, int max)
1708 : {
1709 0 : char * d = dest;
1710 0 : if (!par) return 0;
1711 0 : char end = *par;
1712 0 : char * dmax = dest + max;
1713 0 : if (end == '>') end = '<';
1714 0 : else if (end != '\'' && end != '"') return 0; // bad XML
1715 0 : for (par++; d < dmax && *par != '\0' && *par != end; par++, d++) *d = *par;
1716 0 : *d = '\0';
1717 0 : mystrrep(dest, "<", "<");
1718 0 : mystrrep(dest, "&", "&");
1719 0 : return (int)(d - dest);
1720 : }
1721 :
1722 0 : int Hunspell::get_langnum() const
1723 : {
1724 0 : return langnum;
1725 : }
1726 :
1727 : // return the beginning of the element (attr == NULL) or the attribute
1728 0 : const char * Hunspell::get_xml_pos(const char * s, const char * attr)
1729 : {
1730 0 : const char * end = strchr(s, '>');
1731 0 : const char * p = s;
1732 0 : if (attr == NULL) return end;
1733 0 : do {
1734 0 : p = strstr(p, attr);
1735 0 : if (!p || p >= end) return 0;
1736 0 : } while (*(p-1) != ' ' && *(p-1) != '\n');
1737 0 : return p + strlen(attr);
1738 : }
1739 :
1740 0 : int Hunspell::check_xml_par(const char * q, const char * attr, const char * value) {
1741 : char cw[MAXWORDUTF8LEN];
1742 0 : if (get_xml_par(cw, get_xml_pos(q, attr), MAXWORDUTF8LEN - 1) &&
1743 0 : strcmp(cw, value) == 0) return 1;
1744 0 : return 0;
1745 : }
1746 :
1747 0 : int Hunspell::get_xml_list(char ***slst, char * list, const char * tag) {
1748 0 : int n = 0;
1749 : char * p;
1750 0 : if (!list) return 0;
1751 0 : for (p = list; (p = strstr(p, tag)); p++) n++;
1752 0 : if (n == 0) return 0;
1753 0 : *slst = (char **) malloc(sizeof(char *) * n);
1754 0 : if (!*slst) return 0;
1755 0 : for (p = list, n = 0; (p = strstr(p, tag)); p++, n++) {
1756 0 : int l = strlen(p);
1757 0 : (*slst)[n] = (char *) malloc(l + 1);
1758 0 : if (!(*slst)[n]) return n;
1759 0 : if (!get_xml_par((*slst)[n], p + strlen(tag) - 1, l)) {
1760 0 : free((*slst)[n]);
1761 0 : break;
1762 : }
1763 : }
1764 0 : return n;
1765 : }
1766 :
1767 0 : int Hunspell::spellml(char*** slst, const char * word)
1768 : {
1769 : char *q, *q2;
1770 : char cw[MAXWORDUTF8LEN], cw2[MAXWORDUTF8LEN];
1771 0 : q = (char *) strstr(word, "<query");
1772 0 : if (!q) return 0; // bad XML input
1773 0 : q2 = strchr(q, '>');
1774 0 : if (!q2) return 0; // bad XML input
1775 0 : q2 = strstr(q2, "<word");
1776 0 : if (!q2) return 0; // bad XML input
1777 0 : if (check_xml_par(q, "type=", "analyze")) {
1778 0 : int n = 0, s = 0;
1779 0 : if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 10)) n = analyze(slst, cw);
1780 0 : if (n == 0) return 0;
1781 : // convert the result to <code><a>ana1</a><a>ana2</a></code> format
1782 0 : for (int i = 0; i < n; i++) s+= strlen((*slst)[i]);
1783 0 : char * r = (char *) malloc(6 + 5 * s + 7 * n + 7 + 1); // XXX 5*s->&->&
1784 0 : if (!r) return 0;
1785 0 : strcpy(r, "<code>");
1786 0 : for (int i = 0; i < n; i++) {
1787 0 : int l = strlen(r);
1788 0 : strcpy(r + l, "<a>");
1789 0 : strcpy(r + l + 3, (*slst)[i]);
1790 0 : mystrrep(r + l + 3, "\t", " ");
1791 0 : mystrrep(r + l + 3, "<", "<");
1792 0 : mystrrep(r + l + 3, "&", "&");
1793 0 : strcat(r, "</a>");
1794 0 : free((*slst)[i]);
1795 : }
1796 0 : strcat(r, "</code>");
1797 0 : (*slst)[0] = r;
1798 0 : return 1;
1799 0 : } else if (check_xml_par(q, "type=", "stem")) {
1800 0 : if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1)) return stem(slst, cw);
1801 0 : } else if (check_xml_par(q, "type=", "generate")) {
1802 0 : int n = get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1);
1803 0 : if (n == 0) return 0;
1804 0 : char * q3 = strstr(q2 + 1, "<word");
1805 0 : if (q3) {
1806 0 : if (get_xml_par(cw2, strchr(q3, '>'), MAXWORDUTF8LEN - 1)) {
1807 0 : return generate(slst, cw, cw2);
1808 : }
1809 : } else {
1810 0 : if ((q2 = strstr(q2 + 1, "<code"))) {
1811 : char ** slst2;
1812 0 : if ((n = get_xml_list(&slst2, strchr(q2, '>'), "<a>"))) {
1813 0 : int n2 = generate(slst, cw, slst2, n);
1814 0 : freelist(&slst2, n);
1815 0 : return uniqlist(*slst, n2);
1816 : }
1817 0 : freelist(&slst2, n);
1818 : }
1819 : }
1820 : }
1821 0 : return 0;
1822 : }
1823 :
1824 :
1825 : #ifdef HUNSPELL_EXPERIMENTAL
1826 : // XXX need UTF-8 support
1827 : char * Hunspell::morph_with_correction(const char * word)
1828 : {
1829 : char cw[MAXWORDUTF8LEN];
1830 : char wspace[MAXWORDUTF8LEN];
1831 : if (! pSMgr || maxdic == 0) return NULL;
1832 : int wl = strlen(word);
1833 : if (utf8) {
1834 : if (wl >= MAXWORDUTF8LEN) return NULL;
1835 : } else {
1836 : if (wl >= MAXWORDLEN) return NULL;
1837 : }
1838 : int captype = 0;
1839 : int abbv = 0;
1840 : wl = cleanword(cw, word, &captype, &abbv);
1841 : if (wl == 0) return NULL;
1842 :
1843 : char result[MAXLNLEN];
1844 : char * st = NULL;
1845 :
1846 : *result = '\0';
1847 :
1848 :
1849 : switch(captype) {
1850 : case NOCAP: {
1851 : st = pSMgr->suggest_morph_for_spelling_error(cw);
1852 : if (st) {
1853 : mystrcat(result, st, MAXLNLEN);
1854 : free(st);
1855 : }
1856 : if (abbv) {
1857 : memcpy(wspace,cw,wl);
1858 : *(wspace+wl) = '.';
1859 : *(wspace+wl+1) = '\0';
1860 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1861 : if (st) {
1862 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1863 : mystrcat(result, st, MAXLNLEN);
1864 : free(st);
1865 : }
1866 : }
1867 : break;
1868 : }
1869 : case INITCAP: {
1870 : memcpy(wspace,cw,(wl+1));
1871 : mkallsmall(wspace);
1872 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1873 : if (st) {
1874 : mystrcat(result, st, MAXLNLEN);
1875 : free(st);
1876 : }
1877 : st = pSMgr->suggest_morph_for_spelling_error(cw);
1878 : if (st) {
1879 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1880 : mystrcat(result, st, MAXLNLEN);
1881 : free(st);
1882 : }
1883 : if (abbv) {
1884 : memcpy(wspace,cw,wl);
1885 : *(wspace+wl) = '.';
1886 : *(wspace+wl+1) = '\0';
1887 : mkallsmall(wspace);
1888 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1889 : if (st) {
1890 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1891 : mystrcat(result, st, MAXLNLEN);
1892 : free(st);
1893 : }
1894 : mkinitcap(wspace);
1895 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1896 : if (st) {
1897 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1898 : mystrcat(result, st, MAXLNLEN);
1899 : free(st);
1900 : }
1901 : }
1902 : break;
1903 : }
1904 : case HUHCAP: {
1905 : st = pSMgr->suggest_morph_for_spelling_error(cw);
1906 : if (st) {
1907 : mystrcat(result, st, MAXLNLEN);
1908 : free(st);
1909 : }
1910 : memcpy(wspace,cw,(wl+1));
1911 : mkallsmall(wspace);
1912 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1913 : if (st) {
1914 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1915 : mystrcat(result, st, MAXLNLEN);
1916 : free(st);
1917 : }
1918 : break;
1919 : }
1920 : case ALLCAP: {
1921 : memcpy(wspace,cw,(wl+1));
1922 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1923 : if (st) {
1924 : mystrcat(result, st, MAXLNLEN);
1925 : free(st);
1926 : }
1927 : mkallsmall(wspace);
1928 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1929 : if (st) {
1930 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1931 : mystrcat(result, st, MAXLNLEN);
1932 : free(st);
1933 : }
1934 : mkinitcap(wspace);
1935 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1936 : if (st) {
1937 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1938 : mystrcat(result, st, MAXLNLEN);
1939 : free(st);
1940 : }
1941 : if (abbv) {
1942 : memcpy(wspace,cw,(wl+1));
1943 : *(wspace+wl) = '.';
1944 : *(wspace+wl+1) = '\0';
1945 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1946 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1947 : if (st) {
1948 : mystrcat(result, st, MAXLNLEN);
1949 : free(st);
1950 : }
1951 : mkallsmall(wspace);
1952 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1953 : if (st) {
1954 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1955 : mystrcat(result, st, MAXLNLEN);
1956 : free(st);
1957 : }
1958 : mkinitcap(wspace);
1959 : st = pSMgr->suggest_morph_for_spelling_error(wspace);
1960 : if (st) {
1961 : if (*result) mystrcat(result, "\n", MAXLNLEN);
1962 : mystrcat(result, st, MAXLNLEN);
1963 : free(st);
1964 : }
1965 : }
1966 : break;
1967 : }
1968 : }
1969 :
1970 : if (*result) return mystrdup(result);
1971 : return NULL;
1972 : }
1973 :
1974 : #endif // END OF HUNSPELL_EXPERIMENTAL CODE
1975 :
1976 0 : Hunhandle *Hunspell_create(const char * affpath, const char * dpath)
1977 : {
1978 0 : return (Hunhandle*)(new Hunspell(affpath, dpath));
1979 : }
1980 :
1981 0 : Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath,
1982 : const char * key)
1983 : {
1984 0 : return (Hunhandle*)(new Hunspell(affpath, dpath, key));
1985 : }
1986 :
1987 0 : void Hunspell_destroy(Hunhandle *pHunspell)
1988 : {
1989 0 : delete (Hunspell*)(pHunspell);
1990 0 : }
1991 :
1992 0 : int Hunspell_spell(Hunhandle *pHunspell, const char *word)
1993 : {
1994 0 : return ((Hunspell*)pHunspell)->spell(word);
1995 : }
1996 :
1997 0 : char *Hunspell_get_dic_encoding(Hunhandle *pHunspell)
1998 : {
1999 0 : return ((Hunspell*)pHunspell)->get_dic_encoding();
2000 : }
2001 :
2002 0 : int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word)
2003 : {
2004 0 : return ((Hunspell*)pHunspell)->suggest(slst, word);
2005 : }
2006 :
2007 0 : int Hunspell_analyze(Hunhandle *pHunspell, char*** slst, const char * word)
2008 : {
2009 0 : return ((Hunspell*)pHunspell)->analyze(slst, word);
2010 : }
2011 :
2012 0 : int Hunspell_stem(Hunhandle *pHunspell, char*** slst, const char * word)
2013 : {
2014 0 : return ((Hunspell*)pHunspell)->stem(slst, word);
2015 : }
2016 :
2017 0 : int Hunspell_stem2(Hunhandle *pHunspell, char*** slst, char** desc, int n)
2018 : {
2019 0 : return ((Hunspell*)pHunspell)->stem(slst, desc, n);
2020 : }
2021 :
2022 0 : int Hunspell_generate(Hunhandle *pHunspell, char*** slst, const char * word,
2023 : const char * word2)
2024 : {
2025 0 : return ((Hunspell*)pHunspell)->generate(slst, word, word2);
2026 : }
2027 :
2028 0 : int Hunspell_generate2(Hunhandle *pHunspell, char*** slst, const char * word,
2029 : char** desc, int n)
2030 : {
2031 0 : return ((Hunspell*)pHunspell)->generate(slst, word, desc, n);
2032 : }
2033 :
2034 : /* functions for run-time modification of the dictionary */
2035 :
2036 : /* add word to the run-time dictionary */
2037 :
2038 0 : int Hunspell_add(Hunhandle *pHunspell, const char * word) {
2039 0 : return ((Hunspell*)pHunspell)->add(word);
2040 : }
2041 :
2042 : /* add word to the run-time dictionary with affix flags of
2043 : * the example (a dictionary word): Hunspell will recognize
2044 : * affixed forms of the new word, too.
2045 : */
2046 :
2047 0 : int Hunspell_add_with_affix(Hunhandle *pHunspell, const char * word,
2048 : const char * example) {
2049 0 : return ((Hunspell*)pHunspell)->add_with_affix(word, example);
2050 : }
2051 :
2052 : /* remove word from the run-time dictionary */
2053 :
2054 0 : int Hunspell_remove(Hunhandle *pHunspell, const char * word) {
2055 0 : return ((Hunspell*)pHunspell)->remove(word);
2056 : }
2057 :
2058 0 : void Hunspell_free_list(Hunhandle *, char *** slst, int n) {
2059 0 : freelist(slst, n);
2060 0 : }
|