1 /*  phonetic.c - generic replacement aglogithms for phonetic transformation
2     Copyright (C) 2000 Bjoern Jacke
3 
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Lesser General Public
6     License version 2.1 as published by the Free Software Foundation;
7 
8     This library is distributed in the hope that it will be useful,
9     but WITHOUT ANY WARRANTY; without even the implied warranty of
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11     Lesser General Public License for more details.
12 
13     You should have received a copy of the GNU Lesser General Public
14     License along with this library; If not, see
15     <http://www.gnu.org/licenses/>.
16 
17     Changelog:
18 
19     2000-01-05  Bjoern Jacke <bjoern at j3e.de>
20                 Initial Release insprired by the article about phonetic
21                 transformations out of c't 25/1999
22 
23     2007-07-26  Bjoern Jacke <bjoern at j3e.de>
24 		Released under MPL/GPL/LGPL tri-license for Hunspell
25 
26     2007-08-23  Laszlo Nemeth <nemeth at OOo>
27                 Porting from Aspell to Hunspell using C-like structs
28 */
29 
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <ctype.h>
34 
35 #include "csutil.hxx"
36 #include "phonet.hxx"
37 
init_phonet_hash(phonetable & parms)38 void init_phonet_hash(phonetable & parms)
39   {
40     int i, k;
41 
42     for (i = 0; i < HASHSIZE; i++) {
43       parms.hash[i] = -1;
44     }
45 
46     for (i = 0; parms.rules[i][0] != '\0'; i += 2) {
47       /**  set hash value  **/
48       k = (unsigned char) parms.rules[i][0];
49 
50       if (parms.hash[k] < 0) {
51 	parms.hash[k] = i;
52       }
53     }
54   }
55 
56 // like strcpy but safe if the strings overlap
57 //   but only if dest < src
strmove(char * dest,char * src)58 static inline void strmove(char * dest, char * src) {
59   while (*src)
60     *dest++ = *src++;
61   *dest = '\0';
62 }
63 
myisalpha(char ch)64 static int myisalpha(char ch) {
65   if ((unsigned char) ch < 128) return isalpha(ch);
66   return 1;
67 }
68 
69 /*  phonetic transcription algorithm                   */
70 /*  see: http://aspell.net/man-html/Phonetic-Code.html */
71 /*  convert string to uppercase before this call       */
phonet(const char * inword,char * target,int len,phonetable & parms)72 int phonet (const char * inword, char * target,
73               int len,
74 	      phonetable & parms)
75   {
76     /**       Do phonetic transformation.       **/
77     /**  "len" = length of "inword" incl. '\0'. **/
78 
79     /**  result:  >= 0:  length of "target"    **/
80     /**            otherwise:  error            **/
81 
82     int  i,j,k=0,n,p,z;
83     int  k0,n0,p0=-333,z0;
84     char c, c0;
85     const char * s;
86     typedef unsigned char uchar;
87     char word[MAXPHONETUTF8LEN + 1];
88     if (len == -1) len = strlen(inword);
89     if (len > MAXPHONETUTF8LEN) return 0;
90     strncpy(word, inword, MAXPHONETUTF8LEN);
91     word[MAXPHONETUTF8LEN] = '\0';
92 
93     /**  check word  **/
94     i = j = z = 0;
95     while ((c = word[i]) != '\0') {
96       n = parms.hash[(uchar) c];
97       z0 = 0;
98 
99       if (n >= 0) {
100         /**  check all rules for the same letter  **/
101         while (parms.rules[n][0] == c) {
102 
103           /**  check whole string  **/
104           k = 1;   /** number of found letters  **/
105           p = 5;   /** default priority  **/
106           s = parms.rules[n];
107           s++;     /**  important for (see below)  "*(s-1)"  **/
108 
109           while (*s != '\0'  &&  word[i+k] == *s
110                  &&  !isdigit ((unsigned char) *s)  &&  strchr ("(-<^$", *s) == NULL) {
111             k++;
112             s++;
113           }
114           if (*s == '(') {
115             /**  check letters in "(..)"  **/
116             if (myisalpha(word[i+k])  // ...could be implied?
117                 && strchr(s+1, word[i+k]) != NULL) {
118               k++;
119               while (*s != ')')
120                 s++;
121               s++;
122             }
123           }
124           p0 = (int) *s;
125           k0 = k;
126           while (*s == '-'  &&  k > 1) {
127             k--;
128             s++;
129           }
130           if (*s == '<')
131             s++;
132           if (isdigit ((unsigned char) *s)) {
133             /**  determine priority  **/
134             p = *s - '0';
135             s++;
136           }
137           if (*s == '^'  &&  *(s+1) == '^')
138             s++;
139 
140           if (*s == '\0'
141               || (*s == '^'
142                   && (i == 0  ||  ! myisalpha(word[i-1]))
143                   && (*(s+1) != '$'
144                       || (! myisalpha(word[i+k0]) )))
145               || (*s == '$'  &&  i > 0
146                   &&  myisalpha(word[i-1])
147                   && (! myisalpha(word[i+k0]) )))
148           {
149             /**  search for followup rules, if:     **/
150             /**  parms.followup and k > 1  and  NO '-' in searchstring **/
151             c0 = word[i+k-1];
152             n0 = parms.hash[(uchar) c0];
153 
154 //            if (parms.followup  &&  k > 1  &&  n0 >= 0
155             if (k > 1  &&  n0 >= 0
156                 &&  p0 != (int) '-'  &&  word[i+k] != '\0') {
157               /**  test follow-up rule for "word[i+k]"  **/
158               while (parms.rules[n0][0] == c0) {
159 
160                 /**  check whole string  **/
161                 k0 = k;
162                 p0 = 5;
163                 s = parms.rules[n0];
164                 s++;
165                 while (*s != '\0'  &&  word[i+k0] == *s
166                        && ! isdigit((unsigned char) *s)  &&  strchr("(-<^$",*s) == NULL) {
167                   k0++;
168                   s++;
169                 }
170                 if (*s == '(') {
171                   /**  check letters  **/
172                   if (myisalpha(word[i+k0])
173                       &&  strchr (s+1, word[i+k0]) != NULL) {
174                     k0++;
175                     while (*s != ')'  &&  *s != '\0')
176                       s++;
177                     if (*s == ')')
178                       s++;
179                   }
180                 }
181                 while (*s == '-') {
182                   /**  "k0" gets NOT reduced   **/
183                   /**  because "if (k0 == k)"  **/
184                   s++;
185                 }
186                 if (*s == '<')
187                   s++;
188                 if (isdigit ((unsigned char) *s)) {
189                   p0 = *s - '0';
190                   s++;
191                 }
192 
193                 if (*s == '\0'
194                     /**  *s == '^' cuts  **/
195                     || (*s == '$'  &&  ! myisalpha(word[i+k0])))
196                 {
197                   if (k0 == k) {
198                     /**  this is just a piece of the string  **/
199                     n0 += 2;
200                     continue;
201                   }
202 
203                   if (p0 < p) {
204                     /**  priority too low  **/
205                     n0 += 2;
206                     continue;
207                   }
208                   /**  rule fits; stop search  **/
209                   break;
210                 }
211                 n0 += 2;
212               } /**  End of "while (parms.rules[n0][0] == c0)"  **/
213 
214               if (p0 >= p  && parms.rules[n0][0] == c0) {
215                 n += 2;
216                 continue;
217               }
218             } /** end of follow-up stuff **/
219 
220             /**  replace string  **/
221             s = parms.rules[n+1];
222             p0 = (parms.rules[n][0] != '\0'
223                  &&  strchr (parms.rules[n]+1,'<') != NULL) ? 1:0;
224             if (p0 == 1 &&  z == 0) {
225               /**  rule with '<' is used  **/
226               if (j > 0  &&  *s != '\0'
227                  && (target[j-1] == c  ||  target[j-1] == *s)) {
228                 j--;
229               }
230               z0 = 1;
231               z = 1;
232               k0 = 0;
233               while (*s != '\0'  &&  word[i+k0] != '\0') {
234                 word[i+k0] = *s;
235                 k0++;
236                 s++;
237               }
238               if (k > k0)
239                 strmove (&word[0]+i+k0, &word[0]+i+k);
240 
241               /**  new "actual letter"  **/
242               c = word[i];
243             }
244             else { /** no '<' rule used **/
245               i += k - 1;
246               z = 0;
247               while (*s != '\0'
248                      &&  *(s+1) != '\0'  &&  j < len) {
249                 if (j == 0  ||  target[j-1] != *s) {
250                   target[j] = *s;
251                   j++;
252                 }
253                 s++;
254               }
255               /**  new "actual letter"  **/
256               c = *s;
257               if (parms.rules[n][0] != '\0'
258                  &&  strstr (parms.rules[n]+1, "^^") != NULL) {
259                 if (c != '\0') {
260                   target[j] = c;
261                   j++;
262                 }
263                 strmove (&word[0], &word[0]+i+1);
264                 i = 0;
265                 z0 = 1;
266               }
267             }
268             break;
269           }  /** end of follow-up stuff **/
270           n += 2;
271         } /**  end of while (parms.rules[n][0] == c)  **/
272       } /**  end of if (n >= 0)  **/
273       if (z0 == 0) {
274 //        if (k && (assert(p0!=-333),!p0) &&  j < len &&  c != '\0'
275 //           && (!parms.collapse_result  ||  j == 0  ||  target[j-1] != c)){
276         if (k && !p0 && j < len &&  c != '\0'
277            && (1 || j == 0  ||  target[j-1] != c)){
278            /**  condense only double letters  **/
279           target[j] = c;
280 	  ///printf("\n setting \n");
281           j++;
282         }
283 
284         i++;
285         z = 0;
286 	k=0;
287       }
288     }  /**  end of   while ((c = word[i]) != '\0')  **/
289 
290     target[j] = '\0';
291     return (j);
292 
293   }  /**  end of function "phonet"  **/
294