Ticket #917: ling_compare3.diff

File ling_compare3.diff, 12.8 KB (added by tryagain, 10 years ago)

More comments.

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