PageRenderTime 48ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/extensions/spellcheck/hunspell/src/affixmgr.cpp

http://github.com/zpao/v8monkey
C++ | 4575 lines | 3604 code | 489 blank | 482 comment | 1549 complexity | 240bb4bb862d7846dbcab5744698c2eb MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, AGPL-1.0, LGPL-2.1, BSD-3-Clause, GPL-2.0, JSON, Apache-2.0, 0BSD
  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. #include <stdlib.h>
  58. #include <string.h>
  59. #include <stdio.h>
  60. #include <ctype.h>
  61. #include <vector>
  62. #include "affixmgr.hxx"
  63. #include "affentry.hxx"
  64. #include "langnum.hxx"
  65. #include "csutil.hxx"
  66. AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key)
  67. {
  68. // register hash manager and load affix data from aff file
  69. pHMgr = ptr[0];
  70. alldic = ptr;
  71. maxdic = md;
  72. keystring = NULL;
  73. trystring = NULL;
  74. encoding=NULL;
  75. csconv=NULL;
  76. utf8 = 0;
  77. complexprefixes = 0;
  78. maptable = NULL;
  79. nummap = 0;
  80. breaktable = NULL;
  81. numbreak = -1;
  82. reptable = NULL;
  83. numrep = 0;
  84. iconvtable = NULL;
  85. oconvtable = NULL;
  86. checkcpdtable = NULL;
  87. // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN)
  88. simplifiedcpd = 0;
  89. numcheckcpd = 0;
  90. defcpdtable = NULL;
  91. numdefcpd = 0;
  92. phone = NULL;
  93. compoundflag = FLAG_NULL; // permits word in compound forms
  94. compoundbegin = FLAG_NULL; // may be first word in compound forms
  95. compoundmiddle = FLAG_NULL; // may be middle word in compound forms
  96. compoundend = FLAG_NULL; // may be last word in compound forms
  97. compoundroot = FLAG_NULL; // compound word signing flag
  98. compoundpermitflag = FLAG_NULL; // compound permitting flag for suffixed word
  99. compoundforbidflag = FLAG_NULL; // compound fordidden flag for suffixed word
  100. checkcompounddup = 0; // forbid double words in compounds
  101. checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution)
  102. checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds
  103. checkcompoundtriple = 0; // forbid compounds with triple letters
  104. simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt)
  105. forbiddenword = FORBIDDENWORD; // forbidden word signing flag
  106. nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag
  107. nongramsuggest = FLAG_NULL;
  108. lang = NULL; // language
  109. langnum = 0; // language code (see http://l10n.openoffice.org/languages.html)
  110. needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes
  111. cpdwordmax = -1; // default: unlimited wordcount in compound words
  112. cpdmin = -1; // undefined
  113. cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words
  114. cpdvowels=NULL; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX)
  115. cpdvowels_utf16=NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search)
  116. cpdvowels_utf16_len=0; // vowels
  117. pfxappnd=NULL; // previous prefix for counting the syllables of prefix BUG
  118. sfxappnd=NULL; // previous suffix for counting a special syllables BUG
  119. cpdsyllablenum=NULL; // syllable count incrementing flag
  120. checknum=0; // checking numbers, and word with numbers
  121. wordchars=NULL; // letters + spec. word characters
  122. wordchars_utf16=NULL; // letters + spec. word characters
  123. wordchars_utf16_len=0; // letters + spec. word characters
  124. ignorechars=NULL; // letters + spec. word characters
  125. ignorechars_utf16=NULL; // letters + spec. word characters
  126. ignorechars_utf16_len=0; // letters + spec. word characters
  127. version=NULL; // affix and dictionary file version string
  128. havecontclass=0; // flags of possible continuing classes (double affix)
  129. // LEMMA_PRESENT: not put root into the morphological output. Lemma presents
  130. // in morhological description in dictionary file. It's often combined with PSEUDOROOT.
  131. lemma_present = FLAG_NULL;
  132. circumfix = FLAG_NULL;
  133. onlyincompound = FLAG_NULL;
  134. maxngramsugs = -1; // undefined
  135. maxdiff = -1; // undefined
  136. onlymaxdiff = 0;
  137. maxcpdsugs = -1; // undefined
  138. nosplitsugs = 0;
  139. sugswithdots = 0;
  140. keepcase = 0;
  141. forceucase = 0;
  142. warn = 0;
  143. forbidwarn = 0;
  144. checksharps = 0;
  145. substandard = FLAG_NULL;
  146. fullstrip = 0;
  147. sfx = NULL;
  148. pfx = NULL;
  149. for (int i=0; i < SETSIZE; i++) {
  150. pStart[i] = NULL;
  151. sStart[i] = NULL;
  152. pFlag[i] = NULL;
  153. sFlag[i] = NULL;
  154. }
  155. for (int j=0; j < CONTSIZE; j++) {
  156. contclasses[j] = 0;
  157. }
  158. if (parse_file(affpath, key)) {
  159. HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath);
  160. }
  161. if (cpdmin == -1) cpdmin = MINCPDLEN;
  162. }
  163. AffixMgr::~AffixMgr()
  164. {
  165. // pass through linked prefix entries and clean up
  166. for (int i=0; i < SETSIZE ;i++) {
  167. pFlag[i] = NULL;
  168. PfxEntry * ptr = pStart[i];
  169. PfxEntry * nptr = NULL;
  170. while (ptr) {
  171. nptr = ptr->getNext();
  172. delete(ptr);
  173. ptr = nptr;
  174. nptr = NULL;
  175. }
  176. }
  177. // pass through linked suffix entries and clean up
  178. for (int j=0; j < SETSIZE ; j++) {
  179. sFlag[j] = NULL;
  180. SfxEntry * ptr = sStart[j];
  181. SfxEntry * nptr = NULL;
  182. while (ptr) {
  183. nptr = ptr->getNext();
  184. delete(ptr);
  185. ptr = nptr;
  186. nptr = NULL;
  187. }
  188. sStart[j] = NULL;
  189. }
  190. if (keystring) free(keystring);
  191. keystring=NULL;
  192. if (trystring) free(trystring);
  193. trystring=NULL;
  194. if (encoding) free(encoding);
  195. encoding=NULL;
  196. if (maptable) {
  197. for (int j=0; j < nummap; j++) {
  198. for (int k=0; k < maptable[j].len; k++) {
  199. if (maptable[j].set[k]) free(maptable[j].set[k]);
  200. }
  201. free(maptable[j].set);
  202. maptable[j].set = NULL;
  203. maptable[j].len = 0;
  204. }
  205. free(maptable);
  206. maptable = NULL;
  207. }
  208. nummap = 0;
  209. if (breaktable) {
  210. for (int j=0; j < numbreak; j++) {
  211. if (breaktable[j]) free(breaktable[j]);
  212. breaktable[j] = NULL;
  213. }
  214. free(breaktable);
  215. breaktable = NULL;
  216. }
  217. numbreak = 0;
  218. if (reptable) {
  219. for (int j=0; j < numrep; j++) {
  220. free(reptable[j].pattern);
  221. free(reptable[j].pattern2);
  222. }
  223. free(reptable);
  224. reptable = NULL;
  225. }
  226. if (iconvtable) delete iconvtable;
  227. if (oconvtable) delete oconvtable;
  228. if (phone && phone->rules) {
  229. for (int j=0; j < phone->num + 1; j++) {
  230. free(phone->rules[j * 2]);
  231. free(phone->rules[j * 2 + 1]);
  232. }
  233. free(phone->rules);
  234. free(phone);
  235. phone = NULL;
  236. }
  237. if (defcpdtable) {
  238. for (int j=0; j < numdefcpd; j++) {
  239. free(defcpdtable[j].def);
  240. defcpdtable[j].def = NULL;
  241. }
  242. free(defcpdtable);
  243. defcpdtable = NULL;
  244. }
  245. numrep = 0;
  246. if (checkcpdtable) {
  247. for (int j=0; j < numcheckcpd; j++) {
  248. free(checkcpdtable[j].pattern);
  249. free(checkcpdtable[j].pattern2);
  250. free(checkcpdtable[j].pattern3);
  251. checkcpdtable[j].pattern = NULL;
  252. checkcpdtable[j].pattern2 = NULL;
  253. checkcpdtable[j].pattern3 = NULL;
  254. }
  255. free(checkcpdtable);
  256. checkcpdtable = NULL;
  257. }
  258. numcheckcpd = 0;
  259. FREE_FLAG(compoundflag);
  260. FREE_FLAG(compoundbegin);
  261. FREE_FLAG(compoundmiddle);
  262. FREE_FLAG(compoundend);
  263. FREE_FLAG(compoundpermitflag);
  264. FREE_FLAG(compoundforbidflag);
  265. FREE_FLAG(compoundroot);
  266. FREE_FLAG(forbiddenword);
  267. FREE_FLAG(nosuggest);
  268. FREE_FLAG(nongramsuggest);
  269. FREE_FLAG(needaffix);
  270. FREE_FLAG(lemma_present);
  271. FREE_FLAG(circumfix);
  272. FREE_FLAG(onlyincompound);
  273. cpdwordmax = 0;
  274. pHMgr = NULL;
  275. cpdmin = 0;
  276. cpdmaxsyllable = 0;
  277. if (cpdvowels) free(cpdvowels);
  278. if (cpdvowels_utf16) free(cpdvowels_utf16);
  279. if (cpdsyllablenum) free(cpdsyllablenum);
  280. free_utf_tbl();
  281. if (lang) free(lang);
  282. if (wordchars) free(wordchars);
  283. if (wordchars_utf16) free(wordchars_utf16);
  284. if (ignorechars) free(ignorechars);
  285. if (ignorechars_utf16) free(ignorechars_utf16);
  286. if (version) free(version);
  287. checknum=0;
  288. #ifdef MOZILLA_CLIENT
  289. delete [] csconv;
  290. #endif
  291. }
  292. // read in aff file and build up prefix and suffix entry objects
  293. int AffixMgr::parse_file(const char * affpath, const char * key)
  294. {
  295. char * line; // io buffers
  296. char ft; // affix type
  297. // checking flag duplication
  298. char dupflags[CONTSIZE];
  299. char dupflags_ini = 1;
  300. // first line indicator for removing byte order mark
  301. int firstline = 1;
  302. // open the affix file
  303. FileMgr * afflst = new FileMgr(affpath, key);
  304. if (!afflst) {
  305. HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath);
  306. return 1;
  307. }
  308. // step one is to parse the affix file building up the internal
  309. // affix data structures
  310. // read in each line ignoring any that do not
  311. // start with a known line type indicator
  312. while ((line = afflst->getline())) {
  313. mychomp(line);
  314. /* remove byte order mark */
  315. if (firstline) {
  316. firstline = 0;
  317. // Affix file begins with byte order mark: possible incompatibility with old Hunspell versions
  318. if (strncmp(line,"\xEF\xBB\xBF",3) == 0) {
  319. memmove(line, line+3, strlen(line+3)+1);
  320. }
  321. }
  322. /* parse in the keyboard string */
  323. if (strncmp(line,"KEY",3) == 0) {
  324. if (parse_string(line, &keystring, afflst->getlinenum())) {
  325. delete afflst;
  326. return 1;
  327. }
  328. }
  329. /* parse in the try string */
  330. if (strncmp(line,"TRY",3) == 0) {
  331. if (parse_string(line, &trystring, afflst->getlinenum())) {
  332. delete afflst;
  333. return 1;
  334. }
  335. }
  336. /* parse in the name of the character set used by the .dict and .aff */
  337. if (strncmp(line,"SET",3) == 0) {
  338. if (parse_string(line, &encoding, afflst->getlinenum())) {
  339. delete afflst;
  340. return 1;
  341. }
  342. if (strcmp(encoding, "UTF-8") == 0) {
  343. utf8 = 1;
  344. #ifndef OPENOFFICEORG
  345. #ifndef MOZILLA_CLIENT
  346. if (initialize_utf_tbl()) return 1;
  347. #endif
  348. #endif
  349. }
  350. }
  351. /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */
  352. if (strncmp(line,"COMPLEXPREFIXES",15) == 0)
  353. complexprefixes = 1;
  354. /* parse in the flag used by the controlled compound words */
  355. if (strncmp(line,"COMPOUNDFLAG",12) == 0) {
  356. if (parse_flag(line, &compoundflag, afflst)) {
  357. delete afflst;
  358. return 1;
  359. }
  360. }
  361. /* parse in the flag used by compound words */
  362. if (strncmp(line,"COMPOUNDBEGIN",13) == 0) {
  363. if (complexprefixes) {
  364. if (parse_flag(line, &compoundend, afflst)) {
  365. delete afflst;
  366. return 1;
  367. }
  368. } else {
  369. if (parse_flag(line, &compoundbegin, afflst)) {
  370. delete afflst;
  371. return 1;
  372. }
  373. }
  374. }
  375. /* parse in the flag used by compound words */
  376. if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) {
  377. if (parse_flag(line, &compoundmiddle, afflst)) {
  378. delete afflst;
  379. return 1;
  380. }
  381. }
  382. /* parse in the flag used by compound words */
  383. if (strncmp(line,"COMPOUNDEND",11) == 0) {
  384. if (complexprefixes) {
  385. if (parse_flag(line, &compoundbegin, afflst)) {
  386. delete afflst;
  387. return 1;
  388. }
  389. } else {
  390. if (parse_flag(line, &compoundend, afflst)) {
  391. delete afflst;
  392. return 1;
  393. }
  394. }
  395. }
  396. /* parse in the data used by compound_check() method */
  397. if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) {
  398. if (parse_num(line, &cpdwordmax, afflst)) {
  399. delete afflst;
  400. return 1;
  401. }
  402. }
  403. /* parse in the flag sign compounds in dictionary */
  404. if (strncmp(line,"COMPOUNDROOT",12) == 0) {
  405. if (parse_flag(line, &compoundroot, afflst)) {
  406. delete afflst;
  407. return 1;
  408. }
  409. }
  410. /* parse in the flag used by compound_check() method */
  411. if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) {
  412. if (parse_flag(line, &compoundpermitflag, afflst)) {
  413. delete afflst;
  414. return 1;
  415. }
  416. }
  417. /* parse in the flag used by compound_check() method */
  418. if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) {
  419. if (parse_flag(line, &compoundforbidflag, afflst)) {
  420. delete afflst;
  421. return 1;
  422. }
  423. }
  424. if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) {
  425. checkcompounddup = 1;
  426. }
  427. if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) {
  428. checkcompoundrep = 1;
  429. }
  430. if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) {
  431. checkcompoundtriple = 1;
  432. }
  433. if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) {
  434. simplifiedtriple = 1;
  435. }
  436. if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) {
  437. checkcompoundcase = 1;
  438. }
  439. if (strncmp(line,"NOSUGGEST",9) == 0) {
  440. if (parse_flag(line, &nosuggest, afflst)) {
  441. delete afflst;
  442. return 1;
  443. }
  444. }
  445. if (strncmp(line,"NONGRAMSUGGEST",14) == 0) {
  446. if (parse_flag(line, &nongramsuggest, afflst)) {
  447. delete afflst;
  448. return 1;
  449. }
  450. }
  451. /* parse in the flag used by forbidden words */
  452. if (strncmp(line,"FORBIDDENWORD",13) == 0) {
  453. if (parse_flag(line, &forbiddenword, afflst)) {
  454. delete afflst;
  455. return 1;
  456. }
  457. }
  458. /* parse in the flag used by forbidden words */
  459. if (strncmp(line,"LEMMA_PRESENT",13) == 0) {
  460. if (parse_flag(line, &lemma_present, afflst)) {
  461. delete afflst;
  462. return 1;
  463. }
  464. }
  465. /* parse in the flag used by circumfixes */
  466. if (strncmp(line,"CIRCUMFIX",9) == 0) {
  467. if (parse_flag(line, &circumfix, afflst)) {
  468. delete afflst;
  469. return 1;
  470. }
  471. }
  472. /* parse in the flag used by fogemorphemes */
  473. if (strncmp(line,"ONLYINCOMPOUND",14) == 0) {
  474. if (parse_flag(line, &onlyincompound, afflst)) {
  475. delete afflst;
  476. return 1;
  477. }
  478. }
  479. /* parse in the flag used by `needaffixs' */
  480. if (strncmp(line,"PSEUDOROOT",10) == 0) {
  481. if (parse_flag(line, &needaffix, afflst)) {
  482. delete afflst;
  483. return 1;
  484. }
  485. }
  486. /* parse in the flag used by `needaffixs' */
  487. if (strncmp(line,"NEEDAFFIX",9) == 0) {
  488. if (parse_flag(line, &needaffix, afflst)) {
  489. delete afflst;
  490. return 1;
  491. }
  492. }
  493. /* parse in the minimal length for words in compounds */
  494. if (strncmp(line,"COMPOUNDMIN",11) == 0) {
  495. if (parse_num(line, &cpdmin, afflst)) {
  496. delete afflst;
  497. return 1;
  498. }
  499. if (cpdmin < 1) cpdmin = 1;
  500. }
  501. /* parse in the max. words and syllables in compounds */
  502. if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) {
  503. if (parse_cpdsyllable(line, afflst)) {
  504. delete afflst;
  505. return 1;
  506. }
  507. }
  508. /* parse in the flag used by compound_check() method */
  509. if (strncmp(line,"SYLLABLENUM",11) == 0) {
  510. if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) {
  511. delete afflst;
  512. return 1;
  513. }
  514. }
  515. /* parse in the flag used by the controlled compound words */
  516. if (strncmp(line,"CHECKNUM",8) == 0) {
  517. checknum=1;
  518. }
  519. /* parse in the extra word characters */
  520. if (strncmp(line,"WORDCHARS",9) == 0) {
  521. if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) {
  522. delete afflst;
  523. return 1;
  524. }
  525. }
  526. /* parse in the ignored characters (for example, Arabic optional diacretics charachters */
  527. if (strncmp(line,"IGNORE",6) == 0) {
  528. if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
  529. delete afflst;
  530. return 1;
  531. }
  532. }
  533. /* parse in the typical fault correcting table */
  534. if (strncmp(line,"REP",3) == 0) {
  535. if (parse_reptable(line, afflst)) {
  536. delete afflst;
  537. return 1;
  538. }
  539. }
  540. /* parse in the input conversion table */
  541. if (strncmp(line,"ICONV",5) == 0) {
  542. if (parse_convtable(line, afflst, &iconvtable, "ICONV")) {
  543. delete afflst;
  544. return 1;
  545. }
  546. }
  547. /* parse in the input conversion table */
  548. if (strncmp(line,"OCONV",5) == 0) {
  549. if (parse_convtable(line, afflst, &oconvtable, "OCONV")) {
  550. delete afflst;
  551. return 1;
  552. }
  553. }
  554. /* parse in the phonetic translation table */
  555. if (strncmp(line,"PHONE",5) == 0) {
  556. if (parse_phonetable(line, afflst)) {
  557. delete afflst;
  558. return 1;
  559. }
  560. }
  561. /* parse in the checkcompoundpattern table */
  562. if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) {
  563. if (parse_checkcpdtable(line, afflst)) {
  564. delete afflst;
  565. return 1;
  566. }
  567. }
  568. /* parse in the defcompound table */
  569. if (strncmp(line,"COMPOUNDRULE",12) == 0) {
  570. if (parse_defcpdtable(line, afflst)) {
  571. delete afflst;
  572. return 1;
  573. }
  574. }
  575. /* parse in the related character map table */
  576. if (strncmp(line,"MAP",3) == 0) {
  577. if (parse_maptable(line, afflst)) {
  578. delete afflst;
  579. return 1;
  580. }
  581. }
  582. /* parse in the word breakpoints table */
  583. if (strncmp(line,"BREAK",5) == 0) {
  584. if (parse_breaktable(line, afflst)) {
  585. delete afflst;
  586. return 1;
  587. }
  588. }
  589. /* parse in the language for language specific codes */
  590. if (strncmp(line,"LANG",4) == 0) {
  591. if (parse_string(line, &lang, afflst->getlinenum())) {
  592. delete afflst;
  593. return 1;
  594. }
  595. langnum = get_lang_num(lang);
  596. }
  597. if (strncmp(line,"VERSION",7) == 0) {
  598. for(line = line + 7; *line == ' ' || *line == '\t'; line++);
  599. version = mystrdup(line);
  600. }
  601. if (strncmp(line,"MAXNGRAMSUGS",12) == 0) {
  602. if (parse_num(line, &maxngramsugs, afflst)) {
  603. delete afflst;
  604. return 1;
  605. }
  606. }
  607. if (strncmp(line,"ONLYMAXDIFF", 11) == 0)
  608. onlymaxdiff = 1;
  609. if (strncmp(line,"MAXDIFF",7) == 0) {
  610. if (parse_num(line, &maxdiff, afflst)) {
  611. delete afflst;
  612. return 1;
  613. }
  614. }
  615. if (strncmp(line,"MAXCPDSUGS",10) == 0) {
  616. if (parse_num(line, &maxcpdsugs, afflst)) {
  617. delete afflst;
  618. return 1;
  619. }
  620. }
  621. if (strncmp(line,"NOSPLITSUGS",11) == 0) {
  622. nosplitsugs=1;
  623. }
  624. if (strncmp(line,"FULLSTRIP",9) == 0) {
  625. fullstrip=1;
  626. }
  627. if (strncmp(line,"SUGSWITHDOTS",12) == 0) {
  628. sugswithdots=1;
  629. }
  630. /* parse in the flag used by forbidden words */
  631. if (strncmp(line,"KEEPCASE",8) == 0) {
  632. if (parse_flag(line, &keepcase, afflst)) {
  633. delete afflst;
  634. return 1;
  635. }
  636. }
  637. /* parse in the flag used by `forceucase' */
  638. if (strncmp(line,"FORCEUCASE",10) == 0) {
  639. if (parse_flag(line, &forceucase, afflst)) {
  640. delete afflst;
  641. return 1;
  642. }
  643. }
  644. /* parse in the flag used by `warn' */
  645. if (strncmp(line,"WARN",4) == 0) {
  646. if (parse_flag(line, &warn, afflst)) {
  647. delete afflst;
  648. return 1;
  649. }
  650. }
  651. if (strncmp(line,"FORBIDWARN",10) == 0) {
  652. forbidwarn=1;
  653. }
  654. /* parse in the flag used by the affix generator */
  655. if (strncmp(line,"SUBSTANDARD",11) == 0) {
  656. if (parse_flag(line, &substandard, afflst)) {
  657. delete afflst;
  658. return 1;
  659. }
  660. }
  661. if (strncmp(line,"CHECKSHARPS",11) == 0) {
  662. checksharps=1;
  663. }
  664. /* parse this affix: P - prefix, S - suffix */
  665. ft = ' ';
  666. if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P';
  667. if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S';
  668. if (ft != ' ') {
  669. if (dupflags_ini) {
  670. memset(dupflags, 0, sizeof(dupflags));
  671. dupflags_ini = 0;
  672. }
  673. if (parse_affix(line, ft, afflst, dupflags)) {
  674. delete afflst;
  675. process_pfx_tree_to_list();
  676. process_sfx_tree_to_list();
  677. return 1;
  678. }
  679. }
  680. }
  681. delete afflst;
  682. // convert affix trees to sorted list
  683. process_pfx_tree_to_list();
  684. process_sfx_tree_to_list();
  685. // now we can speed up performance greatly taking advantage of the
  686. // relationship between the affixes and the idea of "subsets".
  687. // View each prefix as a potential leading subset of another and view
  688. // each suffix (reversed) as a potential trailing subset of another.
  689. // To illustrate this relationship if we know the prefix "ab" is found in the
  690. // word to examine, only prefixes that "ab" is a leading subset of need be examined.
  691. // Furthermore is "ab" is not present then none of the prefixes that "ab" is
  692. // is a subset need be examined.
  693. // The same argument goes for suffix string that are reversed.
  694. // Then to top this off why not examine the first char of the word to quickly
  695. // limit the set of prefixes to examine (i.e. the prefixes to examine must
  696. // be leading supersets of the first character of the word (if they exist)
  697. // To take advantage of this "subset" relationship, we need to add two links
  698. // from entry. One to take next if the current prefix is found (call it nexteq)
  699. // and one to take next if the current prefix is not found (call it nextne).
  700. // Since we have built ordered lists, all that remains is to properly initialize
  701. // the nextne and nexteq pointers that relate them
  702. process_pfx_order();
  703. process_sfx_order();
  704. /* get encoding for CHECKCOMPOUNDCASE */
  705. if (!utf8) {
  706. char * enc = get_encoding();
  707. csconv = get_current_cs(enc);
  708. free(enc);
  709. enc = NULL;
  710. char expw[MAXLNLEN];
  711. if (wordchars) {
  712. strcpy(expw, wordchars);
  713. free(wordchars);
  714. } else *expw = '\0';
  715. for (int i = 0; i <= 255; i++) {
  716. if ( (csconv[i].cupper != csconv[i].clower) &&
  717. (! strchr(expw, (char) i))) {
  718. *(expw + strlen(expw) + 1) = '\0';
  719. *(expw + strlen(expw)) = (char) i;
  720. }
  721. }
  722. wordchars = mystrdup(expw);
  723. }
  724. // default BREAK definition
  725. if (numbreak == -1) {
  726. breaktable = (char **) malloc(sizeof(char *) * 3);
  727. if (!breaktable) return 1;
  728. breaktable[0] = mystrdup("-");
  729. breaktable[1] = mystrdup("^-");
  730. breaktable[2] = mystrdup("-$");
  731. if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3;
  732. }
  733. return 0;
  734. }
  735. // we want to be able to quickly access prefix information
  736. // both by prefix flag, and sorted by prefix string itself
  737. // so we need to set up two indexes
  738. int AffixMgr::build_pfxtree(PfxEntry* pfxptr)
  739. {
  740. PfxEntry * ptr;
  741. PfxEntry * pptr;
  742. PfxEntry * ep = pfxptr;
  743. // get the right starting points
  744. const char * key = ep->getKey();
  745. const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
  746. // first index by flag which must exist
  747. ptr = pFlag[flg];
  748. ep->setFlgNxt(ptr);
  749. pFlag[flg] = ep;
  750. // handle the special case of null affix string
  751. if (strlen(key) == 0) {
  752. // always inset them at head of list at element 0
  753. ptr = pStart[0];
  754. ep->setNext(ptr);
  755. pStart[0] = ep;
  756. return 0;
  757. }
  758. // now handle the normal case
  759. ep->setNextEQ(NULL);
  760. ep->setNextNE(NULL);
  761. unsigned char sp = *((const unsigned char *)key);
  762. ptr = pStart[sp];
  763. // handle the first insert
  764. if (!ptr) {
  765. pStart[sp] = ep;
  766. return 0;
  767. }
  768. // otherwise use binary tree insertion so that a sorted
  769. // list can easily be generated later
  770. pptr = NULL;
  771. for (;;) {
  772. pptr = ptr;
  773. if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
  774. ptr = ptr->getNextEQ();
  775. if (!ptr) {
  776. pptr->setNextEQ(ep);
  777. break;
  778. }
  779. } else {
  780. ptr = ptr->getNextNE();
  781. if (!ptr) {
  782. pptr->setNextNE(ep);
  783. break;
  784. }
  785. }
  786. }
  787. return 0;
  788. }
  789. // we want to be able to quickly access suffix information
  790. // both by suffix flag, and sorted by the reverse of the
  791. // suffix string itself; so we need to set up two indexes
  792. int AffixMgr::build_sfxtree(SfxEntry* sfxptr)
  793. {
  794. SfxEntry * ptr;
  795. SfxEntry * pptr;
  796. SfxEntry * ep = sfxptr;
  797. /* get the right starting point */
  798. const char * key = ep->getKey();
  799. const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
  800. // first index by flag which must exist
  801. ptr = sFlag[flg];
  802. ep->setFlgNxt(ptr);
  803. sFlag[flg] = ep;
  804. // next index by affix string
  805. // handle the special case of null affix string
  806. if (strlen(key) == 0) {
  807. // always inset them at head of list at element 0
  808. ptr = sStart[0];
  809. ep->setNext(ptr);
  810. sStart[0] = ep;
  811. return 0;
  812. }
  813. // now handle the normal case
  814. ep->setNextEQ(NULL);
  815. ep->setNextNE(NULL);
  816. unsigned char sp = *((const unsigned char *)key);
  817. ptr = sStart[sp];
  818. // handle the first insert
  819. if (!ptr) {
  820. sStart[sp] = ep;
  821. return 0;
  822. }
  823. // otherwise use binary tree insertion so that a sorted
  824. // list can easily be generated later
  825. pptr = NULL;
  826. for (;;) {
  827. pptr = ptr;
  828. if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
  829. ptr = ptr->getNextEQ();
  830. if (!ptr) {
  831. pptr->setNextEQ(ep);
  832. break;
  833. }
  834. } else {
  835. ptr = ptr->getNextNE();
  836. if (!ptr) {
  837. pptr->setNextNE(ep);
  838. break;
  839. }
  840. }
  841. }
  842. return 0;
  843. }
  844. // convert from binary tree to sorted list
  845. int AffixMgr::process_pfx_tree_to_list()
  846. {
  847. for (int i=1; i< SETSIZE; i++) {
  848. pStart[i] = process_pfx_in_order(pStart[i],NULL);
  849. }
  850. return 0;
  851. }
  852. PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr)
  853. {
  854. if (ptr) {
  855. nptr = process_pfx_in_order(ptr->getNextNE(), nptr);
  856. ptr->setNext(nptr);
  857. nptr = process_pfx_in_order(ptr->getNextEQ(), ptr);
  858. }
  859. return nptr;
  860. }
  861. // convert from binary tree to sorted list
  862. int AffixMgr:: process_sfx_tree_to_list()
  863. {
  864. for (int i=1; i< SETSIZE; i++) {
  865. sStart[i] = process_sfx_in_order(sStart[i],NULL);
  866. }
  867. return 0;
  868. }
  869. SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr)
  870. {
  871. if (ptr) {
  872. nptr = process_sfx_in_order(ptr->getNextNE(), nptr);
  873. ptr->setNext(nptr);
  874. nptr = process_sfx_in_order(ptr->getNextEQ(), ptr);
  875. }
  876. return nptr;
  877. }
  878. // reinitialize the PfxEntry links NextEQ and NextNE to speed searching
  879. // using the idea of leading subsets this time
  880. int AffixMgr::process_pfx_order()
  881. {
  882. PfxEntry* ptr;
  883. // loop through each prefix list starting point
  884. for (int i=1; i < SETSIZE; i++) {
  885. ptr = pStart[i];
  886. // look through the remainder of the list
  887. // and find next entry with affix that
  888. // the current one is not a subset of
  889. // mark that as destination for NextNE
  890. // use next in list that you are a subset
  891. // of as NextEQ
  892. for (; ptr != NULL; ptr = ptr->getNext()) {
  893. PfxEntry * nptr = ptr->getNext();
  894. for (; nptr != NULL; nptr = nptr->getNext()) {
  895. if (! isSubset( ptr->getKey() , nptr->getKey() )) break;
  896. }
  897. ptr->setNextNE(nptr);
  898. ptr->setNextEQ(NULL);
  899. if ((ptr->getNext()) && isSubset(ptr->getKey() , (ptr->getNext())->getKey()))
  900. ptr->setNextEQ(ptr->getNext());
  901. }
  902. // now clean up by adding smart search termination strings:
  903. // if you are already a superset of the previous prefix
  904. // but not a subset of the next, search can end here
  905. // so set NextNE properly
  906. ptr = pStart[i];
  907. for (; ptr != NULL; ptr = ptr->getNext()) {
  908. PfxEntry * nptr = ptr->getNext();
  909. PfxEntry * mptr = NULL;
  910. for (; nptr != NULL; nptr = nptr->getNext()) {
  911. if (! isSubset(ptr->getKey(),nptr->getKey())) break;
  912. mptr = nptr;
  913. }
  914. if (mptr) mptr->setNextNE(NULL);
  915. }
  916. }
  917. return 0;
  918. }
  919. // initialize the SfxEntry links NextEQ and NextNE to speed searching
  920. // using the idea of leading subsets this time
  921. int AffixMgr::process_sfx_order()
  922. {
  923. SfxEntry* ptr;
  924. // loop through each prefix list starting point
  925. for (int i=1; i < SETSIZE; i++) {
  926. ptr = sStart[i];
  927. // look through the remainder of the list
  928. // and find next entry with affix that
  929. // the current one is not a subset of
  930. // mark that as destination for NextNE
  931. // use next in list that you are a subset
  932. // of as NextEQ
  933. for (; ptr != NULL; ptr = ptr->getNext()) {
  934. SfxEntry * nptr = ptr->getNext();
  935. for (; nptr != NULL; nptr = nptr->getNext()) {
  936. if (! isSubset(ptr->getKey(),nptr->getKey())) break;
  937. }
  938. ptr->setNextNE(nptr);
  939. ptr->setNextEQ(NULL);
  940. if ((ptr->getNext()) && isSubset(ptr->getKey(),(ptr->getNext())->getKey()))
  941. ptr->setNextEQ(ptr->getNext());
  942. }
  943. // now clean up by adding smart search termination strings:
  944. // if you are already a superset of the previous suffix
  945. // but not a subset of the next, search can end here
  946. // so set NextNE properly
  947. ptr = sStart[i];
  948. for (; ptr != NULL; ptr = ptr->getNext()) {
  949. SfxEntry * nptr = ptr->getNext();
  950. SfxEntry * mptr = NULL;
  951. for (; nptr != NULL; nptr = nptr->getNext()) {
  952. if (! isSubset(ptr->getKey(),nptr->getKey())) break;
  953. mptr = nptr;
  954. }
  955. if (mptr) mptr->setNextNE(NULL);
  956. }
  957. }
  958. return 0;
  959. }
  960. // add flags to the result for dictionary debugging
  961. void AffixMgr::debugflag(char * result, unsigned short flag) {
  962. char * st = encode_flag(flag);
  963. mystrcat(result, " ", MAXLNLEN);
  964. mystrcat(result, MORPH_FLAG, MAXLNLEN);
  965. if (st) {
  966. mystrcat(result, st, MAXLNLEN);
  967. free(st);
  968. }
  969. }
  970. // calculate the character length of the condition
  971. int AffixMgr::condlen(char * st)
  972. {
  973. int l = 0;
  974. bool group = false;
  975. for(; *st; st++) {
  976. if (*st == '[') {
  977. group = true;
  978. l++;
  979. } else if (*st == ']') group = false;
  980. else if (!group && (!utf8 ||
  981. (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++;
  982. }
  983. return l;
  984. }
  985. int AffixMgr::encodeit(affentry &entry, char * cs)
  986. {
  987. if (strcmp(cs,".") != 0) {
  988. entry.numconds = (char) condlen(cs);
  989. strncpy(entry.c.conds, cs, MAXCONDLEN);
  990. // long condition (end of conds padded by strncpy)
  991. if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) {
  992. entry.opts += aeLONGCOND;
  993. entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1);
  994. if (!entry.c.l.conds2) return 1;
  995. }
  996. } else {
  997. entry.numconds = 0;
  998. entry.c.conds[0] = '\0';
  999. }
  1000. return 0;
  1001. }
  1002. // return 1 if s1 is a leading subset of s2 (dots are for infixes)
  1003. inline int AffixMgr::isSubset(const char * s1, const char * s2)
  1004. {
  1005. while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) {
  1006. s1++;
  1007. s2++;
  1008. }
  1009. return (*s1 == '\0');
  1010. }
  1011. // check word for prefixes
  1012. struct hentry * AffixMgr::prefix_check(const char * word, int len, char in_compound,
  1013. const FLAG needflag)
  1014. {
  1015. struct hentry * rv= NULL;
  1016. pfx = NULL;
  1017. pfxappnd = NULL;
  1018. sfxappnd = NULL;
  1019. // first handle the special case of 0 length prefixes
  1020. PfxEntry * pe = pStart[0];
  1021. while (pe) {
  1022. if (
  1023. // fogemorpheme
  1024. ((in_compound != IN_CPD_NOT) || !(pe->getCont() &&
  1025. (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) &&
  1026. // permit prefixes in compounds
  1027. ((in_compound != IN_CPD_END) || (pe->getCont() &&
  1028. (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen()))))
  1029. ) {
  1030. // check prefix
  1031. rv = pe->checkword(word, len, in_compound, needflag);
  1032. if (rv) {
  1033. pfx=pe; // BUG: pfx not stateless
  1034. return rv;
  1035. }
  1036. }
  1037. pe = pe->getNext();
  1038. }
  1039. // now handle the general case
  1040. unsigned char sp = *((const unsigned char *)word);
  1041. PfxEntry * pptr = pStart[sp];
  1042. while (pptr) {
  1043. if (isSubset(pptr->getKey(),word)) {
  1044. if (
  1045. // fogemorpheme
  1046. ((in_compound != IN_CPD_NOT) || !(pptr->getCont() &&
  1047. (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) &&
  1048. // permit prefixes in compounds
  1049. ((in_compound != IN_CPD_END) || (pptr->getCont() &&
  1050. (TESTAFF(pptr->getCont(), compoundpermitflag, pptr->getContLen()))))
  1051. ) {
  1052. // check prefix
  1053. rv = pptr->checkword(word, len, in_compound, needflag);
  1054. if (rv) {
  1055. pfx=pptr; // BUG: pfx not stateless
  1056. return rv;
  1057. }
  1058. }
  1059. pptr = pptr->getNextEQ();
  1060. } else {
  1061. pptr = pptr->getNextNE();
  1062. }
  1063. }
  1064. return NULL;
  1065. }
  1066. // check word for prefixes
  1067. struct hentry * AffixMgr::prefix_check_twosfx(const char * word, int len,
  1068. char in_compound, const FLAG needflag)
  1069. {
  1070. struct hentry * rv= NULL;
  1071. pfx = NULL;
  1072. sfxappnd = NULL;
  1073. // first handle the special case of 0 length prefixes
  1074. PfxEntry * pe = pStart[0];
  1075. while (pe) {
  1076. rv = pe->check_twosfx(word, len, in_compound, needflag);
  1077. if (rv) return rv;
  1078. pe = pe->getNext();
  1079. }
  1080. // now handle the general case
  1081. unsigned char sp = *((const unsigned char *)word);
  1082. PfxEntry * pptr = pStart[sp];
  1083. while (pptr) {
  1084. if (isSubset(pptr->getKey(),word)) {
  1085. rv = pptr->check_twosfx(word, len, in_compound, needflag);
  1086. if (rv) {
  1087. pfx = pptr;
  1088. return rv;
  1089. }
  1090. pptr = pptr->getNextEQ();
  1091. } else {
  1092. pptr = pptr->getNextNE();
  1093. }
  1094. }
  1095. return NULL;
  1096. }
  1097. // check word for prefixes
  1098. char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound,
  1099. const FLAG needflag)
  1100. {
  1101. char * st;
  1102. char result[MAXLNLEN];
  1103. result[0] = '\0';
  1104. pfx = NULL;
  1105. sfxappnd = NULL;
  1106. // first handle the special case of 0 length prefixes
  1107. PfxEntry * pe = pStart[0];
  1108. while (pe) {
  1109. st = pe->check_morph(word,len,in_compound, needflag);
  1110. if (st) {
  1111. mystrcat(result, st, MAXLNLEN);
  1112. free(st);
  1113. }
  1114. // if (rv) return rv;
  1115. pe = pe->getNext();
  1116. }
  1117. // now handle the general case
  1118. unsigned char sp = *((const unsigned char *)word);
  1119. PfxEntry * pptr = pStart[sp];
  1120. while (pptr) {
  1121. if (isSubset(pptr->getKey(),word)) {
  1122. st = pptr->check_morph(word,len,in_compound, needflag);
  1123. if (st) {
  1124. // fogemorpheme
  1125. if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() &&
  1126. (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) {
  1127. mystrcat(result, st, MAXLNLEN);
  1128. pfx = pptr;
  1129. }
  1130. free(st);
  1131. }
  1132. pptr = pptr->getNextEQ();
  1133. } else {
  1134. pptr = pptr->getNextNE();
  1135. }
  1136. }
  1137. if (*result) return mystrdup(result);
  1138. return NULL;
  1139. }
  1140. // check word for prefixes
  1141. char * AffixMgr::prefix_check_twosfx_morph(const char * word, int len,
  1142. char in_compound, const FLAG needflag)
  1143. {
  1144. char * st;
  1145. char result[MAXLNLEN];
  1146. result[0] = '\0';
  1147. pfx = NULL;
  1148. sfxappnd = NULL;
  1149. // first handle the special case of 0 length prefixes
  1150. PfxEntry * pe = pStart[0];
  1151. while (pe) {
  1152. st = pe->check_twosfx_morph(word,len,in_compound, needflag);
  1153. if (st) {
  1154. mystrcat(result, st, MAXLNLEN);
  1155. free(st);
  1156. }
  1157. pe = pe->getNext();
  1158. }
  1159. // now handle the general case
  1160. unsigned char sp = *((const unsigned char *)word);
  1161. PfxEntry * pptr = pStart[sp];
  1162. while (pptr) {
  1163. if (isSubset(pptr->getKey(),word)) {
  1164. st = pptr->check_twosfx_morph(word, len, in_compound, needflag);
  1165. if (st) {
  1166. mystrcat(result, st, MAXLNLEN);
  1167. free(st);
  1168. pfx = pptr;
  1169. }
  1170. pptr = pptr->getNextEQ();
  1171. } else {
  1172. pptr = pptr->getNextNE();
  1173. }
  1174. }
  1175. if (*result) return mystrdup(result);
  1176. return NULL;
  1177. }
  1178. // Is word a non compound with a REP substitution (see checkcompoundrep)?
  1179. int AffixMgr::cpdrep_check(const char * word, int wl)
  1180. {
  1181. char candidate[MAXLNLEN];
  1182. const char * r;
  1183. int lenr, lenp;
  1184. if ((wl < 2) || !numrep) return 0;
  1185. for (int i=0; i < numrep; i++ ) {
  1186. r = word;
  1187. lenr = strlen(reptable[i].pattern2);
  1188. lenp = strlen(reptable[i].pattern);
  1189. // search every occurence of the pattern in the word
  1190. while ((r=strstr(r, reptable[i].pattern)) != NULL) {
  1191. strcpy(candidate, word);
  1192. if (r-word + lenr + strlen(r+lenp) >= MAXLNLEN) break;
  1193. strcpy(candidate+(r-word),reptable[i].pattern2);
  1194. strcpy(candidate+(r-word)+lenr, r+lenp);
  1195. if (candidate_check(candidate,strlen(candidate))) return 1;
  1196. r++; // search for the next letter
  1197. }
  1198. }
  1199. return 0;
  1200. }
  1201. // forbid compoundings when there are special patterns at word bound
  1202. int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2, const char affixed)
  1203. {
  1204. int len;
  1205. for (int i = 0; i < numcheckcpd; i++) {
  1206. if (isSubset(checkcpdtable[i].pattern2, word + pos) &&
  1207. (!r1 || !checkcpdtable[i].cond ||
  1208. (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) &&
  1209. (!r2 || !checkcpdtable[i].cond2 ||
  1210. (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) &&
  1211. // zero length pattern => only TESTAFF
  1212. // zero pattern (0/flag) => unmodified stem (zero affixes allowed)
  1213. (!*(checkcpdtable[i].pattern) || (
  1214. (*(checkcpdtable[i].pattern)=='0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) ||
  1215. (*(checkcpdtable[i].pattern)!='0' && (len = strlen(checkcpdtable[i].pattern)) &&
  1216. strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) {
  1217. return 1;
  1218. }
  1219. }
  1220. return 0;
  1221. }
  1222. // forbid compounding with neighbouring upper and lower case characters at word bounds
  1223. int AffixMgr::cpdcase_check(const char * word, int pos)
  1224. {
  1225. if (utf8) {
  1226. w_char u, w;
  1227. const char * p;
  1228. u8_u16(&u, 1, word + pos);
  1229. for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--);
  1230. u8_u16(&w, 1, p);
  1231. unsigned short a = (u.h << 8) + u.l;
  1232. unsigned short b = (w.h << 8) + w.l;
  1233. if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) &&
  1234. (a != '-') && (b != '-')) return 1;
  1235. } else {
  1236. unsigned char a = *(word + pos - 1);
  1237. unsigned char b = *(word + pos);
  1238. if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1;
  1239. }
  1240. return 0;
  1241. }
  1242. // check compound patterns
  1243. int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all)
  1244. {
  1245. signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking
  1246. signed short btwp[MAXWORDLEN]; // word positions for metacharacters
  1247. int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions
  1248. short bt = 0;
  1249. int i, j;
  1250. int ok;
  1251. int w = 0;
  1252. if (!*words) {
  1253. w = 1;
  1254. *words = def;
  1255. }
  1256. if (!*words) {
  1257. return 0;
  1258. }
  1259. (*words)[wnum] = rv;
  1260. // has the last word COMPOUNDRULE flag?
  1261. if (rv->alen == 0) {
  1262. (*words)[wnum] = NULL;
  1263. if (w) *words = NULL;
  1264. return 0;
  1265. }
  1266. ok = 0;
  1267. for (i = 0; i < numdefcpd; i++) {
  1268. for (j = 0; j < defcpdtable[i].len; j++) {
  1269. if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' &&
  1270. TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) ok = 1;
  1271. }
  1272. }
  1273. if (ok == 0) {
  1274. (*words)[wnum] = NULL;
  1275. if (w) *words = NULL;
  1276. return 0;
  1277. }
  1278. for (i = 0; i < numdefcpd; i++) {
  1279. signed short pp = 0; // pattern position
  1280. signed short wp = 0; // "words" position
  1281. int ok2;
  1282. ok = 1;
  1283. ok2 = 1;
  1284. do {
  1285. while ((pp < defcpdtable[i].len) && (wp <= wnum)) {
  1286. if (((pp+1) < defcpdtable[i].len) &&
  1287. ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) {
  1288. int wend = (defcpdtable[i].def[pp+1] == '?') ? wp : wnum;
  1289. ok2 = 1;
  1290. pp+=2;
  1291. btpp[bt] = pp;
  1292. btwp[bt] = wp;
  1293. while (wp <= wend) {
  1294. if (!(*words)[wp]->alen ||
  1295. !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp-2], (*words)[wp]->alen)) {
  1296. ok2 = 0;
  1297. break;
  1298. }
  1299. wp++;
  1300. }
  1301. if (wp <= wnum) ok2 = 0;
  1302. btnum[bt] = wp - btwp[bt];
  1303. if (btnum[bt] > 0) bt++;
  1304. if (ok2) break;
  1305. } else {
  1306. ok2 = 1;
  1307. if (!(*words)[wp] || !(*words)[wp]->alen ||
  1308. !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], (*words)[wp]->alen)) {
  1309. ok = 0;
  1310. break;
  1311. }
  1312. pp++;
  1313. wp++;
  1314. if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0;
  1315. }
  1316. }
  1317. if (ok && ok2) {
  1318. int r = pp;
  1319. while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) &&
  1320. ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2;
  1321. if (defcpdtable[i].len <= r) return 1;
  1322. }
  1323. // backtrack
  1324. if (bt) do {
  1325. ok = 1;
  1326. btnum[bt - 1]--;
  1327. pp = btpp[bt - 1];
  1328. wp = btwp[bt - 1] + (signed short) btnum[bt - 1];
  1329. } while ((btnum[bt - 1] < 0) && --bt);
  1330. } while (bt);
  1331. if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1;
  1332. // check zero ending
  1333. while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) &&
  1334. ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2;
  1335. if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1;
  1336. }
  1337. (*words)[wnum] = NULL;
  1338. if (w) *words = NULL;
  1339. return 0;
  1340. }
  1341. inline int AffixMgr::candidate_check(const char * word, int len)
  1342. {
  1343. struct hentry * rv=NULL;
  1344. rv = lookup(word);
  1345. if (rv) return 1;
  1346. // rv = prefix_check(word,len,1);
  1347. // if (rv) return 1;
  1348. rv = affix_check(word,len);
  1349. if (rv) return 1;
  1350. return 0;
  1351. }
  1352. // calculate number of syllable for compound-checking
  1353. short AffixMgr::get_syllable(const char * word, int wlen)
  1354. {
  1355. if (cpdmaxsyllable==0) return 0;
  1356. short num=0;
  1357. if (!utf8) {
  1358. for (int i=0; i<wlen; i++) {
  1359. if (strchr(cpdvowels, word[i])) num++;
  1360. }
  1361. } else if (cpdvowels_utf16) {
  1362. w_char w[MAXWORDUTF8LEN];
  1363. int i = u8_u16(w, MAXWORDUTF8LEN, word);
  1364. for (; i > 0; i--) {
  1365. if (flag_bsearch((unsigned short *) cpdvowels_utf16,
  1366. ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++;
  1367. }
  1368. }
  1369. return num;
  1370. }
  1371. void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) {
  1372. if (utf8) {
  1373. int i;
  1374. for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) {
  1375. for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++);
  1376. }
  1377. for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) {
  1378. for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--);
  1379. }
  1380. } else {
  1381. *cmin = cpdmin;
  1382. *cmax = len - cpdmin + 1;
  1383. }
  1384. }
  1385. // check if compound word is correctly spelled
  1386. // hu_mov_rule = spec. Hungarian rule (XXX)
  1387. struct hentry * AffixMgr::compound_check(const char * word, int len,
  1388. short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL,
  1389. char hu_mov_rule = 0, char is_sug = 0, int * info = NULL)
  1390. {
  1391. int i;
  1392. short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
  1393. struct hentry * rv = NULL;
  1394. struct hentry * rv_first;
  1395. struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
  1396. char st [MAXWORDUTF8LEN + 4];
  1397. char ch = '\0';
  1398. int cmin;
  1399. int cmax;
  1400. int striple = 0;
  1401. int scpd = 0;
  1402. int soldi = 0;
  1403. int oldcmin = 0;
  1404. int oldcmax = 0;
  1405. int oldlen = 0;
  1406. int checkedstriple = 0;
  1407. int onlycpdrule;
  1408. int affixed = 0;
  1409. hentry ** oldwords = words;
  1410. int checked_prefix;
  1411. setcminmax(&cmin, &cmax, word, len);
  1412. strcpy(st, word);
  1413. for (i = cmin; i < cmax; i++) {
  1414. // go to end of the UTF-8 character
  1415. if (utf8) {
  1416. for (; (st[i] & 0xc0) == 0x80; i++);
  1417. if (i >= cmax) return NULL;
  1418. }
  1419. words = oldwords;
  1420. onlycpdrule = (words) ? 1 : 0;
  1421. do { // onlycpdrule loop
  1422. oldnumsyllable = numsyllable;
  1423. oldwordnum = wordnum;
  1424. checked_prefix = 0;
  1425. do { // simplified checkcompoundpattern loop
  1426. if (scpd > 0) {
  1427. for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 ||
  1428. strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++);
  1429. if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop
  1430. strcpy(st + i, checkcpdtable[scpd-1].pattern);
  1431. soldi = i;
  1432. i += strlen(checkcpdtable[scpd-1].pattern);
  1433. strcpy(st + i, checkcpdtable[scpd-1].pattern2);
  1434. strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3));
  1435. oldlen = len;
  1436. len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3);
  1437. oldcmin = cmin;
  1438. oldcmax = cmax;
  1439. setcminmax(&cmin, &cmax, st, len);
  1440. cmax = len - cpdmin + 1;
  1441. }
  1442. ch = st[i];
  1443. st[i] = '\0';
  1444. sfx = NULL;
  1445. pfx = NULL;
  1446. // FIRST WORD
  1447. affixed = 1;
  1448. rv = lookup(st); // perhaps without prefix
  1449. // search homonym with compound flag
  1450. while ((rv) && !hu_mov_rule &&
  1451. ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
  1452. !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
  1453. (compoundbegin && !wordnum && !onlycpdrule &&
  1454. TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
  1455. (compoundmiddle && wordnum && !words && !onlycpdrule &&
  1456. TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
  1457. (numdefcpd && onlycpdrule &&
  1458. ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
  1459. (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))) ||
  1460. (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL &&
  1461. !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)))
  1462. ) {
  1463. rv = rv->next_homonym;
  1464. }
  1465. if (rv) affixed = 0;
  1466. if (!rv) {
  1467. if (onlycpdrule) break;
  1468. if (compoundflag &&
  1469. !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
  1470. if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
  1471. FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
  1472. sfx->getCont() &&
  1473. ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag,
  1474. sfx->getContLen())) || (compoundend &&
  1475. TESTAFF(sfx->getCont(), compoundend,
  1476. sfx->getContLen())))) {
  1477. rv = NULL;
  1478. }
  1479. }
  1480. if (rv ||
  1481. (((wordnum == 0) && compoundbegin &&
  1482. ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
  1483. (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
  1484. ((wordnum > 0) && compoundmiddle &&
  1485. ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
  1486. (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
  1487. ) checked_prefix = 1;
  1488. // else check forbiddenwords and needaffix
  1489. } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
  1490. TESTAFF(rv->astr, needaffix, rv->alen) ||
  1491. TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
  1492. (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen))
  1493. )) {
  1494. st[i] = ch;
  1495. //continue;
  1496. break;
  1497. }
  1498. // check non_compound flag in suffix and prefix
  1499. if ((rv) && !hu_mov_rule &&
  1500. ((pfx && pfx->getCont() &&
  1501. TESTAFF(pfx->getCont(), compoundforbidflag,
  1502. pfx->getContLen())) ||
  1503. (sfx && sfx->getCont() &&
  1504. TESTAFF(sfx->getCont(), compoundforbidflag,
  1505. sfx->getContLen())))) {
  1506. rv = NULL;
  1507. }
  1508. // check compoundend flag in suffix and prefix
  1509. if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
  1510. ((pfx && pfx->getCont() &&
  1511. TESTAFF(pfx->getCont(), compoundend,
  1512. pfx->getContLen())) ||
  1513. (sfx && sfx->getCont() &&
  1514. TESTAFF(sfx->getCont(), compoundend,
  1515. sfx->getContLen())))) {
  1516. rv = NULL;
  1517. }
  1518. // check compoundmiddle flag in suffix and prefix
  1519. if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
  1520. ((pfx && pfx->getCont() &&
  1521. TESTAFF(pfx->getCont(), compoundmiddle,
  1522. pfx->getContLen())) ||
  1523. (sfx && sfx->getCont() &&
  1524. TESTAFF(sfx->getCont(), compoundmiddle,
  1525. sfx->getContLen())))) {
  1526. rv = NULL;
  1527. }
  1528. // check forbiddenwords
  1529. if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
  1530. TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
  1531. (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
  1532. return NULL;
  1533. }
  1534. // increment word number, if the second root has a compoundroot flag
  1535. if ((rv) && compoundroot &&
  1536. (TESTAFF(rv->astr, compoundroot, rv->alen))) {
  1537. wordnum++;
  1538. }
  1539. // first word is acceptable in compound words?
  1540. if (((rv) &&
  1541. ( checked_prefix || (words && words[wnum]) ||
  1542. (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
  1543. ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
  1544. ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))// ||
  1545. // (numdefcpd && )
  1546. // LANG_hu section: spec. Hungarian rule
  1547. || ((langnum == LANG_hu) && hu_mov_rule && (
  1548. TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes
  1549. TESTAFF(rv->astr, 'G', rv->alen) ||
  1550. TESTAFF(rv->astr, 'H', rv->alen)
  1551. )
  1552. )
  1553. // END of LANG_hu section
  1554. ) &&
  1555. (
  1556. // test CHECKCOMPOUNDPATTERN conditions
  1557. scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL ||
  1558. TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)
  1559. )
  1560. && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters
  1561. (word[i-1]==word[i]) && (
  1562. ((i>1) && (word[i-1]==word[i-2])) ||
  1563. ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
  1564. )
  1565. ) ||
  1566. (
  1567. checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i)
  1568. ))
  1569. )
  1570. // LANG_hu section: spec. Hungarian rule
  1571. || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
  1572. (sfx && sfx->getCont() && ( // XXX hardwired Hungarian dic. codes
  1573. TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
  1574. TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
  1575. )
  1576. )
  1577. )
  1578. ) { // first word is ok condition
  1579. // LANG_hu section: spec. Hungarian rule
  1580. if (langnum == LANG_hu) {
  1581. // calculate syllable number of the word
  1582. numsyllable += get_syllable(st, i);
  1583. // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
  1584. if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
  1585. }
  1586. // END of LANG_hu section
  1587. // NEXT WORD(S)
  1588. rv_first = rv;
  1589. st[i] = ch;
  1590. do { // striple loop
  1591. // check simplifiedtriple
  1592. if (simplifiedtriple) {
  1593. if (striple) {
  1594. checkedstriple = 1;
  1595. i--; // check "fahrt" instead of "ahrt" in "Schiffahrt"
  1596. } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1;
  1597. }
  1598. rv = lookup((st+i)); // perhaps without prefix
  1599. // search homonym with compound flag
  1600. while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
  1601. !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
  1602. (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
  1603. (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) ||
  1604. (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL &&
  1605. !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
  1606. )) {
  1607. rv = rv->next_homonym;
  1608. }
  1609. // check FORCEUCASE
  1610. if (rv && forceucase && (rv) &&
  1611. (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL;
  1612. if (rv && words && words[wnum + 1]) return rv_first;
  1613. oldnumsyllable2 = numsyllable;
  1614. oldwordnum2 = wordnum;
  1615. // LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code
  1616. if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
  1617. numsyllable--;
  1618. }
  1619. // END of LANG_hu section
  1620. // increment word number, if the second root has a compoundroot flag
  1621. if ((rv) && (compoundroot) &&
  1622. (TESTAFF(rv->astr, compoundroot, rv->alen))) {
  1623. wordnum++;
  1624. }
  1625. // check forbiddenwords
  1626. if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
  1627. TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
  1628. (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
  1629. // second word is acceptable, as a root?
  1630. // hungarian conventions: compounding is acceptable,
  1631. // when compound forms consist of 2 words, or if more,
  1632. // then the syllable number of root words must be 6, or lesser.
  1633. if ((rv) && (
  1634. (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
  1635. (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
  1636. )
  1637. && (
  1638. ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
  1639. ((cpdmaxsyllable!=0) &&
  1640. (numsyllable + get_syllable(HENTRY_WORD(rv), rv->clen)<=cpdmaxsyllable))
  1641. ) &&
  1642. (
  1643. // test CHECKCOMPOUNDPATTERN
  1644. !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv, 0)
  1645. ) &&
  1646. (
  1647. (!checkcompounddup || (rv != rv_first))
  1648. )
  1649. // test CHECKCOMPOUNDPATTERN conditions
  1650. && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
  1651. TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
  1652. )
  1653. {
  1654. // forbid compound word, if it is a non compound word with typical fault
  1655. if (checkcompoundrep && cpdrep_check(word,len)) return NULL;
  1656. return rv_first;
  1657. }
  1658. numsyllable = oldnumsyllable2;
  1659. wordnum = oldwordnum2;
  1660. // perhaps second word has prefix or/and suffix
  1661. sfx = NULL;
  1662. sfxflag = FLAG_NULL;
  1663. rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL;
  1664. if (!rv && compoundend && !onlycpdrule) {
  1665. sfx = NULL;
  1666. pfx = NULL;
  1667. rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END);
  1668. }
  1669. if (!rv && numdefcpd && words) {
  1670. rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
  1671. if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first;
  1672. rv = NULL;
  1673. }
  1674. // test CHECKCOMPOUNDPATTERN conditions (allowed forms)
  1675. if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
  1676. TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL;
  1677. // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds)
  1678. if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL;
  1679. // check non_compound flag in suffix and prefix
  1680. if ((rv) &&
  1681. ((pfx && pfx->getCont() &&
  1682. TESTAFF(pfx->getCont(), compoundforbidflag,
  1683. pfx->getContLen())) ||
  1684. (sfx && sfx->getCont() &&
  1685. TESTAFF(sfx->getCont(), compoundforbidflag,
  1686. sfx->getContLen())))) {
  1687. rv = NULL;
  1688. }
  1689. // check FORCEUCASE
  1690. if (rv && forceucase && (rv) &&
  1691. (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL;
  1692. // check forbiddenwords
  1693. if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
  1694. TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
  1695. (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
  1696. // pfxappnd = prefix of word+i, or NULL
  1697. // calculate syllable number of prefix.
  1698. // hungarian convention: when syllable number of prefix is more,
  1699. // than 1, the prefix+word counts as two words.
  1700. if (langnum == LANG_hu) {
  1701. // calculate syllable number of the word
  1702. numsyllable += get_syllable(word + i, strlen(word + i));
  1703. // - affix syllable num.
  1704. // XXX only second suffix (inflections, not derivations)
  1705. if (sfxappnd) {
  1706. char * tmp = myrevstrdup(sfxappnd);
  1707. numsyllable -= get_syllable(tmp, strlen(tmp));
  1708. free(tmp);
  1709. }
  1710. // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
  1711. if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
  1712. // increment syllable num, if last word has a SYLLABLENUM flag
  1713. // and the suffix is beginning `s'
  1714. if (cpdsyllablenum) {
  1715. switch (sfxflag) {
  1716. case 'c': { numsyllable+=2; break; }
  1717. case 'J': { numsyllable += 1; break; }
  1718. case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
  1719. }
  1720. }
  1721. }
  1722. // increment word number, if the second word has a compoundroot flag
  1723. if ((rv) && (compoundroot) &&
  1724. (TESTAFF(rv->astr, compoundroot, rv->alen))) {
  1725. wordnum++;
  1726. }
  1727. // second word is acceptable, as a word with prefix or/and suffix?
  1728. // hungarian conventions: compounding is acceptable,
  1729. // when compound forms consist 2 word, otherwise
  1730. // the syllable number of root words is 6, or lesser.
  1731. if ((rv) &&
  1732. (
  1733. ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) ||
  1734. ((cpdmaxsyllable != 0) &&
  1735. (numsyllable <= cpdmaxsyllable))
  1736. )
  1737. && (
  1738. (!checkcompounddup || (rv != rv_first))
  1739. )) {
  1740. // forbid compound word, if it is a non compound word with typical fault
  1741. if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
  1742. return rv_first;
  1743. }
  1744. numsyllable = oldnumsyllable2;
  1745. wordnum = oldwordnum2;
  1746. // perhaps second word is a compound word (recursive call)
  1747. if (wordnum < maxwordnum) {
  1748. rv = compound_check((st+i),strlen(st+i), wordnum+1,
  1749. numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info);
  1750. if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) ||
  1751. (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL;
  1752. } else {
  1753. rv=NULL;
  1754. }
  1755. if (rv) {
  1756. // forbid compound word, if it is a non compound word with typical fault
  1757. if (checkcompoundrep || forbiddenword) {
  1758. struct hentry * rv2 = NULL;
  1759. if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
  1760. // check first part
  1761. if (strncmp(rv->word, word + i, rv->blen) == 0) {
  1762. char r = *(st + i + rv->blen);
  1763. *(st + i + rv->blen) = '\0';
  1764. if (checkcompoundrep && cpdrep_check(st, i + rv->blen)) {
  1765. *(st + i + rv->blen) = r;
  1766. continue;
  1767. }
  1768. if (forbiddenword) {
  1769. rv2 = lookup(word);
  1770. if (!rv2) rv2 = affix_check(word, len);
  1771. if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) &&
  1772. (strncmp(rv2->word, st, i + rv->blen) == 0)) {
  1773. return NULL;
  1774. }
  1775. }
  1776. *(st + i + rv->blen) = r;
  1777. }
  1778. }
  1779. return rv_first;
  1780. }
  1781. } while (striple && !checkedstriple); // end of striple loop
  1782. if (checkedstriple) {
  1783. i++;
  1784. checkedstriple = 0;
  1785. striple = 0;
  1786. }
  1787. } // first word is ok condition
  1788. if (soldi != 0) {
  1789. i = soldi;
  1790. soldi = 0;
  1791. len = oldlen;
  1792. cmin = oldcmin;
  1793. cmax = oldcmax;
  1794. }
  1795. scpd++;
  1796. } while (!onlycpdrule && simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop
  1797. scpd = 0;
  1798. wordnum = oldwordnum;
  1799. numsyllable = oldnumsyllable;
  1800. if (soldi != 0) {
  1801. i = soldi;
  1802. strcpy(st, word); // XXX add more optim.
  1803. soldi = 0;
  1804. } else st[i] = ch;
  1805. } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
  1806. }
  1807. return NULL;
  1808. }
  1809. // check if compound word is correctly spelled
  1810. // hu_mov_rule = spec. Hungarian rule (XXX)
  1811. int AffixMgr::compound_check_morph(const char * word, int len,
  1812. short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words,
  1813. char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL)
  1814. {
  1815. int i;
  1816. short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
  1817. int ok = 0;
  1818. struct hentry * rv = NULL;
  1819. struct hentry * rv_first;
  1820. struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
  1821. char st [MAXWORDUTF8LEN + 4];
  1822. char ch;
  1823. int checked_prefix;
  1824. char presult[MAXLNLEN];
  1825. int cmin;
  1826. int cmax;
  1827. int onlycpdrule;
  1828. int affixed = 0;
  1829. hentry ** oldwords = words;
  1830. setcminmax(&cmin, &cmax, word, len);
  1831. strcpy(st, word);
  1832. for (i = cmin; i < cmax; i++) {
  1833. oldnumsyllable = numsyllable;
  1834. oldwordnum = wordnum;
  1835. checked_prefix = 0;
  1836. // go to end of the UTF-8 character
  1837. if (utf8) {
  1838. for (; (st[i] & 0xc0) == 0x80; i++);
  1839. if (i >= cmax) return 0;
  1840. }
  1841. words = oldwords;
  1842. onlycpdrule = (words) ? 1 : 0;
  1843. do { // onlycpdrule loop
  1844. oldnumsyllable = numsyllable;
  1845. oldwordnum = wordnum;
  1846. checked_prefix = 0;
  1847. ch = st[i];
  1848. st[i] = '\0';
  1849. sfx = NULL;
  1850. // FIRST WORD
  1851. affixed = 1;
  1852. *presult = '\0';
  1853. if (partresult) mystrcat(presult, partresult, MAXLNLEN);
  1854. rv = lookup(st); // perhaps without prefix
  1855. // search homonym with compound flag
  1856. while ((rv) && !hu_mov_rule &&
  1857. ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
  1858. !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
  1859. (compoundbegin && !wordnum && !onlycpdrule &&
  1860. TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
  1861. (compoundmiddle && wordnum && !words && !onlycpdrule &&
  1862. TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
  1863. (numdefcpd && onlycpdrule &&
  1864. ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
  1865. (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))
  1866. ))) {
  1867. rv = rv->next_homonym;
  1868. }
  1869. if (rv) affixed = 0;
  1870. if (rv) {
  1871. sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st);
  1872. if (!HENTRY_FIND(rv, MORPH_STEM)) {
  1873. sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st);
  1874. }
  1875. // store the pointer of the hash entry
  1876. // sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv);
  1877. if (HENTRY_DATA(rv)) {
  1878. sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv));
  1879. }
  1880. }
  1881. if (!rv) {
  1882. if (onlycpdrule) break;
  1883. if (compoundflag &&
  1884. !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
  1885. if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
  1886. FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
  1887. sfx->getCont() &&
  1888. ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag,
  1889. sfx->getContLen())) || (compoundend &&
  1890. TESTAFF(sfx->getCont(), compoundend,
  1891. sfx->getContLen())))) {
  1892. rv = NULL;
  1893. }
  1894. }
  1895. if (rv ||
  1896. (((wordnum == 0) && compoundbegin &&
  1897. ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
  1898. (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
  1899. ((wordnum > 0) && compoundmiddle &&
  1900. ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
  1901. (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
  1902. ) {
  1903. // char * p = prefix_check_morph(st, i, 0, compound);
  1904. char * p = NULL;
  1905. if (compoundflag) p = affix_check_morph(st, i, compoundflag);
  1906. if (!p || (*p == '\0')) {
  1907. if (p) free(p);
  1908. p = NULL;
  1909. if ((wordnum == 0) && compoundbegin) {
  1910. p = affix_check_morph(st, i, compoundbegin);
  1911. } else if ((wordnum > 0) && compoundmiddle) {
  1912. p = affix_check_morph(st, i, compoundmiddle);
  1913. }
  1914. }
  1915. if (p && (*p != '\0')) {
  1916. sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD,
  1917. MORPH_PART, st, line_uniq_app(&p, MSEP_REC));
  1918. }
  1919. if (p) free(p);
  1920. checked_prefix = 1;
  1921. }
  1922. // else check forbiddenwords
  1923. } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
  1924. TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
  1925. TESTAFF(rv->astr, needaffix, rv->alen))) {
  1926. st[i] = ch;
  1927. continue;
  1928. }
  1929. // check non_compound flag in suffix and prefix
  1930. if ((rv) && !hu_mov_rule &&
  1931. ((pfx && pfx->getCont() &&
  1932. TESTAFF(pfx->getCont(), compoundforbidflag,
  1933. pfx->getContLen())) ||
  1934. (sfx && sfx->getCont() &&
  1935. TESTAFF(sfx->getCont(), compoundforbidflag,
  1936. sfx->getContLen())))) {
  1937. continue;
  1938. }
  1939. // check compoundend flag in suffix and prefix
  1940. if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
  1941. ((pfx && pfx->getCont() &&
  1942. TESTAFF(pfx->getCont(), compoundend,
  1943. pfx->getContLen())) ||
  1944. (sfx && sfx->getCont() &&
  1945. TESTAFF(sfx->getCont(), compoundend,
  1946. sfx->getContLen())))) {
  1947. continue;
  1948. }
  1949. // check compoundmiddle flag in suffix and prefix
  1950. if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
  1951. ((pfx && pfx->getCont() &&
  1952. TESTAFF(pfx->getCont(), compoundmiddle,
  1953. pfx->getContLen())) ||
  1954. (sfx && sfx->getCont() &&
  1955. TESTAFF(sfx->getCont(), compoundmiddle,
  1956. sfx->getContLen())))) {
  1957. rv = NULL;
  1958. }
  1959. // check forbiddenwords
  1960. if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen)
  1961. || TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) continue;
  1962. // increment word number, if the second root has a compoundroot flag
  1963. if ((rv) && (compoundroot) &&
  1964. (TESTAFF(rv->astr, compoundroot, rv->alen))) {
  1965. wordnum++;
  1966. }
  1967. // first word is acceptable in compound words?
  1968. if (((rv) &&
  1969. ( checked_prefix || (words && words[wnum]) ||
  1970. (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
  1971. ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
  1972. ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))
  1973. // LANG_hu section: spec. Hungarian rule
  1974. || ((langnum == LANG_hu) && // hu_mov_rule
  1975. hu_mov_rule && (
  1976. TESTAFF(rv->astr, 'F', rv->alen) ||
  1977. TESTAFF(rv->astr, 'G', rv->alen) ||
  1978. TESTAFF(rv->astr, 'H', rv->alen)
  1979. )
  1980. )
  1981. // END of LANG_hu section
  1982. )
  1983. && ! (( checkcompoundtriple && !words && // test triple letters
  1984. (word[i-1]==word[i]) && (
  1985. ((i>1) && (word[i-1]==word[i-2])) ||
  1986. ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
  1987. )
  1988. ) ||
  1989. (
  1990. // test CHECKCOMPOUNDPATTERN
  1991. numcheckcpd && !words && cpdpat_check(word, i, rv, NULL, affixed)
  1992. ) ||
  1993. (
  1994. checkcompoundcase && !words && cpdcase_check(word, i)
  1995. ))
  1996. )
  1997. // LANG_hu section: spec. Hungarian rule
  1998. || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
  1999. (sfx && sfx->getCont() && (
  2000. TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
  2001. TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
  2002. )
  2003. )
  2004. )
  2005. // END of LANG_hu section
  2006. ) {
  2007. // LANG_hu section: spec. Hungarian rule
  2008. if (langnum == LANG_hu) {
  2009. // calculate syllable number of the word
  2010. numsyllable += get_syllable(st, i);
  2011. // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
  2012. if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
  2013. }
  2014. // END of LANG_hu section
  2015. // NEXT WORD(S)
  2016. rv_first = rv;
  2017. rv = lookup((word+i)); // perhaps without prefix
  2018. // search homonym with compound flag
  2019. while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
  2020. !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
  2021. (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
  2022. (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) {
  2023. rv = rv->next_homonym;
  2024. }
  2025. if (rv && words && words[wnum + 1]) {
  2026. mystrcat(*result, presult, MAXLNLEN);
  2027. mystrcat(*result, " ", MAXLNLEN);
  2028. mystrcat(*result, MORPH_PART, MAXLNLEN);
  2029. mystrcat(*result, word+i, MAXLNLEN);
  2030. if (complexprefixes && HENTRY_DATA(rv)) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
  2031. if (!HENTRY_FIND(rv, MORPH_STEM)) {
  2032. mystrcat(*result, " ", MAXLNLEN);
  2033. mystrcat(*result, MORPH_STEM, MAXLNLEN);
  2034. mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
  2035. }
  2036. // store the pointer of the hash entry
  2037. // sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
  2038. if (!complexprefixes && HENTRY_DATA(rv)) {
  2039. mystrcat(*result, " ", MAXLNLEN);
  2040. mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
  2041. }
  2042. mystrcat(*result, "\n", MAXLNLEN);
  2043. ok = 1;
  2044. return 0;
  2045. }
  2046. oldnumsyllable2 = numsyllable;
  2047. oldwordnum2 = wordnum;
  2048. // LANG_hu section: spec. Hungarian rule
  2049. if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
  2050. numsyllable--;
  2051. }
  2052. // END of LANG_hu section
  2053. // increment word number, if the second root has a compoundroot flag
  2054. if ((rv) && (compoundroot) &&
  2055. (TESTAFF(rv->astr, compoundroot, rv->alen))) {
  2056. wordnum++;
  2057. }
  2058. // check forbiddenwords
  2059. if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
  2060. TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) {
  2061. st[i] = ch;
  2062. continue;
  2063. }
  2064. // second word is acceptable, as a root?
  2065. // hungarian conventions: compounding is acceptable,
  2066. // when compound forms consist of 2 words, or if more,
  2067. // then the syllable number of root words must be 6, or lesser.
  2068. if ((rv) && (
  2069. (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
  2070. (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
  2071. )
  2072. && (
  2073. ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
  2074. ((cpdmaxsyllable!=0) &&
  2075. (numsyllable+get_syllable(HENTRY_WORD(rv),rv->blen)<=cpdmaxsyllable))
  2076. )
  2077. && (
  2078. (!checkcompounddup || (rv != rv_first))
  2079. )
  2080. )
  2081. {
  2082. // bad compound word
  2083. mystrcat(*result, presult, MAXLNLEN);
  2084. mystrcat(*result, " ", MAXLNLEN);
  2085. mystrcat(*result, MORPH_PART, MAXLNLEN);
  2086. mystrcat(*result, word+i, MAXLNLEN);
  2087. if (HENTRY_DATA(rv)) {
  2088. if (complexprefixes) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
  2089. if (! HENTRY_FIND(rv, MORPH_STEM)) {
  2090. mystrcat(*result, " ", MAXLNLEN);
  2091. mystrcat(*result, MORPH_STEM, MAXLNLEN);
  2092. mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
  2093. }
  2094. // store the pointer of the hash entry
  2095. // sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
  2096. if (!complexprefixes) {
  2097. mystrcat(*result, " ", MAXLNLEN);
  2098. mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
  2099. }
  2100. }
  2101. mystrcat(*result, "\n", MAXLNLEN);
  2102. ok = 1;
  2103. }
  2104. numsyllable = oldnumsyllable2 ;
  2105. wordnum = oldwordnum2;
  2106. // perhaps second word has prefix or/and suffix
  2107. sfx = NULL;
  2108. sfxflag = FLAG_NULL;
  2109. if (compoundflag && !onlycpdrule) rv = affix_check((word+i),strlen(word+i), compoundflag); else rv = NULL;
  2110. if (!rv && compoundend && !onlycpdrule) {
  2111. sfx = NULL;
  2112. pfx = NULL;
  2113. rv = affix_check((word+i),strlen(word+i), compoundend);
  2114. }
  2115. if (!rv && numdefcpd && words) {
  2116. rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
  2117. if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
  2118. char * m = NULL;
  2119. if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
  2120. if ((!m || *m == '\0') && compoundend) {
  2121. if (m) free(m);
  2122. m = affix_check_morph((word+i),strlen(word+i), compoundend);
  2123. }
  2124. mystrcat(*result, presult, MAXLNLEN);
  2125. if (m || (*m != '\0')) {
  2126. sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
  2127. MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
  2128. }
  2129. if (m) free(m);
  2130. mystrcat(*result, "\n", MAXLNLEN);
  2131. ok = 1;
  2132. }
  2133. }
  2134. // check non_compound flag in suffix and prefix
  2135. if ((rv) &&
  2136. ((pfx && pfx->getCont() &&
  2137. TESTAFF(pfx->getCont(), compoundforbidflag,
  2138. pfx->getContLen())) ||
  2139. (sfx && sfx->getCont() &&
  2140. TESTAFF(sfx->getCont(), compoundforbidflag,
  2141. sfx->getContLen())))) {
  2142. rv = NULL;
  2143. }
  2144. // check forbiddenwords
  2145. if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen) ||
  2146. TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))
  2147. && (! TESTAFF(rv->astr, needaffix, rv->alen))) {
  2148. st[i] = ch;
  2149. continue;
  2150. }
  2151. if (langnum == LANG_hu) {
  2152. // calculate syllable number of the word
  2153. numsyllable += get_syllable(word + i, strlen(word + i));
  2154. // - affix syllable num.
  2155. // XXX only second suffix (inflections, not derivations)
  2156. if (sfxappnd) {
  2157. char * tmp = myrevstrdup(sfxappnd);
  2158. numsyllable -= get_syllable(tmp, strlen(tmp));
  2159. free(tmp);
  2160. }
  2161. // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
  2162. if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
  2163. // increment syllable num, if last word has a SYLLABLENUM flag
  2164. // and the suffix is beginning `s'
  2165. if (cpdsyllablenum) {
  2166. switch (sfxflag) {
  2167. case 'c': { numsyllable+=2; break; }
  2168. case 'J': { numsyllable += 1; break; }
  2169. case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
  2170. }
  2171. }
  2172. }
  2173. // increment word number, if the second word has a compoundroot flag
  2174. if ((rv) && (compoundroot) &&
  2175. (TESTAFF(rv->astr, compoundroot, rv->alen))) {
  2176. wordnum++;
  2177. }
  2178. // second word is acceptable, as a word with prefix or/and suffix?
  2179. // hungarian conventions: compounding is acceptable,
  2180. // when compound forms consist 2 word, otherwise
  2181. // the syllable number of root words is 6, or lesser.
  2182. if ((rv) &&
  2183. (
  2184. ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
  2185. ((cpdmaxsyllable!=0) &&
  2186. (numsyllable <= cpdmaxsyllable))
  2187. )
  2188. && (
  2189. (!checkcompounddup || (rv != rv_first))
  2190. )) {
  2191. char * m = NULL;
  2192. if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
  2193. if ((!m || *m == '\0') && compoundend) {
  2194. if (m) free(m);
  2195. m = affix_check_morph((word+i),strlen(word+i), compoundend);
  2196. }
  2197. mystrcat(*result, presult, MAXLNLEN);
  2198. if (m && (*m != '\0')) {
  2199. sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
  2200. MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
  2201. }
  2202. if (m) free(m);
  2203. sprintf(*result + strlen(*result), "%c", MSEP_REC);
  2204. ok = 1;
  2205. }
  2206. numsyllable = oldnumsyllable2;
  2207. wordnum = oldwordnum2;
  2208. // perhaps second word is a compound word (recursive call)
  2209. if ((wordnum < maxwordnum) && (ok == 0)) {
  2210. compound_check_morph((word+i),strlen(word+i), wordnum+1,
  2211. numsyllable, maxwordnum, wnum + 1, words, 0, result, presult);
  2212. } else {
  2213. rv=NULL;
  2214. }
  2215. }
  2216. st[i] = ch;
  2217. wordnum = oldwordnum;
  2218. numsyllable = oldnumsyllable;
  2219. } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
  2220. }
  2221. return 0;
  2222. }
  2223. // return 1 if s1 (reversed) is a leading subset of end of s2
  2224. /* inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
  2225. {
  2226. while ((len > 0) && *s1 && (*s1 == *end_of_s2)) {
  2227. s1++;
  2228. end_of_s2--;
  2229. len--;
  2230. }
  2231. return (*s1 == '\0');
  2232. }
  2233. */
  2234. inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
  2235. {
  2236. while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) {
  2237. s1++;
  2238. end_of_s2--;
  2239. len--;
  2240. }
  2241. return (*s1 == '\0');
  2242. }
  2243. // check word for suffixes
  2244. struct hentry * AffixMgr::suffix_check (const char * word, int len,
  2245. int sfxopts, PfxEntry * ppfx, char ** wlst, int maxSug, int * ns,
  2246. const FLAG cclass, const FLAG needflag, char in_compound)
  2247. {
  2248. struct hentry * rv = NULL;
  2249. PfxEntry* ep = ppfx;
  2250. // first handle the special case of 0 length suffixes
  2251. SfxEntry * se = sStart[0];
  2252. while (se) {
  2253. if (!cclass || se->getCont()) {
  2254. // suffixes are not allowed in beginning of compounds
  2255. if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
  2256. // except when signed with compoundpermitflag flag
  2257. (se->getCont() && compoundpermitflag &&
  2258. TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
  2259. // no circumfix flag in prefix and suffix
  2260. ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
  2261. circumfix, ep->getContLen())) &&
  2262. (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
  2263. // circumfix flag in prefix AND suffix
  2264. ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
  2265. circumfix, ep->getContLen())) &&
  2266. (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) &&
  2267. // fogemorpheme
  2268. (in_compound ||
  2269. !(se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) &&
  2270. // needaffix on prefix or first suffix
  2271. (cclass ||
  2272. !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
  2273. (ppfx && !((ep->getCont()) &&
  2274. TESTAFF(ep->getCont(), needaffix,
  2275. ep->getContLen())))
  2276. )) {
  2277. rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass,
  2278. needflag, (in_compound ? 0 : onlyincompound));
  2279. if (rv) {
  2280. sfx=se; // BUG: sfx not stateless
  2281. return rv;
  2282. }
  2283. }
  2284. }
  2285. se = se->getNext();
  2286. }
  2287. // now handle the general case
  2288. if (len == 0) return NULL; // FULLSTRIP
  2289. unsigned char sp= *((const unsigned char *)(word + len - 1));
  2290. SfxEntry * sptr = sStart[sp];
  2291. while (sptr) {
  2292. if (isRevSubset(sptr->getKey(), word + len - 1, len)
  2293. ) {
  2294. // suffixes are not allowed in beginning of compounds
  2295. if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
  2296. // except when signed with compoundpermitflag flag
  2297. (sptr->getCont() && compoundpermitflag &&
  2298. TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
  2299. // no circumfix flag in prefix and suffix
  2300. ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
  2301. circumfix, ep->getContLen())) &&
  2302. (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
  2303. // circumfix flag in prefix AND suffix
  2304. ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
  2305. circumfix, ep->getContLen())) &&
  2306. (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) &&
  2307. // fogemorpheme
  2308. (in_compound ||
  2309. !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
  2310. // needaffix on prefix or first suffix
  2311. (cclass ||
  2312. !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
  2313. (ppfx && !((ep->getCont()) &&
  2314. TESTAFF(ep->getCont(), needaffix,
  2315. ep->getContLen())))
  2316. )
  2317. ) if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) {
  2318. rv = sptr->checkword(word,len, sfxopts, ppfx, wlst,
  2319. maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound));
  2320. if (rv) {
  2321. sfx=sptr; // BUG: sfx not stateless
  2322. sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
  2323. if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
  2324. return rv;
  2325. }
  2326. }
  2327. sptr = sptr->getNextEQ();
  2328. } else {
  2329. sptr = sptr->getNextNE();
  2330. }
  2331. }
  2332. return NULL;
  2333. }
  2334. // check word for two-level suffixes
  2335. struct hentry * AffixMgr::suffix_check_twosfx(const char * word, int len,
  2336. int sfxopts, PfxEntry * ppfx, const FLAG needflag)
  2337. {
  2338. struct hentry * rv = NULL;
  2339. // first handle the special case of 0 length suffixes
  2340. SfxEntry * se = sStart[0];
  2341. while (se) {
  2342. if (contclasses[se->getFlag()])
  2343. {
  2344. rv = se->check_twosfx(word,len, sfxopts, ppfx, needflag);
  2345. if (rv) return rv;
  2346. }
  2347. se = se->getNext();
  2348. }
  2349. // now handle the general case
  2350. if (len == 0) return NULL; // FULLSTRIP
  2351. unsigned char sp = *((const unsigned char *)(word + len - 1));
  2352. SfxEntry * sptr = sStart[sp];
  2353. while (sptr) {
  2354. if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
  2355. if (contclasses[sptr->getFlag()])
  2356. {
  2357. rv = sptr->check_twosfx(word,len, sfxopts, ppfx, needflag);
  2358. if (rv) {
  2359. sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
  2360. if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
  2361. return rv;
  2362. }
  2363. }
  2364. sptr = sptr->getNextEQ();
  2365. } else {
  2366. sptr = sptr->getNextNE();
  2367. }
  2368. }
  2369. return NULL;
  2370. }
  2371. char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len,
  2372. int sfxopts, PfxEntry * ppfx, const FLAG needflag)
  2373. {
  2374. char result[MAXLNLEN];
  2375. char result2[MAXLNLEN];
  2376. char result3[MAXLNLEN];
  2377. char * st;
  2378. result[0] = '\0';
  2379. result2[0] = '\0';
  2380. result3[0] = '\0';
  2381. // first handle the special case of 0 length suffixes
  2382. SfxEntry * se = sStart[0];
  2383. while (se) {
  2384. if (contclasses[se->getFlag()])
  2385. {
  2386. st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
  2387. if (st) {
  2388. if (ppfx) {
  2389. if (ppfx->getMorph()) {
  2390. mystrcat(result, ppfx->getMorph(), MAXLNLEN);
  2391. mystrcat(result, " ", MAXLNLEN);
  2392. } else debugflag(result, ppfx->getFlag());
  2393. }
  2394. mystrcat(result, st, MAXLNLEN);
  2395. free(st);
  2396. if (se->getMorph()) {
  2397. mystrcat(result, " ", MAXLNLEN);
  2398. mystrcat(result, se->getMorph(), MAXLNLEN);
  2399. } else debugflag(result, se->getFlag());
  2400. mystrcat(result, "\n", MAXLNLEN);
  2401. }
  2402. }
  2403. se = se->getNext();
  2404. }
  2405. // now handle the general case
  2406. if (len == 0) return NULL; // FULLSTRIP
  2407. unsigned char sp = *((const unsigned char *)(word + len - 1));
  2408. SfxEntry * sptr = sStart[sp];
  2409. while (sptr) {
  2410. if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
  2411. if (contclasses[sptr->getFlag()])
  2412. {
  2413. st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
  2414. if (st) {
  2415. sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
  2416. if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
  2417. strcpy(result2, st);
  2418. free(st);
  2419. result3[0] = '\0';
  2420. if (sptr->getMorph()) {
  2421. mystrcat(result3, " ", MAXLNLEN);
  2422. mystrcat(result3, sptr->getMorph(), MAXLNLEN);
  2423. } else debugflag(result3, sptr->getFlag());
  2424. strlinecat(result2, result3);
  2425. mystrcat(result2, "\n", MAXLNLEN);
  2426. mystrcat(result, result2, MAXLNLEN);
  2427. }
  2428. }
  2429. sptr = sptr->getNextEQ();
  2430. } else {
  2431. sptr = sptr->getNextNE();
  2432. }
  2433. }
  2434. if (*result) return mystrdup(result);
  2435. return NULL;
  2436. }
  2437. char * AffixMgr::suffix_check_morph(const char * word, int len,
  2438. int sfxopts, PfxEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound)
  2439. {
  2440. char result[MAXLNLEN];
  2441. struct hentry * rv = NULL;
  2442. result[0] = '\0';
  2443. PfxEntry* ep = ppfx;
  2444. // first handle the special case of 0 length suffixes
  2445. SfxEntry * se = sStart[0];
  2446. while (se) {
  2447. if (!cclass || se->getCont()) {
  2448. // suffixes are not allowed in beginning of compounds
  2449. if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
  2450. // except when signed with compoundpermitflag flag
  2451. (se->getCont() && compoundpermitflag &&
  2452. TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
  2453. // no circumfix flag in prefix and suffix
  2454. ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
  2455. circumfix, ep->getContLen())) &&
  2456. (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
  2457. // circumfix flag in prefix AND suffix
  2458. ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
  2459. circumfix, ep->getContLen())) &&
  2460. (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) &&
  2461. // fogemorpheme
  2462. (in_compound ||
  2463. !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) &&
  2464. // needaffix on prefix or first suffix
  2465. (cclass ||
  2466. !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
  2467. (ppfx && !((ep->getCont()) &&
  2468. TESTAFF(ep->getCont(), needaffix,
  2469. ep->getContLen())))
  2470. )
  2471. ))
  2472. rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
  2473. while (rv) {
  2474. if (ppfx) {
  2475. if (ppfx->getMorph()) {
  2476. mystrcat(result, ppfx->getMorph(), MAXLNLEN);
  2477. mystrcat(result, " ", MAXLNLEN);
  2478. } else debugflag(result, ppfx->getFlag());
  2479. }
  2480. if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
  2481. if (! HENTRY_FIND(rv, MORPH_STEM)) {
  2482. mystrcat(result, " ", MAXLNLEN);
  2483. mystrcat(result, MORPH_STEM, MAXLNLEN);
  2484. mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
  2485. }
  2486. // store the pointer of the hash entry
  2487. // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
  2488. if (!complexprefixes && HENTRY_DATA(rv)) {
  2489. mystrcat(result, " ", MAXLNLEN);
  2490. mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
  2491. }
  2492. if (se->getMorph()) {
  2493. mystrcat(result, " ", MAXLNLEN);
  2494. mystrcat(result, se->getMorph(), MAXLNLEN);
  2495. } else debugflag(result, se->getFlag());
  2496. mystrcat(result, "\n", MAXLNLEN);
  2497. rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
  2498. }
  2499. }
  2500. se = se->getNext();
  2501. }
  2502. // now handle the general case
  2503. if (len == 0) return NULL; // FULLSTRIP
  2504. unsigned char sp = *((const unsigned char *)(word + len - 1));
  2505. SfxEntry * sptr = sStart[sp];
  2506. while (sptr) {
  2507. if (isRevSubset(sptr->getKey(), word + len - 1, len)
  2508. ) {
  2509. // suffixes are not allowed in beginning of compounds
  2510. if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
  2511. // except when signed with compoundpermitflag flag
  2512. (sptr->getCont() && compoundpermitflag &&
  2513. TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
  2514. // no circumfix flag in prefix and suffix
  2515. ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
  2516. circumfix, ep->getContLen())) &&
  2517. (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
  2518. // circumfix flag in prefix AND suffix
  2519. ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
  2520. circumfix, ep->getContLen())) &&
  2521. (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) &&
  2522. // fogemorpheme
  2523. (in_compound ||
  2524. !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
  2525. // needaffix on first suffix
  2526. (cclass || !(sptr->getCont() &&
  2527. TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())))
  2528. )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
  2529. while (rv) {
  2530. if (ppfx) {
  2531. if (ppfx->getMorph()) {
  2532. mystrcat(result, ppfx->getMorph(), MAXLNLEN);
  2533. mystrcat(result, " ", MAXLNLEN);
  2534. } else debugflag(result, ppfx->getFlag());
  2535. }
  2536. if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
  2537. if (! HENTRY_FIND(rv, MORPH_STEM)) {
  2538. mystrcat(result, " ", MAXLNLEN);
  2539. mystrcat(result, MORPH_STEM, MAXLNLEN);
  2540. mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
  2541. }
  2542. // store the pointer of the hash entry
  2543. // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
  2544. if (!complexprefixes && HENTRY_DATA(rv)) {
  2545. mystrcat(result, " ", MAXLNLEN);
  2546. mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
  2547. }
  2548. if (sptr->getMorph()) {
  2549. mystrcat(result, " ", MAXLNLEN);
  2550. mystrcat(result, sptr->getMorph(), MAXLNLEN);
  2551. } else debugflag(result, sptr->getFlag());
  2552. mystrcat(result, "\n", MAXLNLEN);
  2553. rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
  2554. }
  2555. sptr = sptr->getNextEQ();
  2556. } else {
  2557. sptr = sptr->getNextNE();
  2558. }
  2559. }
  2560. if (*result) return mystrdup(result);
  2561. return NULL;
  2562. }
  2563. // check if word with affixes is correctly spelled
  2564. struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound)
  2565. {
  2566. struct hentry * rv= NULL;
  2567. // check all prefixes (also crossed with suffixes if allowed)
  2568. rv = prefix_check(word, len, in_compound, needflag);
  2569. if (rv) return rv;
  2570. // if still not found check all suffixes
  2571. rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound);
  2572. if (havecontclass) {
  2573. sfx = NULL;
  2574. pfx = NULL;
  2575. if (rv) return rv;
  2576. // if still not found check all two-level suffixes
  2577. rv = suffix_check_twosfx(word, len, 0, NULL, needflag);
  2578. if (rv) return rv;
  2579. // if still not found check all two-level suffixes
  2580. rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag);
  2581. }
  2582. return rv;
  2583. }
  2584. // check if word with affixes is correctly spelled
  2585. char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound)
  2586. {
  2587. char result[MAXLNLEN];
  2588. char * st = NULL;
  2589. *result = '\0';
  2590. // check all prefixes (also crossed with suffixes if allowed)
  2591. st = prefix_check_morph(word, len, in_compound);
  2592. if (st) {
  2593. mystrcat(result, st, MAXLNLEN);
  2594. free(st);
  2595. }
  2596. // if still not found check all suffixes
  2597. st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound);
  2598. if (st) {
  2599. mystrcat(result, st, MAXLNLEN);
  2600. free(st);
  2601. }
  2602. if (havecontclass) {
  2603. sfx = NULL;
  2604. pfx = NULL;
  2605. // if still not found check all two-level suffixes
  2606. st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag);
  2607. if (st) {
  2608. mystrcat(result, st, MAXLNLEN);
  2609. free(st);
  2610. }
  2611. // if still not found check all two-level suffixes
  2612. st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag);
  2613. if (st) {
  2614. mystrcat(result, st, MAXLNLEN);
  2615. free(st);
  2616. }
  2617. }
  2618. return mystrdup(result);
  2619. }
  2620. char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap,
  2621. unsigned short al, char * morph, char * targetmorph, int level)
  2622. {
  2623. // handle suffixes
  2624. char * stemmorph;
  2625. char * stemmorphcatpos;
  2626. char mymorph[MAXLNLEN];
  2627. if (!morph) return NULL;
  2628. // check substandard flag
  2629. if (TESTAFF(ap, substandard, al)) return NULL;
  2630. if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts);
  2631. // int targetcount = get_sfxcount(targetmorph);
  2632. // use input suffix fields, if exist
  2633. if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) {
  2634. stemmorph = mymorph;
  2635. strcpy(stemmorph, morph);
  2636. mystrcat(stemmorph, " ", MAXLNLEN);
  2637. stemmorphcatpos = stemmorph + strlen(stemmorph);
  2638. } else {
  2639. stemmorph = morph;
  2640. stemmorphcatpos = NULL;
  2641. }
  2642. for (int i = 0; i < al; i++) {
  2643. const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
  2644. SfxEntry * sptr = sFlag[c];
  2645. while (sptr) {
  2646. if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) ||
  2647. // don't generate forms with substandard affixes
  2648. !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) {
  2649. if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph());
  2650. else stemmorph = (char *) sptr->getMorph();
  2651. int cmp = morphcmp(stemmorph, targetmorph);
  2652. if (cmp == 0) {
  2653. char * newword = sptr->add(ts, wl);
  2654. if (newword) {
  2655. hentry * check = pHMgr->lookup(newword); // XXX extra dic
  2656. if (!check || !check->astr ||
  2657. !(TESTAFF(check->astr, forbiddenword, check->alen) ||
  2658. TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) {
  2659. return newword;
  2660. }
  2661. free(newword);
  2662. }
  2663. }
  2664. // recursive call for secondary suffixes
  2665. if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) &&
  2666. // (get_sfxcount(stemmorph) < targetcount) &&
  2667. !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) {
  2668. char * newword = sptr->add(ts, wl);
  2669. if (newword) {
  2670. char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(),
  2671. sptr->getContLen(), stemmorph, targetmorph, 1);
  2672. if (newword2) {
  2673. free(newword);
  2674. return newword2;
  2675. }
  2676. free(newword);
  2677. newword = NULL;
  2678. }
  2679. }
  2680. }
  2681. sptr = sptr->getFlgNxt();
  2682. }
  2683. }
  2684. return NULL;
  2685. }
  2686. int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts,
  2687. int wl, const unsigned short * ap, unsigned short al, char * bad, int badl,
  2688. char * phon)
  2689. {
  2690. int nh=0;
  2691. // first add root word to list
  2692. if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) ||
  2693. (onlyincompound && TESTAFF(ap, onlyincompound, al))))) {
  2694. wlst[nh].word = mystrdup(ts);
  2695. if (!wlst[nh].word) return 0;
  2696. wlst[nh].allow = (1 == 0);
  2697. wlst[nh].orig = NULL;
  2698. nh++;
  2699. // add special phonetic version
  2700. if (phon && (nh < maxn)) {
  2701. wlst[nh].word = mystrdup(phon);
  2702. if (!wlst[nh].word) return nh - 1;
  2703. wlst[nh].allow = (1 == 0);
  2704. wlst[nh].orig = mystrdup(ts);
  2705. if (!wlst[nh].orig) return nh - 1;
  2706. nh++;
  2707. }
  2708. }
  2709. // handle suffixes
  2710. for (int i = 0; i < al; i++) {
  2711. const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
  2712. SfxEntry * sptr = sFlag[c];
  2713. while (sptr) {
  2714. if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) &&
  2715. (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) &&
  2716. // check needaffix flag
  2717. !(sptr->getCont() && ((needaffix &&
  2718. TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
  2719. (circumfix &&
  2720. TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) ||
  2721. (onlyincompound &&
  2722. TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))
  2723. ) {
  2724. char * newword = sptr->add(ts, wl);
  2725. if (newword) {
  2726. if (nh < maxn) {
  2727. wlst[nh].word = newword;
  2728. wlst[nh].allow = sptr->allowCross();
  2729. wlst[nh].orig = NULL;
  2730. nh++;
  2731. // add special phonetic version
  2732. if (phon && (nh < maxn)) {
  2733. char st[MAXWORDUTF8LEN];
  2734. strcpy(st, phon);
  2735. strcat(st, sptr->getKey());
  2736. reverseword(st + strlen(phon));
  2737. wlst[nh].word = mystrdup(st);
  2738. if (!wlst[nh].word) return nh - 1;
  2739. wlst[nh].allow = (1 == 0);
  2740. wlst[nh].orig = mystrdup(newword);
  2741. if (!wlst[nh].orig) return nh - 1;
  2742. nh++;
  2743. }
  2744. } else {
  2745. free(newword);
  2746. }
  2747. }
  2748. }
  2749. sptr = sptr->getFlgNxt();
  2750. }
  2751. }
  2752. int n = nh;
  2753. // handle cross products of prefixes and suffixes
  2754. for (int j=1;j<n ;j++)
  2755. if (wlst[j].allow) {
  2756. for (int k = 0; k < al; k++) {
  2757. const unsigned char c = (unsigned char) (ap[k] & 0x00FF);
  2758. PfxEntry * cptr = pFlag[c];
  2759. while (cptr) {
  2760. if ((cptr->getFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) &&
  2761. (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) {
  2762. int l1 = strlen(wlst[j].word);
  2763. char * newword = cptr->add(wlst[j].word, l1);
  2764. if (newword) {
  2765. if (nh < maxn) {
  2766. wlst[nh].word = newword;
  2767. wlst[nh].allow = cptr->allowCross();
  2768. wlst[nh].orig = NULL;
  2769. nh++;
  2770. } else {
  2771. free(newword);
  2772. }
  2773. }
  2774. }
  2775. cptr = cptr->getFlgNxt();
  2776. }
  2777. }
  2778. }
  2779. // now handle pure prefixes
  2780. for (int m = 0; m < al; m ++) {
  2781. const unsigned char c = (unsigned char) (ap[m] & 0x00FF);
  2782. PfxEntry * ptr = pFlag[c];
  2783. while (ptr) {
  2784. if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) &&
  2785. (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) &&
  2786. // check needaffix flag
  2787. !(ptr->getCont() && ((needaffix &&
  2788. TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) ||
  2789. (circumfix &&
  2790. TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) ||
  2791. (onlyincompound &&
  2792. TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen()))))
  2793. ) {
  2794. char * newword = ptr->add(ts, wl);
  2795. if (newword) {
  2796. if (nh < maxn) {
  2797. wlst[nh].word = newword;
  2798. wlst[nh].allow = ptr->allowCross();
  2799. wlst[nh].orig = NULL;
  2800. nh++;
  2801. } else {
  2802. free(newword);
  2803. }
  2804. }
  2805. }
  2806. ptr = ptr->getFlgNxt();
  2807. }
  2808. }
  2809. return nh;
  2810. }
  2811. // return length of replacing table
  2812. int AffixMgr::get_numrep() const
  2813. {
  2814. return numrep;
  2815. }
  2816. // return replacing table
  2817. struct replentry * AffixMgr::get_reptable() const
  2818. {
  2819. if (! reptable ) return NULL;
  2820. return reptable;
  2821. }
  2822. // return iconv table
  2823. RepList * AffixMgr::get_iconvtable() const
  2824. {
  2825. if (! iconvtable ) return NULL;
  2826. return iconvtable;
  2827. }
  2828. // return oconv table
  2829. RepList * AffixMgr::get_oconvtable() const
  2830. {
  2831. if (! oconvtable ) return NULL;
  2832. return oconvtable;
  2833. }
  2834. // return replacing table
  2835. struct phonetable * AffixMgr::get_phonetable() const
  2836. {
  2837. if (! phone ) return NULL;
  2838. return phone;
  2839. }
  2840. // return length of character map table
  2841. int AffixMgr::get_nummap() const
  2842. {
  2843. return nummap;
  2844. }
  2845. // return character map table
  2846. struct mapentry * AffixMgr::get_maptable() const
  2847. {
  2848. if (! maptable ) return NULL;
  2849. return maptable;
  2850. }
  2851. // return length of word break table
  2852. int AffixMgr::get_numbreak() const
  2853. {
  2854. return numbreak;
  2855. }
  2856. // return character map table
  2857. char ** AffixMgr::get_breaktable() const
  2858. {
  2859. if (! breaktable ) return NULL;
  2860. return breaktable;
  2861. }
  2862. // return text encoding of dictionary
  2863. char * AffixMgr::get_encoding()
  2864. {
  2865. if (! encoding ) encoding = mystrdup(SPELL_ENCODING);
  2866. return mystrdup(encoding);
  2867. }
  2868. // return text encoding of dictionary
  2869. int AffixMgr::get_langnum() const
  2870. {
  2871. return langnum;
  2872. }
  2873. // return double prefix option
  2874. int AffixMgr::get_complexprefixes() const
  2875. {
  2876. return complexprefixes;
  2877. }
  2878. // return FULLSTRIP option
  2879. int AffixMgr::get_fullstrip() const
  2880. {
  2881. return fullstrip;
  2882. }
  2883. FLAG AffixMgr::get_keepcase() const
  2884. {
  2885. return keepcase;
  2886. }
  2887. FLAG AffixMgr::get_forceucase() const
  2888. {
  2889. return forceucase;
  2890. }
  2891. FLAG AffixMgr::get_warn() const
  2892. {
  2893. return warn;
  2894. }
  2895. int AffixMgr::get_forbidwarn() const
  2896. {
  2897. return forbidwarn;
  2898. }
  2899. int AffixMgr::get_checksharps() const
  2900. {
  2901. return checksharps;
  2902. }
  2903. char * AffixMgr::encode_flag(unsigned short aflag) const
  2904. {
  2905. return pHMgr->encode_flag(aflag);
  2906. }
  2907. // return the preferred ignore string for suggestions
  2908. char * AffixMgr::get_ignore() const
  2909. {
  2910. if (!ignorechars) return NULL;
  2911. return ignorechars;
  2912. }
  2913. // return the preferred ignore string for suggestions
  2914. unsigned short * AffixMgr::get_ignore_utf16(int * len) const
  2915. {
  2916. *len = ignorechars_utf16_len;
  2917. return ignorechars_utf16;
  2918. }
  2919. // return the keyboard string for suggestions
  2920. char * AffixMgr::get_key_string()
  2921. {
  2922. if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING);
  2923. return mystrdup(keystring);
  2924. }
  2925. // return the preferred try string for suggestions
  2926. char * AffixMgr::get_try_string() const
  2927. {
  2928. if (! trystring ) return NULL;
  2929. return mystrdup(trystring);
  2930. }
  2931. // return the preferred try string for suggestions
  2932. const char * AffixMgr::get_wordchars() const
  2933. {
  2934. return wordchars;
  2935. }
  2936. unsigned short * AffixMgr::get_wordchars_utf16(int * len) const
  2937. {
  2938. *len = wordchars_utf16_len;
  2939. return wordchars_utf16;
  2940. }
  2941. // is there compounding?
  2942. int AffixMgr::get_compound() const
  2943. {
  2944. return compoundflag || compoundbegin || numdefcpd;
  2945. }
  2946. // return the compound words control flag
  2947. FLAG AffixMgr::get_compoundflag() const
  2948. {
  2949. return compoundflag;
  2950. }
  2951. // return the forbidden words control flag
  2952. FLAG AffixMgr::get_forbiddenword() const
  2953. {
  2954. return forbiddenword;
  2955. }
  2956. // return the forbidden words control flag
  2957. FLAG AffixMgr::get_nosuggest() const
  2958. {
  2959. return nosuggest;
  2960. }
  2961. // return the forbidden words control flag
  2962. FLAG AffixMgr::get_nongramsuggest() const
  2963. {
  2964. return nongramsuggest;
  2965. }
  2966. // return the forbidden words flag modify flag
  2967. FLAG AffixMgr::get_needaffix() const
  2968. {
  2969. return needaffix;
  2970. }
  2971. // return the onlyincompound flag
  2972. FLAG AffixMgr::get_onlyincompound() const
  2973. {
  2974. return onlyincompound;
  2975. }
  2976. // return the compound word signal flag
  2977. FLAG AffixMgr::get_compoundroot() const
  2978. {
  2979. return compoundroot;
  2980. }
  2981. // return the compound begin signal flag
  2982. FLAG AffixMgr::get_compoundbegin() const
  2983. {
  2984. return compoundbegin;
  2985. }
  2986. // return the value of checknum
  2987. int AffixMgr::get_checknum() const
  2988. {
  2989. return checknum;
  2990. }
  2991. // return the value of prefix
  2992. const char * AffixMgr::get_prefix() const
  2993. {
  2994. if (pfx) return pfx->getKey();
  2995. return NULL;
  2996. }
  2997. // return the value of suffix
  2998. const char * AffixMgr::get_suffix() const
  2999. {
  3000. return sfxappnd;
  3001. }
  3002. // return the value of suffix
  3003. const char * AffixMgr::get_version() const
  3004. {
  3005. return version;
  3006. }
  3007. // return lemma_present flag
  3008. FLAG AffixMgr::get_lemma_present() const
  3009. {
  3010. return lemma_present;
  3011. }
  3012. // utility method to look up root words in hash table
  3013. struct hentry * AffixMgr::lookup(const char * word)
  3014. {
  3015. int i;
  3016. struct hentry * he = NULL;
  3017. for (i = 0; i < *maxdic && !he; i++) {
  3018. he = (alldic[i])->lookup(word);
  3019. }
  3020. return he;
  3021. }
  3022. // return the value of suffix
  3023. int AffixMgr::have_contclass() const
  3024. {
  3025. return havecontclass;
  3026. }
  3027. // return utf8
  3028. int AffixMgr::get_utf8() const
  3029. {
  3030. return utf8;
  3031. }
  3032. int AffixMgr::get_maxngramsugs(void) const
  3033. {
  3034. return maxngramsugs;
  3035. }
  3036. int AffixMgr::get_maxcpdsugs(void) const
  3037. {
  3038. return maxcpdsugs;
  3039. }
  3040. int AffixMgr::get_maxdiff(void) const
  3041. {
  3042. return maxdiff;
  3043. }
  3044. int AffixMgr::get_onlymaxdiff(void) const
  3045. {
  3046. return onlymaxdiff;
  3047. }
  3048. // return nosplitsugs
  3049. int AffixMgr::get_nosplitsugs(void) const
  3050. {
  3051. return nosplitsugs;
  3052. }
  3053. // return sugswithdots
  3054. int AffixMgr::get_sugswithdots(void) const
  3055. {
  3056. return sugswithdots;
  3057. }
  3058. /* parse flag */
  3059. int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) {
  3060. char * s = NULL;
  3061. if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) {
  3062. HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
  3063. return 1;
  3064. }
  3065. if (parse_string(line, &s, af->getlinenum())) return 1;
  3066. *out = pHMgr->decode_flag(s);
  3067. free(s);
  3068. return 0;
  3069. }
  3070. /* parse num */
  3071. int AffixMgr::parse_num(char * line, int * out, FileMgr * af) {
  3072. char * s = NULL;
  3073. if (*out != -1) {
  3074. HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
  3075. return 1;
  3076. }
  3077. if (parse_string(line, &s, af->getlinenum())) return 1;
  3078. *out = atoi(s);
  3079. free(s);
  3080. return 0;
  3081. }
  3082. /* parse in the max syllablecount of compound words and */
  3083. int AffixMgr::parse_cpdsyllable(char * line, FileMgr * af)
  3084. {
  3085. char * tp = line;
  3086. char * piece;
  3087. int i = 0;
  3088. int np = 0;
  3089. w_char w[MAXWORDLEN];
  3090. piece = mystrsep(&tp, 0);
  3091. while (piece) {
  3092. if (*piece != '\0') {
  3093. switch(i) {
  3094. case 0: { np++; break; }
  3095. case 1: { cpdmaxsyllable = atoi(piece); np++; break; }
  3096. case 2: {
  3097. if (!utf8) {
  3098. cpdvowels = mystrdup(piece);
  3099. } else {
  3100. int n = u8_u16(w, MAXWORDLEN, piece);
  3101. if (n > 0) {
  3102. flag_qsort((unsigned short *) w, 0, n);
  3103. cpdvowels_utf16 = (w_char *) malloc(n * sizeof(w_char));
  3104. if (!cpdvowels_utf16) return 1;
  3105. memcpy(cpdvowels_utf16, w, n * sizeof(w_char));
  3106. }
  3107. cpdvowels_utf16_len = n;
  3108. }
  3109. np++;
  3110. break;
  3111. }
  3112. default: break;
  3113. }
  3114. i++;
  3115. }
  3116. piece = mystrsep(&tp, 0);
  3117. }
  3118. if (np < 2) {
  3119. HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum());
  3120. return 1;
  3121. }
  3122. if (np == 2) cpdvowels = mystrdup("aeiouAEIOU");
  3123. return 0;
  3124. }
  3125. /* parse in the typical fault correcting table */
  3126. int AffixMgr::parse_reptable(char * line, FileMgr * af)
  3127. {
  3128. if (numrep != 0) {
  3129. HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
  3130. return 1;
  3131. }
  3132. char * tp = line;
  3133. char * piece;
  3134. int i = 0;
  3135. int np = 0;
  3136. piece = mystrsep(&tp, 0);
  3137. while (piece) {
  3138. if (*piece != '\0') {
  3139. switch(i) {
  3140. case 0: { np++; break; }
  3141. case 1: {
  3142. numrep = atoi(piece);
  3143. if (numrep < 1) {
  3144. HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
  3145. return 1;
  3146. }
  3147. reptable = (replentry *) malloc(numrep * sizeof(struct replentry));
  3148. if (!reptable) return 1;
  3149. np++;
  3150. break;
  3151. }
  3152. default: break;
  3153. }
  3154. i++;
  3155. }
  3156. piece = mystrsep(&tp, 0);
  3157. }
  3158. if (np != 2) {
  3159. HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
  3160. return 1;
  3161. }
  3162. /* now parse the numrep lines to read in the remainder of the table */
  3163. char * nl;
  3164. for (int j=0; j < numrep; j++) {
  3165. if (!(nl = af->getline())) return 1;
  3166. mychomp(nl);
  3167. tp = nl;
  3168. i = 0;
  3169. reptable[j].pattern = NULL;
  3170. reptable[j].pattern2 = NULL;
  3171. piece = mystrsep(&tp, 0);
  3172. while (piece) {
  3173. if (*piece != '\0') {
  3174. switch(i) {
  3175. case 0: {
  3176. if (strncmp(piece,"REP",3) != 0) {
  3177. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3178. numrep = 0;
  3179. return 1;
  3180. }
  3181. break;
  3182. }
  3183. case 1: {
  3184. if (*piece == '^') reptable[j].start = true; else reptable[j].start = false;
  3185. reptable[j].pattern = mystrrep(mystrdup(piece + int(reptable[j].start)),"_"," ");
  3186. int lr = strlen(reptable[j].pattern) - 1;
  3187. if (reptable[j].pattern[lr] == '$') {
  3188. reptable[j].end = true;
  3189. reptable[j].pattern[lr] = '\0';
  3190. } else reptable[j].end = false;
  3191. break;
  3192. }
  3193. case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; }
  3194. default: break;
  3195. }
  3196. i++;
  3197. }
  3198. piece = mystrsep(&tp, 0);
  3199. }
  3200. if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) {
  3201. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3202. numrep = 0;
  3203. return 1;
  3204. }
  3205. }
  3206. return 0;
  3207. }
  3208. /* parse in the typical fault correcting table */
  3209. int AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword)
  3210. {
  3211. if (*rl) {
  3212. HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
  3213. return 1;
  3214. }
  3215. char * tp = line;
  3216. char * piece;
  3217. int i = 0;
  3218. int np = 0;
  3219. int numrl = 0;
  3220. piece = mystrsep(&tp, 0);
  3221. while (piece) {
  3222. if (*piece != '\0') {
  3223. switch(i) {
  3224. case 0: { np++; break; }
  3225. case 1: {
  3226. numrl = atoi(piece);
  3227. if (numrl < 1) {
  3228. HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
  3229. return 1;
  3230. }
  3231. *rl = new RepList(numrl);
  3232. if (!*rl) return 1;
  3233. np++;
  3234. break;
  3235. }
  3236. default: break;
  3237. }
  3238. i++;
  3239. }
  3240. piece = mystrsep(&tp, 0);
  3241. }
  3242. if (np != 2) {
  3243. HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
  3244. return 1;
  3245. }
  3246. /* now parse the num lines to read in the remainder of the table */
  3247. char * nl;
  3248. for (int j=0; j < numrl; j++) {
  3249. if (!(nl = af->getline())) return 1;
  3250. mychomp(nl);
  3251. tp = nl;
  3252. i = 0;
  3253. char * pattern = NULL;
  3254. char * pattern2 = NULL;
  3255. piece = mystrsep(&tp, 0);
  3256. while (piece) {
  3257. if (*piece != '\0') {
  3258. switch(i) {
  3259. case 0: {
  3260. if (strncmp(piece, keyword, strlen(keyword)) != 0) {
  3261. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3262. delete *rl;
  3263. *rl = NULL;
  3264. return 1;
  3265. }
  3266. break;
  3267. }
  3268. case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; }
  3269. case 2: {
  3270. pattern2 = mystrrep(mystrdup(piece),"_"," ");
  3271. break;
  3272. }
  3273. default: break;
  3274. }
  3275. i++;
  3276. }
  3277. piece = mystrsep(&tp, 0);
  3278. }
  3279. if (!pattern || !pattern2) {
  3280. if (pattern)
  3281. free(pattern);
  3282. if (pattern2)
  3283. free(pattern2);
  3284. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3285. return 1;
  3286. }
  3287. (*rl)->add(pattern, pattern2);
  3288. }
  3289. return 0;
  3290. }
  3291. /* parse in the typical fault correcting table */
  3292. int AffixMgr::parse_phonetable(char * line, FileMgr * af)
  3293. {
  3294. if (phone) {
  3295. HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
  3296. return 1;
  3297. }
  3298. char * tp = line;
  3299. char * piece;
  3300. int i = 0;
  3301. int np = 0;
  3302. piece = mystrsep(&tp, 0);
  3303. while (piece) {
  3304. if (*piece != '\0') {
  3305. switch(i) {
  3306. case 0: { np++; break; }
  3307. case 1: {
  3308. phone = (phonetable *) malloc(sizeof(struct phonetable));
  3309. if (!phone) return 1;
  3310. phone->num = atoi(piece);
  3311. phone->rules = NULL;
  3312. phone->utf8 = (char) utf8;
  3313. if (phone->num < 1) {
  3314. HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
  3315. return 1;
  3316. }
  3317. phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *));
  3318. if (!phone->rules) {
  3319. free(phone);
  3320. phone = NULL;
  3321. return 1;
  3322. }
  3323. np++;
  3324. break;
  3325. }
  3326. default: break;
  3327. }
  3328. i++;
  3329. }
  3330. piece = mystrsep(&tp, 0);
  3331. }
  3332. if (np != 2) {
  3333. HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
  3334. return 1;
  3335. }
  3336. /* now parse the phone->num lines to read in the remainder of the table */
  3337. char * nl;
  3338. for (int j=0; j < phone->num; j++) {
  3339. if (!(nl = af->getline())) return 1;
  3340. mychomp(nl);
  3341. tp = nl;
  3342. i = 0;
  3343. phone->rules[j * 2] = NULL;
  3344. phone->rules[j * 2 + 1] = NULL;
  3345. piece = mystrsep(&tp, 0);
  3346. while (piece) {
  3347. if (*piece != '\0') {
  3348. switch(i) {
  3349. case 0: {
  3350. if (strncmp(piece,"PHONE",5) != 0) {
  3351. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3352. phone->num = 0;
  3353. return 1;
  3354. }
  3355. break;
  3356. }
  3357. case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; }
  3358. case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; }
  3359. default: break;
  3360. }
  3361. i++;
  3362. }
  3363. piece = mystrsep(&tp, 0);
  3364. }
  3365. if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) {
  3366. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3367. phone->num = 0;
  3368. return 1;
  3369. }
  3370. }
  3371. phone->rules[phone->num * 2] = mystrdup("");
  3372. phone->rules[phone->num * 2 + 1] = mystrdup("");
  3373. init_phonet_hash(*phone);
  3374. return 0;
  3375. }
  3376. /* parse in the checkcompoundpattern table */
  3377. int AffixMgr::parse_checkcpdtable(char * line, FileMgr * af)
  3378. {
  3379. if (numcheckcpd != 0) {
  3380. HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
  3381. return 1;
  3382. }
  3383. char * tp = line;
  3384. char * piece;
  3385. int i = 0;
  3386. int np = 0;
  3387. piece = mystrsep(&tp, 0);
  3388. while (piece) {
  3389. if (*piece != '\0') {
  3390. switch(i) {
  3391. case 0: { np++; break; }
  3392. case 1: {
  3393. numcheckcpd = atoi(piece);
  3394. if (numcheckcpd < 1) {
  3395. HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
  3396. return 1;
  3397. }
  3398. checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry));
  3399. if (!checkcpdtable) return 1;
  3400. np++;
  3401. break;
  3402. }
  3403. default: break;
  3404. }
  3405. i++;
  3406. }
  3407. piece = mystrsep(&tp, 0);
  3408. }
  3409. if (np != 2) {
  3410. HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
  3411. return 1;
  3412. }
  3413. /* now parse the numcheckcpd lines to read in the remainder of the table */
  3414. char * nl;
  3415. for (int j=0; j < numcheckcpd; j++) {
  3416. if (!(nl = af->getline())) return 1;
  3417. mychomp(nl);
  3418. tp = nl;
  3419. i = 0;
  3420. checkcpdtable[j].pattern = NULL;
  3421. checkcpdtable[j].pattern2 = NULL;
  3422. checkcpdtable[j].pattern3 = NULL;
  3423. checkcpdtable[j].cond = FLAG_NULL;
  3424. checkcpdtable[j].cond2 = FLAG_NULL;
  3425. piece = mystrsep(&tp, 0);
  3426. while (piece) {
  3427. if (*piece != '\0') {
  3428. switch(i) {
  3429. case 0: {
  3430. if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) {
  3431. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3432. numcheckcpd = 0;
  3433. return 1;
  3434. }
  3435. break;
  3436. }
  3437. case 1: {
  3438. checkcpdtable[j].pattern = mystrdup(piece);
  3439. char * p = strchr(checkcpdtable[j].pattern, '/');
  3440. if (p) {
  3441. *p = '\0';
  3442. checkcpdtable[j].cond = pHMgr->decode_flag(p + 1);
  3443. }
  3444. break; }
  3445. case 2: {
  3446. checkcpdtable[j].pattern2 = mystrdup(piece);
  3447. char * p = strchr(checkcpdtable[j].pattern2, '/');
  3448. if (p) {
  3449. *p = '\0';
  3450. checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1);
  3451. }
  3452. break;
  3453. }
  3454. case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; }
  3455. default: break;
  3456. }
  3457. i++;
  3458. }
  3459. piece = mystrsep(&tp, 0);
  3460. }
  3461. if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) {
  3462. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3463. numcheckcpd = 0;
  3464. return 1;
  3465. }
  3466. }
  3467. return 0;
  3468. }
  3469. /* parse in the compound rule table */
  3470. int AffixMgr::parse_defcpdtable(char * line, FileMgr * af)
  3471. {
  3472. if (numdefcpd != 0) {
  3473. HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
  3474. return 1;
  3475. }
  3476. char * tp = line;
  3477. char * piece;
  3478. int i = 0;
  3479. int np = 0;
  3480. piece = mystrsep(&tp, 0);
  3481. while (piece) {
  3482. if (*piece != '\0') {
  3483. switch(i) {
  3484. case 0: { np++; break; }
  3485. case 1: {
  3486. numdefcpd = atoi(piece);
  3487. if (numdefcpd < 1) {
  3488. HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
  3489. return 1;
  3490. }
  3491. defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry));
  3492. if (!defcpdtable) return 1;
  3493. np++;
  3494. break;
  3495. }
  3496. default: break;
  3497. }
  3498. i++;
  3499. }
  3500. piece = mystrsep(&tp, 0);
  3501. }
  3502. if (np != 2) {
  3503. HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
  3504. return 1;
  3505. }
  3506. /* now parse the numdefcpd lines to read in the remainder of the table */
  3507. char * nl;
  3508. for (int j=0; j < numdefcpd; j++) {
  3509. if (!(nl = af->getline())) return 1;
  3510. mychomp(nl);
  3511. tp = nl;
  3512. i = 0;
  3513. defcpdtable[j].def = NULL;
  3514. piece = mystrsep(&tp, 0);
  3515. while (piece) {
  3516. if (*piece != '\0') {
  3517. switch(i) {
  3518. case 0: {
  3519. if (strncmp(piece, "COMPOUNDRULE", 12) != 0) {
  3520. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3521. numdefcpd = 0;
  3522. return 1;
  3523. }
  3524. break;
  3525. }
  3526. case 1: { // handle parenthesized flags
  3527. if (strchr(piece, '(')) {
  3528. defcpdtable[j].def = (FLAG *) malloc(strlen(piece) * sizeof(FLAG));
  3529. defcpdtable[j].len = 0;
  3530. int end = 0;
  3531. FLAG * conv;
  3532. while (!end) {
  3533. char * par = piece + 1;
  3534. while (*par != '(' && *par != ')' && *par != '\0') par++;
  3535. if (*par == '\0') end = 1; else *par = '\0';
  3536. if (*piece == '(') piece++;
  3537. if (*piece == '*' || *piece == '?') {
  3538. defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece;
  3539. } else if (*piece != '\0') {
  3540. int l = pHMgr->decode_flags(&conv, piece, af);
  3541. for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k];
  3542. free(conv);
  3543. }
  3544. piece = par + 1;
  3545. }
  3546. } else {
  3547. defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af);
  3548. }
  3549. break;
  3550. }
  3551. default: break;
  3552. }
  3553. i++;
  3554. }
  3555. piece = mystrsep(&tp, 0);
  3556. }
  3557. if (!defcpdtable[j].len) {
  3558. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3559. numdefcpd = 0;
  3560. return 1;
  3561. }
  3562. }
  3563. return 0;
  3564. }
  3565. /* parse in the character map table */
  3566. int AffixMgr::parse_maptable(char * line, FileMgr * af)
  3567. {
  3568. if (nummap != 0) {
  3569. HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
  3570. return 1;
  3571. }
  3572. char * tp = line;
  3573. char * piece;
  3574. int i = 0;
  3575. int np = 0;
  3576. piece = mystrsep(&tp, 0);
  3577. while (piece) {
  3578. if (*piece != '\0') {
  3579. switch(i) {
  3580. case 0: { np++; break; }
  3581. case 1: {
  3582. nummap = atoi(piece);
  3583. if (nummap < 1) {
  3584. HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
  3585. return 1;
  3586. }
  3587. maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry));
  3588. if (!maptable) return 1;
  3589. np++;
  3590. break;
  3591. }
  3592. default: break;
  3593. }
  3594. i++;
  3595. }
  3596. piece = mystrsep(&tp, 0);
  3597. }
  3598. if (np != 2) {
  3599. HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
  3600. return 1;
  3601. }
  3602. /* now parse the nummap lines to read in the remainder of the table */
  3603. char * nl;
  3604. for (int j=0; j < nummap; j++) {
  3605. if (!(nl = af->getline())) return 1;
  3606. mychomp(nl);
  3607. tp = nl;
  3608. i = 0;
  3609. maptable[j].set = NULL;
  3610. maptable[j].len = 0;
  3611. piece = mystrsep(&tp, 0);
  3612. while (piece) {
  3613. if (*piece != '\0') {
  3614. switch(i) {
  3615. case 0: {
  3616. if (strncmp(piece,"MAP",3) != 0) {
  3617. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3618. nummap = 0;
  3619. return 1;
  3620. }
  3621. break;
  3622. }
  3623. case 1: {
  3624. int setn = 0;
  3625. maptable[j].len = strlen(piece);
  3626. maptable[j].set = (char **) malloc(maptable[j].len * sizeof(char*));
  3627. if (!maptable[j].set) return 1;
  3628. for (int k = 0; k < maptable[j].len; k++) {
  3629. int chl = 1;
  3630. int chb = k;
  3631. if (piece[k] == '(') {
  3632. char * parpos = strchr(piece + k, ')');
  3633. if (parpos != NULL) {
  3634. chb = k + 1;
  3635. chl = (int)(parpos - piece) - k - 1;
  3636. k = k + chl + 1;
  3637. }
  3638. } else {
  3639. if (utf8 && (piece[k] & 0xc0) == 0xc0) {
  3640. for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++);
  3641. chl = k - chb;
  3642. k--;
  3643. }
  3644. }
  3645. maptable[j].set[setn] = (char *) malloc(chl + 1);
  3646. if (!maptable[j].set[setn]) return 1;
  3647. strncpy(maptable[j].set[setn], piece + chb, chl);
  3648. maptable[j].set[setn][chl] = '\0';
  3649. setn++;
  3650. }
  3651. maptable[j].len = setn;
  3652. break; }
  3653. default: break;
  3654. }
  3655. i++;
  3656. }
  3657. piece = mystrsep(&tp, 0);
  3658. }
  3659. if (!maptable[j].set || !maptable[j].len) {
  3660. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3661. nummap = 0;
  3662. return 1;
  3663. }
  3664. }
  3665. return 0;
  3666. }
  3667. /* parse in the word breakpoint table */
  3668. int AffixMgr::parse_breaktable(char * line, FileMgr * af)
  3669. {
  3670. if (numbreak > -1) {
  3671. HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
  3672. return 1;
  3673. }
  3674. char * tp = line;
  3675. char * piece;
  3676. int i = 0;
  3677. int np = 0;
  3678. piece = mystrsep(&tp, 0);
  3679. while (piece) {
  3680. if (*piece != '\0') {
  3681. switch(i) {
  3682. case 0: { np++; break; }
  3683. case 1: {
  3684. numbreak = atoi(piece);
  3685. if (numbreak < 0) {
  3686. HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
  3687. return 1;
  3688. }
  3689. if (numbreak == 0) return 0;
  3690. breaktable = (char **) malloc(numbreak * sizeof(char *));
  3691. if (!breaktable) return 1;
  3692. np++;
  3693. break;
  3694. }
  3695. default: break;
  3696. }
  3697. i++;
  3698. }
  3699. piece = mystrsep(&tp, 0);
  3700. }
  3701. if (np != 2) {
  3702. HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
  3703. return 1;
  3704. }
  3705. /* now parse the numbreak lines to read in the remainder of the table */
  3706. char * nl;
  3707. for (int j=0; j < numbreak; j++) {
  3708. if (!(nl = af->getline())) return 1;
  3709. mychomp(nl);
  3710. tp = nl;
  3711. i = 0;
  3712. piece = mystrsep(&tp, 0);
  3713. while (piece) {
  3714. if (*piece != '\0') {
  3715. switch(i) {
  3716. case 0: {
  3717. if (strncmp(piece,"BREAK",5) != 0) {
  3718. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3719. numbreak = 0;
  3720. return 1;
  3721. }
  3722. break;
  3723. }
  3724. case 1: {
  3725. breaktable[j] = mystrdup(piece);
  3726. break;
  3727. }
  3728. default: break;
  3729. }
  3730. i++;
  3731. }
  3732. piece = mystrsep(&tp, 0);
  3733. }
  3734. if (!breaktable) {
  3735. HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
  3736. numbreak = 0;
  3737. return 1;
  3738. }
  3739. }
  3740. return 0;
  3741. }
  3742. void AffixMgr::reverse_condition(char * piece) {
  3743. int neg = 0;
  3744. for (char * k = piece + strlen(piece) - 1; k >= piece; k--) {
  3745. switch(*k) {
  3746. case '[': {
  3747. if (neg) *(k+1) = '['; else *k = ']';
  3748. break;
  3749. }
  3750. case ']': {
  3751. *k = '[';
  3752. if (neg) *(k+1) = '^';
  3753. neg = 0;
  3754. break;
  3755. }
  3756. case '^': {
  3757. if (*(k+1) == ']') neg = 1; else *(k+1) = *k;
  3758. break;
  3759. }
  3760. default: {
  3761. if (neg) *(k+1) = *k;
  3762. }
  3763. }
  3764. }
  3765. }
  3766. int AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags)
  3767. {
  3768. int numents = 0; // number of affentry structures to parse
  3769. unsigned short aflag = 0; // affix char identifier
  3770. char ff=0;
  3771. std::vector<affentry> affentries;
  3772. char * tp = line;
  3773. char * nl = line;
  3774. char * piece;
  3775. int i = 0;
  3776. // checking lines with bad syntax
  3777. #ifdef DEBUG
  3778. int basefieldnum = 0;
  3779. #endif
  3780. // split affix header line into pieces
  3781. int np = 0;
  3782. piece = mystrsep(&tp, 0);
  3783. while (piece) {
  3784. if (*piece != '\0') {
  3785. switch(i) {
  3786. // piece 1 - is type of affix
  3787. case 0: { np++; break; }
  3788. // piece 2 - is affix char
  3789. case 1: {
  3790. np++;
  3791. aflag = pHMgr->decode_flag(piece);
  3792. if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
  3793. ((at == 'P') && (dupflags[aflag] & dupPFX))) {
  3794. HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n",
  3795. af->getlinenum());
  3796. // return 1; XXX permissive mode for bad dictionaries
  3797. }
  3798. dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX);
  3799. break;
  3800. }
  3801. // piece 3 - is cross product indicator
  3802. case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; }
  3803. // piece 4 - is number of affentries
  3804. case 3: {
  3805. np++;
  3806. numents = atoi(piece);
  3807. if (numents == 0) {
  3808. char * err = pHMgr->encode_flag(aflag);
  3809. if (err) {
  3810. HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
  3811. af->getlinenum());
  3812. free(err);
  3813. }
  3814. return 1;
  3815. }
  3816. affentries.resize(numents);
  3817. affentries[0].opts = ff;
  3818. if (utf8) affentries[0].opts += aeUTF8;
  3819. if (pHMgr->is_aliasf()) affentries[0].opts += aeALIASF;
  3820. if (pHMgr->is_aliasm()) affentries[0].opts += aeALIASM;
  3821. affentries[0].aflag = aflag;
  3822. }
  3823. default: break;
  3824. }
  3825. i++;
  3826. }
  3827. piece = mystrsep(&tp, 0);
  3828. }
  3829. // check to make sure we parsed enough pieces
  3830. if (np != 4) {
  3831. char * err = pHMgr->encode_flag(aflag);
  3832. if (err) {
  3833. HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
  3834. free(err);
  3835. }
  3836. return 1;
  3837. }
  3838. // now parse numents affentries for this affix
  3839. std::vector<affentry>::iterator start = affentries.begin();
  3840. std::vector<affentry>::iterator end = affentries.end();
  3841. for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
  3842. if (!(nl = af->getline())) return 1;
  3843. mychomp(nl);
  3844. tp = nl;
  3845. i = 0;
  3846. np = 0;
  3847. // split line into pieces
  3848. piece = mystrsep(&tp, 0);
  3849. while (piece) {
  3850. if (*piece != '\0') {
  3851. switch(i) {
  3852. // piece 1 - is type
  3853. case 0: {
  3854. np++;
  3855. if (entry != start) entry->opts = start->opts &
  3856. (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM);
  3857. break;
  3858. }
  3859. // piece 2 - is affix char
  3860. case 1: {
  3861. np++;
  3862. if (pHMgr->decode_flag(piece) != aflag) {
  3863. char * err = pHMgr->encode_flag(aflag);
  3864. if (err) {
  3865. HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
  3866. af->getlinenum(), err);
  3867. free(err);
  3868. }
  3869. return 1;
  3870. }
  3871. if (entry != start) entry->aflag = start->aflag;
  3872. break;
  3873. }
  3874. // piece 3 - is string to strip or 0 for null
  3875. case 2: {
  3876. np++;
  3877. if (complexprefixes) {
  3878. if (utf8) reverseword_utf(piece); else reverseword(piece);
  3879. }
  3880. entry->strip = mystrdup(piece);
  3881. entry->stripl = (unsigned char) strlen(entry->strip);
  3882. if (strcmp(entry->strip,"0") == 0) {
  3883. free(entry->strip);
  3884. entry->strip=mystrdup("");
  3885. entry->stripl = 0;
  3886. }
  3887. break;
  3888. }
  3889. // piece 4 - is affix string or 0 for null
  3890. case 3: {
  3891. char * dash;
  3892. entry->morphcode = NULL;
  3893. entry->contclass = NULL;
  3894. entry->contclasslen = 0;
  3895. np++;
  3896. dash = strchr(piece, '/');
  3897. if (dash) {
  3898. *dash = '\0';
  3899. if (ignorechars) {
  3900. if (utf8) {
  3901. remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
  3902. } else {
  3903. remove_ignored_chars(piece,ignorechars);
  3904. }
  3905. }
  3906. if (complexprefixes) {
  3907. if (utf8) reverseword_utf(piece); else reverseword(piece);
  3908. }
  3909. entry->appnd = mystrdup(piece);
  3910. if (pHMgr->is_aliasf()) {
  3911. int index = atoi(dash + 1);
  3912. entry->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(entry->contclass), af);
  3913. if (!entry->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1);
  3914. } else {
  3915. entry->contclasslen = (unsigned short) pHMgr->decode_flags(&(entry->contclass), dash + 1, af);
  3916. flag_qsort(entry->contclass, 0, entry->contclasslen);
  3917. }
  3918. *dash = '/';
  3919. havecontclass = 1;
  3920. for (unsigned short _i = 0; _i < entry->contclasslen; _i++) {
  3921. contclasses[(entry->contclass)[_i]] = 1;
  3922. }
  3923. } else {
  3924. if (ignorechars) {
  3925. if (utf8) {
  3926. remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
  3927. } else {
  3928. remove_ignored_chars(piece,ignorechars);
  3929. }
  3930. }
  3931. if (complexprefixes) {
  3932. if (utf8) reverseword_utf(piece); else reverseword(piece);
  3933. }
  3934. entry->appnd = mystrdup(piece);
  3935. }
  3936. entry->appndl = (unsigned char) strlen(entry->appnd);
  3937. if (strcmp(entry->appnd,"0") == 0) {
  3938. free(entry->appnd);
  3939. entry->appnd=mystrdup("");
  3940. entry->appndl = 0;
  3941. }
  3942. break;
  3943. }
  3944. // piece 5 - is the conditions descriptions
  3945. case 4: {
  3946. np++;
  3947. if (complexprefixes) {
  3948. if (utf8) reverseword_utf(piece); else reverseword(piece);
  3949. reverse_condition(piece);
  3950. }
  3951. if (entry->stripl && (strcmp(piece, ".") != 0) &&
  3952. redundant_condition(at, entry->strip, entry->stripl, piece, af->getlinenum()))
  3953. strcpy(piece, ".");
  3954. if (at == 'S') {
  3955. reverseword(piece);
  3956. reverse_condition(piece);
  3957. }
  3958. if (encodeit(*entry, piece)) return 1;
  3959. break;
  3960. }
  3961. case 5: {
  3962. np++;
  3963. if (pHMgr->is_aliasm()) {
  3964. int index = atoi(piece);
  3965. entry->morphcode = pHMgr->get_aliasm(index);
  3966. } else {
  3967. if (complexprefixes) { // XXX - fix me for morph. gen.
  3968. if (utf8) reverseword_utf(piece); else reverseword(piece);
  3969. }
  3970. // add the remaining of the line
  3971. if (*tp) {
  3972. *(tp - 1) = ' ';
  3973. tp = tp + strlen(tp);
  3974. }
  3975. entry->morphcode = mystrdup(piece);
  3976. if (!entry->morphcode) return 1;
  3977. }
  3978. break;
  3979. }
  3980. default: break;
  3981. }
  3982. i++;
  3983. }
  3984. piece = mystrsep(&tp, 0);
  3985. }
  3986. // check to make sure we parsed enough pieces
  3987. if (np < 4) {
  3988. char * err = pHMgr->encode_flag(aflag);
  3989. if (err) {
  3990. HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
  3991. af->getlinenum(), err);
  3992. free(err);
  3993. }
  3994. return 1;
  3995. }
  3996. #ifdef DEBUG
  3997. // detect unnecessary fields, excepting comments
  3998. if (basefieldnum) {
  3999. int fieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
  4000. if (fieldnum != basefieldnum)
  4001. HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum());
  4002. } else {
  4003. basefieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
  4004. }
  4005. #endif
  4006. }
  4007. // now create SfxEntry or PfxEntry objects and use links to
  4008. // build an ordered (sorted by affix string) list
  4009. for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
  4010. if (at == 'P') {
  4011. PfxEntry * pfxptr = new PfxEntry(this,&(*entry));
  4012. build_pfxtree(pfxptr);
  4013. } else {
  4014. SfxEntry * sfxptr = new SfxEntry(this,&(*entry));
  4015. build_sfxtree(sfxptr);
  4016. }
  4017. }
  4018. return 0;
  4019. }
  4020. int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) {
  4021. int condl = strlen(cond);
  4022. int i;
  4023. int j;
  4024. int neg;
  4025. int in;
  4026. if (ft == 'P') { // prefix
  4027. if (strncmp(strip, cond, condl) == 0) return 1;
  4028. if (utf8) {
  4029. } else {
  4030. for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) {
  4031. if (cond[j] != '[') {
  4032. if (cond[j] != strip[i]) {
  4033. HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
  4034. return 0;
  4035. }
  4036. } else {
  4037. neg = (cond[j+1] == '^') ? 1 : 0;
  4038. in = 0;
  4039. do {
  4040. j++;
  4041. if (strip[i] == cond[j]) in = 1;
  4042. } while ((j < (condl - 1)) && (cond[j] != ']'));
  4043. if (j == (condl - 1) && (cond[j] != ']')) {
  4044. HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum, cond);
  4045. return 0;
  4046. }
  4047. if ((!neg && !in) || (neg && in)) {
  4048. HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
  4049. return 0;
  4050. }
  4051. }
  4052. }
  4053. if (j >= condl) return 1;
  4054. }
  4055. } else { // suffix
  4056. if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1;
  4057. if (utf8) {
  4058. } else {
  4059. for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) {
  4060. if (cond[j] != ']') {
  4061. if (cond[j] != strip[i]) {
  4062. HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
  4063. return 0;
  4064. }
  4065. } else {
  4066. in = 0;
  4067. do {
  4068. j--;
  4069. if (strip[i] == cond[j]) in = 1;
  4070. } while ((j > 0) && (cond[j] != '['));
  4071. if ((j == 0) && (cond[j] != '[')) {
  4072. HUNSPELL_WARNING(stderr, "error: line: %d: missing ] in condition:\n%s\n", linenum, cond);
  4073. return 0;
  4074. }
  4075. neg = (cond[j+1] == '^') ? 1 : 0;
  4076. if ((!neg && !in) || (neg && in)) {
  4077. HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
  4078. return 0;
  4079. }
  4080. }
  4081. }
  4082. if (j < 0) return 1;
  4083. }
  4084. }
  4085. return 0;
  4086. }