| OLD | NEW |
| (Empty) |
| 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 | |
| 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 | |
| 58 static inline void strmove(char * dest, char * src) { | |
| 59 while (*src) | |
| 60 *dest++ = *src++; | |
| 61 *dest = '\0'; | |
| 62 } | |
| 63 | |
| 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 */ | |
| 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 strcpy(word, inword); | |
| 91 | |
| 92 /** check word **/ | |
| 93 i = j = z = 0; | |
| 94 while ((c = word[i]) != '\0') { | |
| 95 n = parms.hash[(uchar) c]; | |
| 96 z0 = 0; | |
| 97 | |
| 98 if (n >= 0) { | |
| 99 /** check all rules for the same letter **/ | |
| 100 while (parms.rules[n][0] == c) { | |
| 101 | |
| 102 /** check whole string **/ | |
| 103 k = 1; /** number of found letters **/ | |
| 104 p = 5; /** default priority **/ | |
| 105 s = parms.rules[n]; | |
| 106 s++; /** important for (see below) "*(s-1)" **/ | |
| 107 | |
| 108 while (*s != '\0' && word[i+k] == *s | |
| 109 && !isdigit ((unsigned char) *s) && strchr ("(-<^$", *s) ==
NULL) { | |
| 110 k++; | |
| 111 s++; | |
| 112 } | |
| 113 if (*s == '(') { | |
| 114 /** check letters in "(..)" **/ | |
| 115 if (myisalpha(word[i+k]) // ...could be implied? | |
| 116 && strchr(s+1, word[i+k]) != NULL) { | |
| 117 k++; | |
| 118 while (*s != ')') | |
| 119 s++; | |
| 120 s++; | |
| 121 } | |
| 122 } | |
| 123 p0 = (int) *s; | |
| 124 k0 = k; | |
| 125 while (*s == '-' && k > 1) { | |
| 126 k--; | |
| 127 s++; | |
| 128 } | |
| 129 if (*s == '<') | |
| 130 s++; | |
| 131 if (isdigit ((unsigned char) *s)) { | |
| 132 /** determine priority **/ | |
| 133 p = *s - '0'; | |
| 134 s++; | |
| 135 } | |
| 136 if (*s == '^' && *(s+1) == '^') | |
| 137 s++; | |
| 138 | |
| 139 if (*s == '\0' | |
| 140 || (*s == '^' | |
| 141 && (i == 0 || ! myisalpha(word[i-1])) | |
| 142 && (*(s+1) != '$' | |
| 143 || (! myisalpha(word[i+k0]) ))) | |
| 144 || (*s == '$' && i > 0 | |
| 145 && myisalpha(word[i-1]) | |
| 146 && (! myisalpha(word[i+k0]) ))) | |
| 147 { | |
| 148 /** search for followup rules, if: **/ | |
| 149 /** parms.followup and k > 1 and NO '-' in searchstring **/ | |
| 150 c0 = word[i+k-1]; | |
| 151 n0 = parms.hash[(uchar) c0]; | |
| 152 | |
| 153 // if (parms.followup && k > 1 && n0 >= 0 | |
| 154 if (k > 1 && n0 >= 0 | |
| 155 && p0 != (int) '-' && word[i+k] != '\0') { | |
| 156 /** test follow-up rule for "word[i+k]" **/ | |
| 157 while (parms.rules[n0][0] == c0) { | |
| 158 | |
| 159 /** check whole string **/ | |
| 160 k0 = k; | |
| 161 p0 = 5; | |
| 162 s = parms.rules[n0]; | |
| 163 s++; | |
| 164 while (*s != '\0' && word[i+k0] == *s | |
| 165 && ! isdigit((unsigned char) *s) && strchr("(-<^$",*s)
== NULL) { | |
| 166 k0++; | |
| 167 s++; | |
| 168 } | |
| 169 if (*s == '(') { | |
| 170 /** check letters **/ | |
| 171 if (myisalpha(word[i+k0]) | |
| 172 && strchr (s+1, word[i+k0]) != NULL) { | |
| 173 k0++; | |
| 174 while (*s != ')' && *s != '\0') | |
| 175 s++; | |
| 176 if (*s == ')') | |
| 177 s++; | |
| 178 } | |
| 179 } | |
| 180 while (*s == '-') { | |
| 181 /** "k0" gets NOT reduced **/ | |
| 182 /** because "if (k0 == k)" **/ | |
| 183 s++; | |
| 184 } | |
| 185 if (*s == '<') | |
| 186 s++; | |
| 187 if (isdigit ((unsigned char) *s)) { | |
| 188 p0 = *s - '0'; | |
| 189 s++; | |
| 190 } | |
| 191 | |
| 192 if (*s == '\0' | |
| 193 /** *s == '^' cuts **/ | |
| 194 || (*s == '$' && ! myisalpha(word[i+k0]))) | |
| 195 { | |
| 196 if (k0 == k) { | |
| 197 /** this is just a piece of the string **/ | |
| 198 n0 += 2; | |
| 199 continue; | |
| 200 } | |
| 201 | |
| 202 if (p0 < p) { | |
| 203 /** priority too low **/ | |
| 204 n0 += 2; | |
| 205 continue; | |
| 206 } | |
| 207 /** rule fits; stop search **/ | |
| 208 break; | |
| 209 } | |
| 210 n0 += 2; | |
| 211 } /** End of "while (parms.rules[n0][0] == c0)" **/ | |
| 212 | |
| 213 if (p0 >= p && parms.rules[n0][0] == c0) { | |
| 214 n += 2; | |
| 215 continue; | |
| 216 } | |
| 217 } /** end of follow-up stuff **/ | |
| 218 | |
| 219 /** replace string **/ | |
| 220 s = parms.rules[n+1]; | |
| 221 p0 = (parms.rules[n][0] != '\0' | |
| 222 && strchr (parms.rules[n]+1,'<') != NULL) ? 1:0; | |
| 223 if (p0 == 1 && z == 0) { | |
| 224 /** rule with '<' is used **/ | |
| 225 if (j > 0 && *s != '\0' | |
| 226 && (target[j-1] == c || target[j-1] == *s)) { | |
| 227 j--; | |
| 228 } | |
| 229 z0 = 1; | |
| 230 z = 1; | |
| 231 k0 = 0; | |
| 232 while (*s != '\0' && word[i+k0] != '\0') { | |
| 233 word[i+k0] = *s; | |
| 234 k0++; | |
| 235 s++; | |
| 236 } | |
| 237 if (k > k0) | |
| 238 strmove (&word[0]+i+k0, &word[0]+i+k); | |
| 239 | |
| 240 /** new "actual letter" **/ | |
| 241 c = word[i]; | |
| 242 } | |
| 243 else { /** no '<' rule used **/ | |
| 244 i += k - 1; | |
| 245 z = 0; | |
| 246 while (*s != '\0' | |
| 247 && *(s+1) != '\0' && j < len) { | |
| 248 if (j == 0 || target[j-1] != *s) { | |
| 249 target[j] = *s; | |
| 250 j++; | |
| 251 } | |
| 252 s++; | |
| 253 } | |
| 254 /** new "actual letter" **/ | |
| 255 c = *s; | |
| 256 if (parms.rules[n][0] != '\0' | |
| 257 && strstr (parms.rules[n]+1, "^^") != NULL) { | |
| 258 if (c != '\0') { | |
| 259 target[j] = c; | |
| 260 j++; | |
| 261 } | |
| 262 strmove (&word[0], &word[0]+i+1); | |
| 263 i = 0; | |
| 264 z0 = 1; | |
| 265 } | |
| 266 } | |
| 267 break; | |
| 268 } /** end of follow-up stuff **/ | |
| 269 n += 2; | |
| 270 } /** end of while (parms.rules[n][0] == c) **/ | |
| 271 } /** end of if (n >= 0) **/ | |
| 272 if (z0 == 0) { | |
| 273 // if (k && (assert(p0!=-333),!p0) && j < len && c != '\0' | |
| 274 // && (!parms.collapse_result || j == 0 || target[j-1] != c)){ | |
| 275 if (k && !p0 && j < len && c != '\0' | |
| 276 && (1 || j == 0 || target[j-1] != c)){ | |
| 277 /** condense only double letters **/ | |
| 278 target[j] = c; | |
| 279 ///printf("\n setting \n"); | |
| 280 j++; | |
| 281 } | |
| 282 | |
| 283 i++; | |
| 284 z = 0; | |
| 285 k=0; | |
| 286 } | |
| 287 } /** end of while ((c = word[i]) != '\0') **/ | |
| 288 | |
| 289 target[j] = '\0'; | |
| 290 return (j); | |
| 291 | |
| 292 } /** end of function "phonet" **/ | |
| OLD | NEW |