Ticket #917: ling_compare6.diff

File ling_compare6.diff, 13.7 KB (added by tryagain, 10 years ago)

Failed assertion expanding Cyrillic logatures is fixed.

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