Guest User

/source/metaphone_ptbr.c

a guest
Mar 16th, 2021
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.48 KB | None | 0 0
  1. /*
  2. Copyright 2008-2017, Carlos Costa Jordao <carlosjordao@gmail.com>.
  3. All rights reserved.
  4.  
  5. Redistribution and use in source and binary forms, with or without modification,
  6. are permitted provided that the following conditions are met:
  7.  
  8. 1. Redistributions of source code must retain the above copyright notice, this
  9. list of conditions and the following disclaimer.
  10. 2. Redistributions in binary form must reproduce the above copyright notice, this
  11. list of conditions and the following disclaimer in the documentation and/or
  12. other materials provided with the distribution.
  13.  
  14.  
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  16. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  17. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  19. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  22. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  24. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  
  26.  
  27. ***********************************************************************/
  28.  
  29.  
  30.  
  31. /* turn off assertions for embedded function */
  32. /* add check for python module */
  33. #ifndef NDEBUG
  34. #define NDEBUG
  35. #endif
  36.  
  37.  
  38. #include "metaphone_ptbr.h"
  39. #include "alloc.h"
  40.  
  41.  
  42. metastring *
  43. NewMetaString(char *init_str)
  44. {
  45. metastring *s;
  46. char empty_string[] = "";
  47.  
  48. META_MALLOC(s, 1, metastring);
  49. assert(s != NULL);
  50.  
  51. if (init_str == NULL)
  52. init_str = empty_string;
  53. s->length = (int)strlen(init_str);
  54. /* preallocate a bit more for potential growth */
  55. s->bufsize = s->length + 7;
  56.  
  57. META_MALLOC(s->str, s->bufsize, char);
  58. assert(s->str != NULL);
  59.  
  60. //strncpy(s->str, init_str, s->length + 1);
  61. memcpy(s->str, init_str, s->length + 1);
  62. s->free_string_on_destroy = 1;
  63.  
  64. return s;
  65. }
  66.  
  67.  
  68. void
  69. DestroyMetaString(metastring * s)
  70. {
  71. if (s == NULL)
  72. return;
  73.  
  74. if (s->free_string_on_destroy && (s->str != NULL))
  75. META_FREE(s->str);
  76.  
  77. META_FREE(s);
  78. }
  79.  
  80.  
  81. void
  82. IncreaseBuffer(metastring * s, int chars_needed)
  83. {
  84. META_REALLOC(s->str, (s->bufsize + chars_needed + 10), char);
  85. assert(s->str != NULL);
  86. s->bufsize = s->bufsize + chars_needed + 10;
  87. }
  88.  
  89. /*
  90. * Hope that fixes toupper() problem with UTF-8 characters
  91. */
  92. inline static wchar_t toUpper(const wchar_t d)
  93. {
  94. wchar_t c;
  95.  
  96. c = (wchar_t)towupper((wint_t)d);
  97.  
  98. switch( c ) {
  99.  
  100. case L'Á':
  101. case L'À':
  102. case L'Ã':
  103. case L'Â':
  104. case L'Ä':
  105. return L'A';
  106.  
  107. case L'É':
  108. case L'È':
  109. case L'Ẽ':
  110. case L'Ê':
  111. case L'Ë':
  112. return L'E';
  113.  
  114. case L'Y':
  115.  
  116. case L'Í':
  117. case L'Ì':
  118. case L'Ĩ':
  119. case L'Î':
  120. case L'Ï':
  121. return L'I';
  122.  
  123. case L'Ó':
  124. case L'Ò':
  125. case L'Õ':
  126. case L'Ô':
  127. case L'Ö':
  128. return L'O';
  129.  
  130. case L'Ú':
  131. case L'Ù':
  132. case L'Ũ':
  133. case L'Û':
  134. case L'Ü':
  135. return L'U';
  136. }
  137.  
  138. return c;
  139. }
  140.  
  141. /*
  142. * Just let the string in uppercase mode.
  143. * and, as this intend to be calling as a preparation measure, let's
  144. * make it clean for some cases that could become a problem.
  145. */
  146. wchar_t*
  147. MakeUpperAndClean(wchar_t* i)
  148. {
  149. wchar_t *s =(wchar_t *)NULL,
  150. *aux =(wchar_t *)NULL;
  151.  
  152. if( !i || *i == L'\0' )
  153. return NULL;
  154.  
  155. /* transforma todos em maiúsculas, com algumas simplificações */
  156. aux = i;
  157. while( *aux ) {
  158. *aux = toUpper(*aux);
  159. aux++;
  160. }
  161.  
  162. /* copia para o novo buffer, eliminando os duplicados. */
  163. META_MALLOC(s,wcslen(i)+1,wchar_t);
  164. if( !s )
  165. return (wchar_t *)NULL;
  166.  
  167. aux = s;
  168. *aux = *i;
  169. aux++;
  170. i++;
  171. for (; *i; i++) {
  172.  
  173. /* clean doubled chars. Not needed in portuguese, except 'R' and 'S' */
  174. if( DOUBLED_CHAR(i) ) {
  175. if( *i != L'R' && *i != L'S' )
  176. continue;
  177.  
  178. /* caso mais de 2 caracteres seguidos, vá até o último. */
  179. while( *i && *i == *(i+1) )
  180. i++;
  181. }
  182. *aux = *i;
  183. aux++;
  184. }
  185.  
  186. *aux = L'\0';
  187. return s;
  188. }
  189.  
  190.  
  191. wchar_t
  192. GetAt(wchar_t* s, int pos)
  193. {
  194. if ((pos < 0) || (pos >= wcslen(s)))
  195. return '\0';
  196.  
  197. return ((wchar_t) *(s + pos));
  198. }
  199. wchar_t
  200. GetSimplifiedAt(wchar_t* s, int pos)
  201. {
  202. if ((pos < 0) || (pos >= wcslen(s)))
  203. return '\0';
  204.  
  205. return ((wchar_t) *(s + pos));
  206. }
  207.  
  208.  
  209.  
  210. void
  211. MetaphAdd(metastring * s, char *new_str)
  212. {
  213. int add_length = 0;
  214.  
  215.  
  216. if (new_str == NULL)
  217. return;
  218.  
  219. add_length = (int)strlen(new_str);
  220. if ((s->length + add_length) > (s->bufsize - 1))
  221. IncreaseBuffer(s, add_length);
  222.  
  223. /* just make the copy. strcat() won't work here */
  224. while (*new_str)
  225. s->str[ s->length++ ] = *new_str++;
  226. }
  227.  
  228. /*
  229. * this function has been included as most of metaphone characters
  230. * has only 1 byte length
  231. */
  232. void
  233. MetaphAddChr(metastring * s, wchar_t nchar)
  234. {
  235. int add_length;
  236. char new_str = (char)nchar;
  237. if (new_str == '\0')
  238. return;
  239.  
  240. add_length = 1;
  241. if ((s->length + add_length) > (s->bufsize - 1))
  242. IncreaseBuffer(s, add_length);
  243.  
  244. s->str[s->length] = new_str;
  245. s->length += add_length;
  246. }
  247. /*
  248. int
  249. isVowel(char chr)
  250. {
  251. switch (chr)
  252. {
  253. // 'Y' é traduzido como 'I' durante a limpeza da string por toUpper()
  254. case L'A': case L'E': case L'I': case L'O': case L'U':
  255. return 1;
  256. }
  257. return 0;
  258. }*/
  259. int
  260. isVowel(wchar_t chr)
  261. {
  262. switch (chr)
  263. {
  264. // 'Y' é traduzido como 'I' durante a limpeza da string por toUpper()
  265. case L'A': case L'E': case L'I': case L'O': case L'U':
  266. return 1;
  267. }
  268. return 0;
  269. }
  270.  
  271.  
  272.  
  273. char *
  274. Metaphone_PTBR(const wchar_t *str, const int max_length)
  275. {
  276. return Metaphone_PTBR_s(str, max_length, '\0');
  277. }
  278.  
  279. //
  280.  
  281. char *
  282. Metaphone_PTBR_s(const wchar_t *str, const int max_length, const wchar_t separator)
  283. {
  284. int length = 0;
  285. wchar_t *original = NULL,
  286. *tmp = NULL;
  287. metastring *primary = NULL;
  288. int current = 0;
  289. int count = 0;
  290. char *code = NULL;
  291. wchar_t current_char = L'\0',
  292. last_char = L'\0',
  293. ahead_char = L'\0';
  294.  
  295. if( !str )
  296. return NULL;
  297.  
  298. /* we need the real length and last prior to padding */
  299. length = (int)wcslen(str);
  300. primary = NewMetaString("");
  301.  
  302. /* let's everything be uppercase. */
  303. tmp = wcsdup(str);
  304. original = MakeUpperAndClean(tmp);
  305. free(tmp);
  306. if( !original )
  307. return NULL;
  308.  
  309.  
  310. /* main loop hard-limited - should be enough for very long names */
  311. while (((primary->length-count) < max_length) && (current < length))
  312. {
  313. current_char = GetSimplifiedAt(original, current);
  314.  
  315. /* skips separator */
  316. if (separator == current_char) {
  317. MetaphAddChr(primary, separator);
  318. count = primary->length;
  319. } else
  320. switch (current_char)
  321. {
  322. case L'A':
  323. case L'E':
  324. case L'I':
  325. case L'O':
  326. case L'U':
  327. /* initials vowels after any space must stay too */
  328. if (WORD_EDGE(last_char))
  329. MetaphAddChr(primary, current_char);
  330. break;
  331.  
  332. case L'L':
  333. ahead_char = GetAt(original, current+1);
  334. /* lha, lho. Adicionado 2009-11-09. Thx Peter Krauss. Ele estava mal-colocado */
  335. if (ahead_char == 'H')
  336. MetaphAddChr(primary, '1');
  337. else
  338. /* como em Louco, aloprado, alado, lampada, etc */
  339. if (isVowel(ahead_char) || WORD_EDGE(last_char))
  340. MetaphAddChr(primary, 'L');
  341. /* atualmente ignora L antes de consoantes */
  342. break;
  343.  
  344. case L'T':
  345. case L'P':
  346. /* those are special cases, from foreign names or
  347. * old portuguese names sintax.
  348. * Besides, should behavior as the others.
  349. */
  350. ahead_char = GetAt(original, current+1);
  351. if (ahead_char == 'H')
  352. {
  353. /* phone, pharmacia, teophilo */
  354. if (current_char == 'P')
  355. MetaphAddChr(primary, 'F');
  356. else
  357. MetaphAddChr(primary, 'T');
  358. current++;
  359. break;
  360. }
  361.  
  362. case L'B':
  363. case L'D':
  364. case L'F':
  365. case L'J':
  366. case L'K':
  367. case L'M':
  368. case L'V':
  369. MetaphAddChr(primary, current_char);
  370. break;
  371.  
  372. /* checar consoantes com som confuso e similares */
  373. case L'G':
  374. ahead_char = GetSimplifiedAt(original, current+1);
  375. switch( ahead_char )
  376. {
  377. case L'H':
  378. /* H sempre complica a vida. Se não for vogal, tratar como 'G',
  379. caso contrário segue o fluxo abaixo. */
  380. if( !isVowel(GetSimplifiedAt(original, current+2)) )
  381. MetaphAddChr(primary,'G');
  382. case L'E':
  383. case L'I':
  384. MetaphAddChr(primary,'J');
  385. break;
  386.  
  387. default:
  388. MetaphAddChr(primary,'G');
  389. break;
  390. }
  391. break;
  392.  
  393. case L'R':
  394. ahead_char = GetSimplifiedAt(original, current+1);
  395.  
  396. /* como em andar, carro, rato */
  397. if (WORD_EDGE(last_char) || WORD_EDGE(ahead_char))
  398. {
  399. MetaphAddChr(primary,'2');
  400. }
  401. else if (ahead_char == 'R')
  402. {
  403. MetaphAddChr(primary,'2');
  404. current++;
  405. }
  406. /* como em arara */
  407. else if (isVowel(last_char) && isVowel(ahead_char))
  408. {
  409. MetaphAddChr(primary,'R');
  410. current++;
  411.  
  412. /* todo o resto, como em arsenico */
  413. } else
  414. MetaphAddChr(primary,'R');
  415.  
  416. break;
  417.  
  418. case L'Z':
  419. ahead_char = GetAt(original, current+1);
  420.  
  421. /* termina com, como em algoz */
  422. if (WORD_EDGE(ahead_char))
  423. MetaphAddChr(primary,'S');
  424. else
  425. MetaphAddChr(primary,'Z');
  426. break;
  427.  
  428.  
  429. case L'N':
  430. ahead_char = GetAt(original, current+1);
  431.  
  432. /* no português, todas as palavras terminam com 'M', exceto
  433. * no caso de nomes próprios, ou estrangeiros. Para todo caso,
  434. * tem som de 'M'
  435. */
  436. if (WORD_EDGE(ahead_char))
  437. {
  438. MetaphAddChr(primary,'M');
  439. }
  440. /* aranha, nhoque, manha */
  441. else if (ahead_char == 'H')
  442. {
  443. MetaphAddChr(primary,'3');
  444. current++;
  445. }
  446. /* duplicado... */
  447. else if (last_char != 'N')
  448. {
  449. MetaphAddChr(primary,'N');
  450. }
  451. break;
  452.  
  453. case L'S':
  454. ahead_char = GetSimplifiedAt(original, current+1);
  455.  
  456. /* aSSar */
  457. if (ahead_char == 'S')
  458. {
  459. MetaphAddChr(primary,'S');
  460. last_char = ahead_char;
  461. current++;
  462. }
  463. /* mais estrangeirismo: sheila, mishel, e compatibilidade sonora com sobrenomes estrangeiros (japoneses) */
  464. else if (ahead_char == 'H')
  465. {
  466. MetaphAddChr(primary,'X');
  467. current++;
  468. }
  469. /* como em asa */
  470. else if (isVowel(last_char) && isVowel(ahead_char))
  471. {
  472. MetaphAddChr(primary,'Z');
  473. }
  474. /* special cases = 'SC' */
  475. else if (ahead_char == 'C')
  476. {
  477. wchar_t ahead2_char = GetSimplifiedAt(original, current+2);
  478. switch (ahead2_char)
  479. { /* aSCEnder, laSCIvia */
  480. case L'E': case L'I':
  481. MetaphAddChr(primary,'S');
  482. current += 2;
  483. break;
  484.  
  485. /* maSCAvo, aSCO, auSCUltar */
  486. case L'A': case L'O': case L'U':
  487. MetaphAdd(primary,"SK");
  488. current += 2;
  489. break;
  490.  
  491. /* estrangeirismo tal como scheila. */
  492. case L'H':
  493. MetaphAddChr(primary,'X');
  494. current += 2;
  495. break;
  496.  
  497. /* mesclado */
  498. default:
  499. MetaphAddChr(primary,'S');
  500. current ++;
  501. break;
  502. }
  503. }
  504. else
  505. /* catch all - deve pegar atrás e sapato */
  506. MetaphAddChr(primary,'S');
  507. break;
  508.  
  509. /* there is too many exceptions to work on... ahh! */
  510. case L'X':
  511. {
  512. wchar_t last2_char = GetAt(original,current-2);
  513. ahead_char = GetSimplifiedAt(original,current+1);
  514.  
  515. /* fax, anticlímax e todos terminados com 'X' */
  516. if (WORD_EDGE(ahead_char))
  517. {
  518. /* o som destes casos:
  519. * MetaphAdd(primary,"KS");
  520. * para manter compatibilidade com outra implementação, usar abaixo
  521. * como em: Felix, Alex
  522. * Na verdade, para o computador tanto faz. Se todos usarem o mesmo
  523. * significado, o computador sabe q são iguais, não que som q tem.
  524. * A discussão está na representação acurada ou não da fonética.
  525. */
  526. MetaphAdd(primary,"X");
  527. }
  528. /* ...ex... */
  529. else if (last_char == 'E')
  530. {
  531. if( isVowel(ahead_char) )
  532. {
  533. /* começados com EX. Exonerar, exército, executar, exemplo, exame, exílio = ex + vowel
  534. * exuberar
  535. */
  536. if (WORD_EDGE(last2_char))
  537. {
  538. /* deixado com o som original dele */
  539. MetaphAddChr(primary,'Z');
  540. }
  541. else switch(ahead_char)
  542. {
  543. case L'E': case L'I':
  544. /* México, mexerica, mexer */
  545. MetaphAddChr(primary,'X');
  546. current ++;
  547. break;
  548. default:
  549. /* Anexar, sexo, convexo, nexo, circunflexo
  550. * sexual
  551. * inclusive Alex e Alexandre, o que eh
  552. * bom, pois há Aleksandro ou Alex sandro
  553. * OBS: texugo cai aqui. Vítima de guerra.
  554. */
  555. MetaphAdd(primary,"KS");
  556. current ++;
  557. break;
  558. }
  559. }
  560. /* exceção, exceto */
  561. else if (ahead_char == 'C')
  562. {
  563. MetaphAddChr(primary,'S');
  564. current++;
  565. /* expatriar, experimentar, extensão, exterminar. Infelizmente, êxtase cai aqui */
  566. } else if (ahead_char == 'P' || ahead_char == 'T' )
  567. MetaphAdd(primary,"S");
  568. /* catch all exceptions */
  569. else
  570. MetaphAdd(primary,"KS");
  571. }
  572. /* parece que certas sílabas predecessoras do 'x' como
  573. * 'ca' em 'abacaxi' provocam o som de 'CH' no 'x'.
  574. * com exceção do 'm', q é mais complexo.
  575. */
  576. else if (isVowel(last_char))
  577. {
  578. /* faxina. Fax é tratado acima. */
  579. switch (last2_char)
  580. {
  581. /* encontros vocálicos */
  582. case L'A': case L'E': case L'I': case L'O': case L'U': /* caixa, trouxe, abaixar, frouxo, guaxo, Teixeira */
  583. case L'C': /* coxa, abacaxi */
  584. case L'K':
  585. case L'G': /* gaxeta */
  586. case L'L': /* laxante, lixa, lixo */
  587. case L'R': /* roxo, bruxa */
  588. case L'X': /* xaxim */
  589. MetaphAddChr(primary,'X');
  590. break;
  591.  
  592. default:
  593. /* táxi, axila, axioma, tóxico, fixar, fixo, monóxido, óxido */
  594. /* maxilar e enquadra máximo aqui tb, embora não seja correto. */
  595. MetaphAdd(primary,"KS");
  596. break;
  597. }
  598. }
  599. /* anything else... enxame, enxada, -- catch all exceptions :( */
  600. else
  601. MetaphAddChr(primary,'X');
  602. }
  603. break;
  604.  
  605. /* ca, ce, ci, co, cu */
  606. case L'C':
  607. ahead_char = GetSimplifiedAt(original,current+1);
  608. switch(ahead_char)
  609. {
  610. case L'E': case L'I':
  611. MetaphAddChr(primary,'S');
  612. break;
  613.  
  614. case L'H':
  615. /* christiano. */
  616. if( GetSimplifiedAt(original,current+2) == 'R' )
  617. MetaphAddChr(primary,'K');
  618. /* CHapéu, chuva */
  619. else
  620. MetaphAddChr(primary,'X');
  621. current ++;
  622. break;
  623.  
  624. /* Jacques - não fazer nada. Deixa o 'Q' cuidar disso
  625. * ou palavras com CK, mesma coisa.
  626. */
  627. case L'Q':
  628. case L'K':
  629. break;
  630.  
  631. default:
  632. MetaphAddChr(primary,'K');
  633. break;
  634. }
  635.  
  636. break;
  637.  
  638. /*
  639. * only considers the vowels after 'H' if only they are on
  640. * the beginning of the word
  641. */
  642. case L'H':
  643. if (WORD_EDGE(last_char))
  644. {
  645. ahead_char = GetSimplifiedAt(original,current+1);
  646. if (isVowel(ahead_char))
  647. {
  648. MetaphAddChr(primary,ahead_char);
  649. /* this will provoque some words behavior differently,
  650. * which can be desirable, due differences between
  651. * sounds and writting. Ex: HOSANA will be mapped to
  652. * 'S' sound, instead 'Z'.
  653. * OBS: para voltar à representação de Z, comente a linha abaixo
  654. */
  655. current ++;
  656. }
  657. }
  658. break;
  659.  
  660. case L'Q':
  661. MetaphAddChr(primary,'K');
  662. break;
  663.  
  664. case L'W':
  665. ahead_char = GetSimplifiedAt(original,current+1);
  666. if (isVowel(ahead_char))
  667. MetaphAddChr(primary,'V');
  668. else if (ahead_char == 'L' || ahead_char == 'R' ) /* sugestão de luisfurquim@gmail.com p/ Wladimir e Wrana */
  669. MetaphAddChr(primary,'V');
  670.  
  671. /* desconsiderar o W no final das palavras, por ter som de U,
  672. * ou ainda seguidos por consoantes, por ter som de U (Newton)
  673.  
  674. * soluções para www?
  675. */
  676. break;
  677.  
  678. case L'Ç':
  679. MetaphAddChr(primary, 'S');
  680. break;
  681.  
  682. }
  683. /* next char */
  684. current ++;
  685.  
  686. last_char = current_char;
  687. }
  688.  
  689.  
  690.  
  691. primary->str[ primary->length ] = '\0';
  692.  
  693. //META_MALLOC(code, current+1, char);
  694. META_MALLOC(code, primary->length+1, char);
  695. if( !code )
  696. return NULL;
  697. //bzero(code, current+1);
  698. //memcpy(code, primary->str, current);
  699. memcpy(code, primary->str, primary->length);
  700. code[primary->length] = '\0';
  701.  
  702. META_FREE(original);
  703. DestroyMetaString(primary);
  704.  
  705. return code;
  706. }
  707.  
  708.  
Add Comment
Please, Sign In to add comment