PageRenderTime 74ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

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

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