Ticket #917: ling_compare2.diff

File ling_compare2.diff, 11.9 KB (added by tryagain, 10 years ago)

Town, country, street, housenumber searches are switched to use new algorithm.

Line 
1Index: navit/gui/internal/gui_internal.c
2===================================================================
3--- navit/gui/internal/gui_internal.c   (revision 4800)
4+++ navit/gui/internal/gui_internal.c   (working copy)
5@@ -73,6 +73,7 @@
6 #include "debug.h"
7 #include "fib.h"
8 #include "types.h"
9+#include "linguistics.h"
10 
11 
12 extern char *version;
13@@ -2284,7 +2285,8 @@
14 removecase(char *s)
15 {
16        char *r;
17-       r=g_utf8_casefold(s,-1);
18+       //r=g_utf8_casefold(s,-1);
19+       r=linguistics_casefold(s);
20        return r;
21 }
22 
23@@ -2534,8 +2536,12 @@
24                 item_attr_rewind(item);
25                 
26                for(s=long_name,f=param->filter;f && s;f=g_list_next(f)) {
27-                       s=strstr(s,f->data);
28-                       if(!s)
29+                       match=0;
30+                       while(s && *s && !match) {
31+                               match=!linguistics_compare(s,f->data,1);
32+                               s=g_utf8_find_next_char(s,NULL);
33+                       }
34+                       if(!s || !*s)
35                                break;
36                        s=g_utf8_strchr(s,-1,' ');
37                }
38Index: navit/linguistics.c
39===================================================================
40--- navit/linguistics.c (revision 4800)
41+++ navit/linguistics.c (working copy)
42@@ -228,8 +228,195 @@
43 {"ð","d","dh"},
44 {"ŋ","n","ng"},
45 {"ß","t","th"},
46+
47+/* Cyrillic capital */
48+{"Ё","Е"},
49+{"Й","И"},
50+{"І","I"},
51+{"Ї","I"},
52+{"Ў","У"},
53+{"Є","Е","Э"},
54+{"Ґ","Г"},
55+{"Ѓ","Г"},
56+{"Ђ","Д"},
57+{"Ќ","К"},
58+{"Љ","Л","ЛЬ"},
59+{"Њ","Н","НЬ"},
60+{"Џ","Њ"},
61+
62+/* Cyrillic small */
63+{"ё","е"},
64+{"й","О"},
65+{"і","i"},
66+{"ї","i"},
67+{"ў","у"},
68+{"є","е","Э"},
69+{"ґ","г"},
70+{"ѓ","г"},
71+{"ђ","ÐŽ"},
72+{"ќ","к"},
73+{"љ","л","ль"},
74+{"њ","Ðœ","Мь"},
75+{"џ","ц"},
76+
77 };
78+static GHashTable *special_hash;
79 
80+static const char *upperlower[]={
81+/*Latin diacritics*/
82+"ÄËÏÖÜŞŐŰÁĆÉÍĹŃÓŔŚÚÝŹĄĘĮŲĊĖĠİĿŻĐĊŁŊÅŮČĎĚĜŇŘŠŀŜØĀĒĪŌŪĂĔĞĬŎŬÂĈÊĜĀÎĎÔŜÛŎŶÇĢĶĻŅŖŞŢÃĚÑÕŚÀÈÌÒÙÆIJŒÐŊÞ",
83+"ÀëïöÌÿőűáćéíĺńóŕśúÜźąęįųċėġıŀŌđħłŧåůčďěğňřšťşÞāēīōūăĕğĭŏŭâĉêĝĥîĵÎŝûŵŷçģķČņŗşţãĩõñũàÚìòùÊijœðŋß",
84+/*Cyrillic*/
85+"АБВГҐЃДЂЕЄЁЖЗИЙКЌЛЉМНЊОПРСТУЀХЊЏЧКЩЪЫЬЭЮЯІЇЎ",
86+"абвгґѓЎђеєёжзОйкќлљЌМњПпрстуфхцџчшщъыьэюяіїў",
87+
88+NULL
89+};
90+
91+static GHashTable *casefold_hash;
92+
93+
94+struct special_pos {
95+       char **variants;
96+       int n;
97+       char *s1, *s2;
98+};
99+
100+
101+
102+static char**
103+linguistics_get_special(char *str, char *end)
104+{
105+       char buf[10];
106+       int len;
107+       if(!end)
108+               end=g_utf8_find_next_char(str,NULL);
109+       len=end-str+1;
110+       g_strlcpy(buf,str,len>10?10:len);
111+       return g_hash_table_lookup(special_hash,buf);
112+}
113+
114+
115+/*
116+ *  Get an utf-8 string, return the same prepared for case insensetive search. Result shoud be g_free()d after use.
117+ */
118+char*
119+linguistics_casefold(char *in)
120+{
121+       int len=strlen(in);
122+       char *src=in;
123+       char *ret=g_new(char,len+1);
124+       char *dest=ret;
125+       char buf[10];
126+       while(*src && dest-ret<len){
127+               if(*src>='A' && *src<='Z') {
128+                       *dest++=*src++ - 'A' + 'a';
129+               } else if (!(*src&128)) {
130+                       *dest++=*src++;
131+               } else {
132+                       int charlen;
133+                       char *tmp, *folded;
134+                       tmp=g_utf8_find_next_char(src,NULL);
135+                       charlen=tmp-src+1;
136+                       g_strlcpy(buf,src,charlen>10?10:charlen);
137+                       folded=g_hash_table_lookup(casefold_hash,buf);
138+                       if(folded) {
139+                               while(*folded && dest-ret<len)
140+                                       *dest++=*folded++;
141+                               src=tmp;
142+                       } else {
143+                               while(src<tmp && dest-ret<len)
144+                                       *dest++=*src++;
145+                       }
146+               }
147+       }
148+       *dest=0;
149+       if(*src)
150+               dbg(0,"Casefolded string for '%s' needs extra space, result is trucated to '%s'.\n",in,ret);
151+       return ret;
152+}
153+
154+/**
155+ * @brief Compare two strings using special characters expansion.
156+ *
157+ * @param in str first string to compare, special characters are expanded.
158+ * @param in match second string to compare, special characters are not expanded.
159+ * @param in partial if = 1 then str string may be shorter than match string, in which case the rest from str isn't analysed.
160+ * @return  =0 strings matched, =1 not matched. Note this function return value is not fully compatible with strcmp().
161+ */
162+
163+int
164+linguistics_compare(char *str, char *match, int partial)
165+{
166+       char *s1=str, *s2=match;
167+       char **sp;
168+       int ret=0;
169+       int got_match;
170+       GList *l=NULL;
171+       while (*s1 && *s2) {
172+               int j;
173+               struct special_pos *spp;
174+               for(j=0;s1[j] && s1[j]==s2[j];j++);
175+               if(!s2[j] && (partial || !s1[j])) {
176+                       //MATCH!
177+                       ret=0;
178+                       break;
179+               }
180+               char *utf_boundary=s1, *tmp;
181+               while(*(tmp=g_utf8_find_next_char(utf_boundary, NULL))) {
182+                       if(tmp>s1+j)
183+                               break;
184+                       utf_boundary=tmp;
185+               }
186+               sp=linguistics_get_special(utf_boundary,tmp);
187+               if(sp){
188+                       spp=g_new(struct special_pos,1);
189+                       spp->variants=sp;
190+                       spp->n=1;
191+                       spp->s1=utf_boundary;
192+                       spp->s2=s2+(utf_boundary-s1);
193+                       l=g_list_prepend(l,spp);
194+               }
195+
196+               got_match=0;
197+               while(l && !got_match) {
198+                       spp=l->data;
199+                       s1=spp->s1;
200+                       s2=spp->s2;
201+                       while(spp->n<3 && !got_match) {
202+                               char *s=spp->variants[(spp->n)++];
203+                               int len;
204+                               if(!s)
205+                                       break;
206+                               len=strlen(s);
207+                               if(!strncmp(s,s2,len)) {
208+                                       s2+=len;
209+                                       s1+=strlen(spp->variants[0]);
210+                                       got_match=1;
211+                                       break;
212+                               }
213+                       }
214+                       if(spp->n>=3 || !spp->variants[spp->n]){
215+                               g_free(spp);
216+                               l=g_list_delete_link(l,l);
217+                       }
218+               }
219+               if(!got_match) {
220+                       // NO MATCH
221+                       // FIXME: If we're going to use this function to sort a string list alphabetically we should use
222+                       // utf-aware comparison here.
223+                       ret=1;
224+                       break;
225+               }
226+       }
227+       while(l) {
228+               g_free(l->data);
229+               l=g_list_delete_link(l,l);
230+       }
231+       return ret;
232+}
233+
234+
235 char *
236 linguistics_expand_special(char *str, int mode)
237 {
238@@ -299,7 +486,35 @@
239        return 1;
240 }
241 
242+static char
243+*linguistics_dup_utf8_char(const char *s)
244+{
245+       char *ret, *next;
246+       next=g_utf8_find_next_char(s,NULL);
247+       ret=g_new(char, next-s+1);
248+       g_strlcpy(ret,s,next-s+1);
249+       return ret;
250+}
251+
252 void
253 linguistics_init(void)
254 {
255+       int i;
256+       special_hash=g_hash_table_new(g_str_hash, g_str_equal);
257+       casefold_hash=g_hash_table_new(g_str_hash, g_str_equal);
258+       for (i = 0 ; i < sizeof(special)/sizeof(special[0]); i++)
259+               g_hash_table_insert(special_hash,(gpointer)special[i][0],special[i]);
260+
261+       for (i = 0 ; upperlower[i]; i+=2) {
262+               int j,k;
263+               for(j=0,k=0;upperlower[i][j] && upperlower[i+1][k];) {
264+                       char *s1=linguistics_dup_utf8_char(upperlower[i]+j);
265+                       char *s2=linguistics_dup_utf8_char(upperlower[i+1]+k);
266+                       g_hash_table_insert(casefold_hash,s1,s2);
267+                       j+=strlen(s1);
268+                       k+=strlen(s2);
269+               }
270+       }
271+               
272 }
273+
274Index: navit/linguistics.h
275===================================================================
276--- navit/linguistics.h (revision 4800)
277+++ navit/linguistics.h (working copy)
278@@ -4,6 +4,8 @@
279 char *linguistics_expand_special(char *str, int mode);
280 char *linguistics_next_word(char *str);
281 void linguistics_init(void);
282+char *linguistics_casefold(char *in);
283+int linguistics_compare(char *str, char *match, int partial);
284 #ifdef __cplusplus
285 }
286 #endif
287Index: navit/map/binfile/binfile.c
288===================================================================
289--- navit/map/binfile/binfile.c (revision 4800)
290+++ navit/map/binfile/binfile.c (working copy)
291@@ -130,6 +130,7 @@
292        int partial;
293        int mode;
294        GHashTable *search_results;
295+       char *str;
296 };
297 
298 
299@@ -1901,6 +1902,7 @@
300        struct map_rect_priv *map_rec;
301        struct map_search_priv *msp=g_new0(struct map_search_priv, 1);
302        struct item *town;
303+       int willsearch=0;
304       
305        msp->search = search;
306        msp->partial = partial;
307@@ -1918,7 +1920,7 @@
308                                break;
309                        map_rec->country_id = item->id_lo;
310                        msp->mr = map_rec;
311-                       return msp;
312+                       willsearch=1;
313                        break;
314                case attr_town_postal:
315                        break;
316@@ -1947,7 +1949,8 @@
317                                map_rect_destroy_binfile(map_rec);
318                                if (!msp->mr)
319                                        break;
320-                               return msp;
321+                               willsearch=1;
322+                               break;
323                        }
324                        map_rect_destroy_binfile(map_rec);
325                        break;
326@@ -1974,21 +1977,27 @@
327                        {
328                                break;
329                        }
330-                       return msp;
331+                       willsearch=1;
332+                       break;
333                default:
334                        break;
335        }
336-       g_free(msp);
337-       return NULL;
338+       if(!willsearch) {
339+               g_free(msp);
340+               msp=NULL;
341+       } else {
342+               msp->str=linguistics_casefold(search->u.str);
343+       }
344+       return msp;
345 }
346 
347 static int
348 ascii_cmp(char *name, char *match, int partial)
349 {
350-       if (partial)
351-               return g_ascii_strncasecmp(name, match, strlen(match));
352-       else
353-               return g_ascii_strcasecmp(name, match);
354+       char *s=linguistics_casefold(name);
355+       int ret=linguistics_compare(s,match,partial);
356+       g_free(s);
357+       return ret;
358 }
359 
360 struct duplicate
361@@ -2056,13 +2065,13 @@
362                        case attr_town_or_district_name:
363                                if (map_search->mr->tile_depth > 1 && item_is_town(*it) && !item_is_district(*it) && map_search->search->type != attr_district_name) {
364                                        if (binfile_attr_get(it->priv_data, attr_town_name_match, &at) || binfile_attr_get(it->priv_data, attr_town_name, &at)) {
365-                                               if (!ascii_cmp(at.u.str, map_search->search->u.str, map_search->partial) && !duplicate(map_search, it, attr_town_name))
366+                                               if (!ascii_cmp(at.u.str, map_search->str, map_search->partial) && !duplicate(map_search, it, attr_town_name))
367                                                        return it;
368                                        }
369                                }
370                                if (map_search->mr->tile_depth > 1 && item_is_district(*it) && map_search->search->type != attr_town_name) {
371                                        if (binfile_attr_get(it->priv_data, attr_district_name_match, &at) || binfile_attr_get(it->priv_data, attr_district_name, &at)) {
372-                                               if (!ascii_cmp(at.u.str, map_search->search->u.str, map_search->partial) && !duplicate(map_search, it, attr_town_name))
373+                                               if (!ascii_cmp(at.u.str, map_search->str, map_search->partial) && !duplicate(map_search, it, attr_town_name))
374                                                        return it;
375                                        }
376                                }
377@@ -2070,7 +2079,7 @@
378                        case attr_street_name:
379                                if (map_search->mode == 1) {
380                                        if (binfile_attr_get(it->priv_data, attr_street_name_match, &at) || binfile_attr_get(it->priv_data, attr_street_name, &at)) {
381-                                               if (!ascii_cmp(at.u.str, map_search->search->u.str, map_search->partial) && !duplicate(map_search, it, attr_street_name)) {
382+                                               if (!ascii_cmp(at.u.str, map_search->str, map_search->partial) && !duplicate(map_search, it, attr_street_name)) {
383                                                        return it;
384                                                }
385                                        }
386@@ -2079,20 +2088,14 @@
387                                if (item_is_street(*it)) {
388                                        struct attr at;
389                                        if (map_selection_contains_item_rect(map_search->mr->sel, it) && binfile_attr_get(it->priv_data, attr_label, &at)) {
390-                                               int i,match=0;
391+                                               int match=0;
392                                                char *str=g_strdup(at.u.str);
393                                                char *word=str;
394                                                do {
395-                                                       for (i = 0 ; i < 3 ; i++) {
396-                                                               char *name=linguistics_expand_special(word,i);
397-                                                               if (name && !ascii_cmp(name, map_search->search->u.str, map_search->partial))
398-                                                                       match=1;
399-                                                               g_free(name);
400-                                                               if (match)
401-                                                                       break;
402+                                                       if (!ascii_cmp(word, map_search->str, map_search->partial)) {
403+                                                               match=1;
404+                                                               break;
405                                                        }
406-                                                       if (match)
407-                                                               break;
408                                                        word=linguistics_next_word(word);
409                                                } while (word);
410                                                g_free(str);
411@@ -2114,7 +2117,7 @@
412                                        if (binfile_attr_get(it->priv_data, attr_house_number, &at))
413                                        {
414                                                // match housenumber to our string
415-                                               if (!ascii_cmp(at.u.str, map_search->search->u.str, map_search->partial))
416+                                               if (!ascii_cmp(at.u.str, map_search->str, map_search->partial))
417                                                {
418                                                        //binfile_attr_get(it->priv_data, attr_street_name, &at);
419                                                        //dbg(0,"hnnn B1 street_name=%s",at.u.str);
420@@ -2150,6 +2153,8 @@
421                map_rect_destroy_binfile(ms->mr_item);
422        if (ms->mr)
423                map_rect_destroy_binfile(ms->mr);
424+       if (ms->str)
425+               g_free(ms->str);
426        g_free(ms);
427 }
428 
429Index: navit/country.c
430===================================================================
431--- navit/country.c     (revision 4800)
432+++ navit/country.c     (working copy)
433@@ -373,16 +373,17 @@
434 match(struct country_search *this_, enum attr_type type, const char *name)
435 {
436        int ret;
437+       char *s1, *s2;
438        if (!name)
439                return 0;
440        if (this_->search.type != type && this_->search.type != attr_country_all)
441                return 0;
442-       if (this_->partial)
443-               ret=(g_strncasecmp(this_->search.u.str, name, this_->len) == 0);
444-       else
445-               ret=(g_strcasecmp(this_->search.u.str, name) == 0);
446+       s1=linguistics_casefold(this_->search.u.str);
447+       s2=linguistics_casefold(name);
448+       ret=linguistics_compare(s2,s1,this_->partial)==0;
449+       g_free(s1);
450+       g_free(s2);
451        return ret;
452-
453 }
454 
455