PageRenderTime 80ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/Main/Libraries/Hunspell/hunspell/affixmgr.cxx

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