1 #include "e_mod_main.h"
2 #include "md5.h"
3 
4 #define MAX_FUZZ  100
5 #define MAX_WORDS 5
6 
7 static const char *home_dir = NULL;
8 static int home_dir_len;
9 static char dir_buf[1024];
10 static char thumb_buf[4096];
11 
12 void
evry_util_file_detail_set(Evry_Item_File * file)13 evry_util_file_detail_set(Evry_Item_File *file)
14 {
15    char *dir = NULL;
16    const char *tmp;
17 
18    if (EVRY_ITEM(file)->detail)
19      return;
20 
21    if (!home_dir)
22      {
23         home_dir = e_user_homedir_get();
24         home_dir_len = strlen(home_dir);
25      }
26 
27    dir = ecore_file_dir_get(file->path);
28    if (!dir || !home_dir) return;
29 
30    if (!strncmp(dir, home_dir, home_dir_len))
31      {
32         tmp = dir + home_dir_len;
33 
34         if (*(tmp) == '\0')
35           snprintf(dir_buf, sizeof(dir_buf), "~%s", tmp);
36         else
37           snprintf(dir_buf, sizeof(dir_buf), "~%s/", tmp);
38 
39         EVRY_ITEM(file)->detail = eina_stringshare_add(dir_buf);
40      }
41    else
42      {
43         if (!strncmp(dir, "//", 2))
44           EVRY_ITEM(file)->detail = eina_stringshare_add(dir + 1);
45         else
46           EVRY_ITEM(file)->detail = eina_stringshare_add(dir);
47      }
48 
49    E_FREE(dir);
50 }
51 
52 static inline Eina_Unicode
_evry_utf8_next(const char * buf,int * iindex)53 _evry_utf8_next(const char *buf, int *iindex)
54 {
55    Eina_Unicode u = eina_unicode_utf8_next_get(buf, iindex);
56    if ((!u) || ((u >= 0xdc80) && (u <= 0xdcff))) return 0;
57    return u;
58 }
59 
60 int
evry_fuzzy_match(const char * str,const char * match)61 evry_fuzzy_match(const char *str, const char *match)
62 {
63    const char *p, *m, *next;
64    int sum = 0;
65 
66    unsigned int last = 0;
67    unsigned int offset = 0;
68    unsigned int min = 0;
69    unsigned char first = 0;
70    /* ignore punctuation */
71    unsigned char ip = 1;
72 
73    unsigned int cnt = 0;
74    /* words in match */
75    unsigned int m_num = 0;
76    unsigned int m_cnt = 0;
77    unsigned int m_min[MAX_WORDS];
78    unsigned int m_len = 0;
79    unsigned int s_len = 0;
80 
81    if (!match || !str || !match[0] || !str[0])
82      return 0;
83 
84    /* remove white spaces at the beginning */
85    for (; (*match != 0) && isspace(*match); match++) ;
86    for (; (*str != 0) && isspace(*str); str++) ;
87 
88    /* count words in match */
89    for (m = match; (*m != 0) && (m_num < MAX_WORDS); )
90      {
91         for (; (*m != 0) && !isspace(*m); m++) ;
92         for (; (*m != 0) && isspace(*m); m++) ;
93         m_min[m_num++] = MAX_FUZZ;
94      }
95    for (m = match; ip && (*m != 0); m++)
96      if (ip && ispunct(*m)) ip = 0;
97 
98    m_len = strlen(match);
99    s_len = strlen(str);
100 
101    /* with less than 3 chars match must be a prefix */
102    if (m_len < 3) m_len = 0;
103 
104    next = str;
105    m = match;
106 
107    while ((m_cnt < m_num) && (*next != 0))
108      {
109         int ii;
110 
111         /* reset match */
112         if (m_cnt == 0) m = match;
113 
114         /* end of matching */
115         if (*m == 0) break;
116 
117         offset = 0;
118         last = 0;
119         min = 1;
120         first = 0;
121         /* m_len = 0; */
122 
123         /* match current word of string against current match */
124         for (p = next; *next != 0; p++)
125           {
126              /* new word of string begins */
127              if ((*p == 0) || isspace(*p) || (ip && ispunct(*p)))
128                {
129                   if (m_cnt < m_num - 1)
130                     {
131                        /* test next match */
132                        for (; (*m != 0) && !isspace(*m); m++) ;
133                        for (; (*m != 0) && isspace(*m); m++) ;
134                        m_cnt++;
135                        break;
136                     }
137                   else
138                     {
139                        ii = 0;
140                        /* go to next word */
141                        for (; (*p != 0) && ((isspace(*p) || (ip && ispunct(*p)))); p += ii)
142                          {
143                             ii = 0;
144                             if (!_evry_utf8_next(p, &ii)) break;
145                          }
146                        cnt++;
147                        next = p;
148                        m_cnt = 0;
149                        break;
150                     }
151                }
152 
153              /* current char matches? */
154              if (tolower(*p) != tolower(*m))
155                {
156                   if (!first)
157                     offset += 1;
158                   else
159                     offset += 3;
160 
161                   /* m_len++; */
162 
163                   if (offset <= m_len * 3)
164                     continue;
165                }
166 
167              if (min < MAX_FUZZ && offset <= m_len * 3)
168                {
169                   /* first offset of match in word */
170                   if (!first)
171                     {
172                        first = 1;
173                        last = offset;
174                     }
175 
176                   min += offset + (offset - last) * 5;
177                   last = offset;
178 
179                   /* try next char of match */
180                   ii = 0;
181                   if (!_evry_utf8_next(m, &ii)) continue;
182                   m += ii;
183                   if (*m != 0 && !isspace(*m))
184                     continue;
185 
186                   /* end of match: store min weight of match */
187                   min += (cnt - m_cnt) > 0 ? (cnt - m_cnt) : 0;
188 
189                   if (min < m_min[m_cnt])
190                     m_min[m_cnt] = min;
191                }
192              else
193                {
194                   ii = 0;
195                   /* go to next match */
196                   for (; (m[0] && m[ii]) && !isspace(*m); m += ii)
197                     {
198                        ii = 0;
199                        if (!_evry_utf8_next(m, &ii)) break;
200                     }
201                }
202 
203              if (m_cnt < m_num - 1)
204                {
205                   ii = 0;
206                   /* test next match */
207                   for (; (m[0] && m[ii]) && !isspace(*m); m += ii)
208                     {
209                        ii = 0;
210                        if (!_evry_utf8_next(m, &ii)) break;
211                     }
212                   m_cnt++;
213                   break;
214                }
215              else if (*p != 0)
216                {
217                   ii = 0;
218                   /* go to next word */
219                   for (; (p[0] && (s_len - (p - str) >= (unsigned int)ii)) &&
220                        !((isspace(*p) || (ip && ispunct(*p))));
221                        p += ii)
222                     {
223                        if (!_evry_utf8_next(p, &ii)) break;
224                     }
225                   ii = 0;
226                   for (; (p[0] && (s_len - (p - str) >= (unsigned int)ii)) &&
227                        ((isspace(*p) || (ip && ispunct(*p))));
228                        p += ii)
229                     {
230                        if (!_evry_utf8_next(p, &ii)) break;
231                     }
232                   cnt++;
233                   next = p;
234                   m_cnt = 0;
235                   break;
236                }
237              else
238                {
239                   next = p;
240                   break;
241                }
242           }
243      }
244 
245    for (m_cnt = 0; m_cnt < m_num; m_cnt++)
246      {
247         sum += m_min[m_cnt];
248 
249         if (sum >= MAX_FUZZ)
250           {
251              sum = 0;
252              break;
253           }
254      }
255 
256    if (sum > 0)
257      {
258         /* exact match ? */
259         if (strcmp(match, str))
260           sum += 10;
261      }
262 
263    return sum;
264 }
265 
266 static int
_evry_fuzzy_match_sort_cb(const void * data1,const void * data2)267 _evry_fuzzy_match_sort_cb(const void *data1, const void *data2)
268 {
269    const Evry_Item *it1 = data1;
270    const Evry_Item *it2 = data2;
271 
272    if (it1->priority - it2->priority)
273      return it1->priority - it2->priority;
274 
275    if (it1->fuzzy_match || it2->fuzzy_match)
276      {
277         if (it1->fuzzy_match && !it2->fuzzy_match)
278           return -1;
279 
280         if (!it1->fuzzy_match && it2->fuzzy_match)
281           return 1;
282 
283         if (it1->fuzzy_match - it2->fuzzy_match)
284           return it1->fuzzy_match - it2->fuzzy_match;
285      }
286 
287    return 0;
288 }
289 
290 Eina_List *
evry_fuzzy_match_sort(Eina_List * items)291 evry_fuzzy_match_sort(Eina_List *items)
292 {
293    return eina_list_sort(items, -1, _evry_fuzzy_match_sort_cb);
294 }
295 
296 static int _sort_flags = 0;
297 
298 static int
_evry_items_sort_func(const void * data1,const void * data2)299 _evry_items_sort_func(const void *data1, const void *data2)
300 {
301    const Evry_Item *it1 = data1;
302    const Evry_Item *it2 = data2;
303 
304    /* if (!((!_sort_flags) &&
305     *       (it1->type == EVRY_TYPE_ACTION) &&
306     *       (it2->type == EVRY_TYPE_ACTION)))
307     *   { */
308    /* only sort actions when there is input otherwise show default order */
309 
310    if (((it1->type == EVRY_TYPE_ACTION) || (it1->subtype == EVRY_TYPE_ACTION)) &&
311        ((it2->type == EVRY_TYPE_ACTION) || (it2->subtype == EVRY_TYPE_ACTION)))
312      {
313         const Evry_Action *act1 = data1;
314         const Evry_Action *act2 = data2;
315 
316         /* sort actions that match the specific type before
317            those matching general type */
318         if (act1->it1.item && act2->it1.item)
319           {
320              if ((act1->it1.type == act1->it1.item->type) &&
321                  (act2->it1.type != act2->it1.item->type))
322                return -1;
323 
324              if ((act1->it1.type != act1->it1.item->type) &&
325                  (act2->it1.type == act2->it1.item->type))
326                return 1;
327           }
328 
329         /* sort context specific actions before
330            general actions */
331         if (act1->remember_context)
332           {
333              if (!act2->remember_context)
334                return -1;
335           }
336         else
337           {
338              if (act2->remember_context)
339                return 1;
340           }
341      }
342    /* } */
343 
344    if (_sort_flags)
345      {
346         /* when there is no input sort items with higher
347          * plugin priority first */
348         if (it1->type != EVRY_TYPE_ACTION &&
349             it2->type != EVRY_TYPE_ACTION)
350           {
351              int prio1 = it1->plugin->config->priority;
352              int prio2 = it2->plugin->config->priority;
353 
354              if (prio1 - prio2)
355                return prio1 - prio2;
356           }
357      }
358 
359    /* sort items which match input or which
360       match much better first */
361    if (it1->fuzzy_match > 0 || it2->fuzzy_match > 0)
362      {
363         if (it2->fuzzy_match <= 0)
364           return -1;
365 
366         if (it1->fuzzy_match <= 0)
367           return 1;
368 
369         if (abs (it1->fuzzy_match - it2->fuzzy_match) > 5)
370           return it1->fuzzy_match - it2->fuzzy_match;
371      }
372 
373    /* sort recently/most frequently used items first */
374    if (it1->usage > 0.0 || it2->usage > 0.0)
375      {
376         return it1->usage > it2->usage ? -1 : 1;
377      }
378 
379    /* sort items which match input better first */
380    if (it1->fuzzy_match > 0 || it2->fuzzy_match > 0)
381      {
382         if (it1->fuzzy_match - it2->fuzzy_match)
383           return it1->fuzzy_match - it2->fuzzy_match;
384      }
385 
386    /* sort itemswith higher priority first */
387    if ((it1->plugin == it2->plugin) &&
388        (it1->priority - it2->priority))
389      return it1->priority - it2->priority;
390 
391    /* sort items with higher plugin priority first */
392    if (it1->type != EVRY_TYPE_ACTION &&
393        it2->type != EVRY_TYPE_ACTION)
394      {
395         int prio1 = it1->plugin->config->priority;
396         int prio2 = it2->plugin->config->priority;
397 
398         if (prio1 - prio2)
399           return prio1 - prio2;
400      }
401 
402    /* user has a broken system: -╯□)╯︵-┻━┻ */
403    if ((!it1->label) || (!it2->label)) return -1;
404    return strcasecmp(it1->label, it2->label);
405 }
406 
407 void
evry_util_items_sort(Eina_List ** items,int flags)408 evry_util_items_sort(Eina_List **items, int flags)
409 {
410    _sort_flags = flags;
411    *items = eina_list_sort(*items, -1, _evry_items_sort_func);
412    _sort_flags = 0;
413 }
414 
415 int
evry_util_plugin_items_add(Evry_Plugin * p,Eina_List * items,const char * input,int match_detail,int set_usage)416 evry_util_plugin_items_add(Evry_Plugin *p, Eina_List *items, const char *input,
417                            int match_detail, int set_usage)
418 {
419    Eina_List *l;
420    Evry_Item *it;
421    int match = 0;
422 
423    EINA_LIST_FOREACH (items, l, it)
424      {
425         it->fuzzy_match = 0;
426 
427         if (set_usage)
428           evry_history_item_usage_set(it, input, NULL);
429 
430         if (!input)
431           {
432              p->items = eina_list_append(p->items, it);
433              continue;
434           }
435 
436         it->fuzzy_match = evry_fuzzy_match(it->label, input);
437 
438         if (match_detail)
439           {
440              match = evry_fuzzy_match(it->detail, input);
441 
442              if (!(it->fuzzy_match) || (match && (match < it->fuzzy_match)))
443                it->fuzzy_match = match;
444           }
445 
446         if (it->fuzzy_match)
447           p->items = eina_list_append(p->items, it);
448      }
449 
450    p->items = eina_list_sort(p->items, -1, _evry_items_sort_func);
451 
452    return !!(p->items);
453 }
454 
455 Evas_Object *
evry_icon_theme_get(const char * icon,Evas * e)456 evry_icon_theme_get(const char *icon, Evas *e)
457 {
458    Evas_Object *o = NULL;
459 
460    if (!icon)
461      return NULL;
462 
463    o = e_icon_add(e);
464    e_icon_scale_size_set(o, 128);
465    e_icon_preload_set(o, 1);
466 
467    if (icon[0] == '/')
468      {
469         e_icon_file_set(o, icon);
470      }
471    else if (!e_util_icon_theme_set(o, icon))
472      {
473         char grp[1024];
474 
475         snprintf(grp, sizeof(grp), "fileman/mime/%s", icon);
476         if (!e_util_icon_theme_set(o, grp))
477           {
478              evas_object_del(o);
479              o = NULL;
480           }
481      }
482 
483    return o;
484 }
485 
486 Evas_Object *
evry_util_icon_get(Evry_Item * it,Evas * e)487 evry_util_icon_get(Evry_Item *it, Evas *e)
488 {
489    Evas_Object *o = NULL;
490 
491    if (it->icon_get)
492      {
493         o = it->icon_get(it, e);
494         if (o) return o;
495      }
496 
497    if ((it->icon) && (it->icon[0] == '/'))
498      {
499         o = evry_icon_theme_get(it->icon, e);
500         if (o) return o;
501      }
502 
503    if (CHECK_TYPE(it, EVRY_TYPE_FILE))
504      {
505         const char *icon;
506         char *sum;
507 
508         GET_FILE(file, it);
509 
510         if (it->browseable)
511           {
512              o = evry_icon_theme_get("folder", e);
513              if (o) return o;
514           }
515 
516         if ((!it->icon) && (file->mime) &&
517             ( /*(!strncmp(file->mime, "image/", 6)) || */
518               (!strncmp(file->mime, "video/", 6)) ||
519               (!strncmp(file->mime, "application/pdf", 15))) &&
520             (evry_file_url_get(file)))
521           {
522              sum = evry_util_md5_sum(file->url);
523 
524              snprintf(thumb_buf, sizeof(thumb_buf),
525                       "%s/.thumbnails/normal/%s.png",
526                       e_user_homedir_get(), sum);
527              free(sum);
528 
529              if ((o = evry_icon_theme_get(thumb_buf, e)))
530                {
531                   it->icon = eina_stringshare_add(thumb_buf);
532                   return o;
533                }
534           }
535 
536         if ((!it->icon) && (file->mime))
537           {
538              icon = efreet_mime_type_icon_get(file->mime, e_config->icon_theme, 128);
539              /* XXX can do _ref ?*/
540              if ((o = evry_icon_theme_get(icon, e)) || (o = evry_icon_theme_get(file->mime, e)))
541                {
542                   /* it->icon = eina_stringshare_add(icon); */
543                   return o;
544                }
545           }
546 
547         if ((icon = efreet_mime_type_icon_get("unknown", e_config->icon_theme, 128)))
548           it->icon = eina_stringshare_add(icon);
549         else
550           it->icon = eina_stringshare_add("");
551      }
552 
553    if (CHECK_TYPE(it, EVRY_TYPE_APP))
554      {
555         GET_APP(app, it);
556 
557         o = e_util_desktop_icon_add(app->desktop, 128, e);
558         if (o) return o;
559 
560         o = evry_icon_theme_get("system-run", e);
561         if (o) return o;
562      }
563 
564    if (it->icon)
565      {
566         o = evry_icon_theme_get(it->icon, e);
567         if (o) return o;
568      }
569 
570    if (it->browseable)
571      {
572         o = evry_icon_theme_get("folder", e);
573         if (o) return o;
574      }
575 
576    o = evry_icon_theme_get("unknown", e);
577    return o;
578 }
579 
580 int
evry_util_exec_app(const Evry_Item * it_app,const Evry_Item * it_file)581 evry_util_exec_app(const Evry_Item *it_app, const Evry_Item *it_file)
582 {
583    E_Zone *zone;
584    Eina_List *files = NULL;
585    char *exe = NULL;
586    char *tmp = NULL;
587 
588    if (!it_app) return 0;
589    GET_APP(app, it_app);
590    GET_FILE(file, it_file);
591 
592    zone = e_zone_current_get();
593 
594    if (app->desktop)
595      {
596         if (file && evry_file_path_get(file))
597           {
598              Eina_List *l;
599              char *mime;
600              int open_folder = 0;
601 
602              /* when the file is no a directory and the app
603                 opens folders, pass only the dir */
604              if (!IS_BROWSEABLE(file))
605                {
606                   EINA_LIST_FOREACH (app->desktop->mime_types, l, mime)
607                     {
608                        if (!mime)
609                          continue;
610 
611                        if (!strcmp(mime, "x-directory/normal"))
612                          open_folder = 1;
613 
614                        if (file->mime && !strcmp(mime, file->mime))
615                          {
616                             open_folder = 0;
617                             break;
618                          }
619                     }
620                }
621 
622              if (open_folder)
623                {
624                   tmp = ecore_file_dir_get(file->path);
625                   files = eina_list_append(files, tmp);
626                }
627              else
628                {
629                   files = eina_list_append(files, file->path);
630                }
631 
632              e_exec(zone, app->desktop, NULL, files, NULL);
633 
634              if (file && file->mime && !open_folder)
635                e_exehist_mime_desktop_add(file->mime, app->desktop);
636 
637              if (files)
638                eina_list_free(files);
639 
640              E_FREE(tmp);
641           }
642         else if (app->file)
643           {
644              files = eina_list_append(files, app->file);
645              e_exec(zone, app->desktop, NULL, files, NULL);
646              eina_list_free(files);
647           }
648         else
649           {
650              e_exec(zone, app->desktop, NULL, NULL, NULL);
651           }
652      }
653    else if (app->file)
654      {
655         if (file && evry_file_path_get(file))
656           {
657              int len;
658              len = strlen(app->file) + strlen(file->path) + 4;
659              exe = malloc(len);
660              snprintf(exe, len, "%s \'%s\'", app->file, file->path);
661              e_exec(zone, NULL, exe, NULL, NULL);
662              E_FREE(exe);
663           }
664         else
665           {
666              exe = (char *)app->file;
667              e_exec(zone, NULL, exe, NULL, NULL);
668           }
669      }
670 
671    return 1;
672 }
673 
674 /* taken from curl:
675  *
676  * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et
677  * al.
678  *
679  * Unescapes the given URL escaped string of given length. Returns a
680  * pointer to a malloced string with length given in *olen.
681  * If length == 0, the length is assumed to be strlen(string).
682  * If olen == NULL, no output length is stored.
683  */
684 #define ISXDIGIT(x) (isxdigit((int)((unsigned char)x)))
685 
686 char *
evry_util_url_unescape(const char * string,int length)687 evry_util_url_unescape(const char *string, int length)
688 {
689    int alloc = (length ? length : (int)strlen(string)) + 1;
690    char *ns = malloc(alloc);
691    unsigned char in;
692    int strindex = 0;
693    unsigned long hex;
694 
695    if ( !ns )
696      return NULL;
697 
698    while (--alloc > 0)
699      {
700         in = *string;
701         if (('%' == in) && ISXDIGIT(string[1]) && ISXDIGIT(string[2]))
702           {
703              /* this is two hexadecimal digits following a '%' */
704              char hexstr[3];
705              char *ptr;
706              hexstr[0] = string[1];
707              hexstr[1] = string[2];
708              hexstr[2] = 0;
709 
710              hex = strtoul(hexstr, &ptr, 16);
711              in = (unsigned char)(hex & (unsigned long)0xFF);
712              // in = ultouc(hex); /* this long is never bigger than 255 anyway */
713 
714              string += 2;
715              alloc -= 2;
716           }
717 
718         ns[strindex++] = in;
719         string++;
720      }
721    ns[strindex] = 0; /* terminate it */
722 
723    return ns;
724 }
725 
726 #undef ISXDIGIT
727 
728 static Eina_Bool
_isalnum(unsigned char in)729 _isalnum(unsigned char in)
730 {
731    switch (in)
732      {
733       case '0':
734       case '1':
735       case '2':
736       case '3':
737       case '4':
738       case '5':
739       case '6':
740       case '7':
741       case '8':
742       case '9':
743       case 'a':
744       case 'b':
745       case 'c':
746       case 'd':
747       case 'e':
748       case 'f':
749       case 'g':
750       case 'h':
751       case 'i':
752       case 'j':
753       case 'k':
754       case 'l':
755       case 'm':
756       case 'n':
757       case 'o':
758       case 'p':
759       case 'q':
760       case 'r':
761       case 's':
762       case 't':
763       case 'u':
764       case 'v':
765       case 'w':
766       case 'x':
767       case 'y':
768       case 'z':
769       case 'A':
770       case 'B':
771       case 'C':
772       case 'D':
773       case 'E':
774       case 'F':
775       case 'G':
776       case 'H':
777       case 'I':
778       case 'J':
779       case 'K':
780       case 'L':
781       case 'M':
782       case 'N':
783       case 'O':
784       case 'P':
785       case 'Q':
786       case 'R':
787       case 'S':
788       case 'T':
789       case 'U':
790       case 'V':
791       case 'W':
792       case 'X':
793       case 'Y':
794       case 'Z':
795         return EINA_TRUE;
796 
797       default:
798         break;
799      }
800    return EINA_FALSE;
801 }
802 
803 char *
evry_util_url_escape(const char * string,int inlength)804 evry_util_url_escape(const char *string, int inlength)
805 {
806    size_t alloc = (inlength ? (size_t)inlength : strlen(string)) + 1;
807    char *ns;
808    char *testing_ptr = NULL;
809    unsigned char in; /* we need to treat the characters unsigned */
810    size_t newlen = alloc;
811    int strindex = 0;
812    size_t length;
813 
814    ns = malloc(alloc);
815    if (!ns)
816      return NULL;
817 
818    length = alloc - 1;
819    while (length--)
820      {
821         in = *string;
822 
823         if (_isalnum(in))
824           {
825              /* just copy this */
826              ns[strindex++] = in;
827           }
828         else {
829              /* encode it */
830              newlen += 2; /* the size grows with two, since this'll become a %XX */
831              if (newlen > alloc)
832                {
833                   alloc *= 2;
834                   testing_ptr = realloc(ns, alloc);
835                   if (!testing_ptr)
836                     {
837                        free(ns);
838                        return NULL;
839                     }
840                   else
841                     {
842                        ns = testing_ptr;
843                     }
844                }
845 
846              snprintf(&ns[strindex], 4, "%%%02X", in);
847 
848              strindex += 3;
849           }
850         string++;
851      }
852    ns[strindex] = 0; /* terminate it */
853    return ns;
854 }
855 
856 const char *
evry_file_path_get(Evry_Item_File * file)857 evry_file_path_get(Evry_Item_File *file)
858 {
859    const char *tmp;
860    char *path;
861 
862    if (file->path)
863      return file->path;
864 
865    if (!file->url)
866      return NULL;
867 
868    if (!strncmp(file->url, "file://", 7))
869      tmp = file->url + 7;
870    else return NULL;
871 
872    if (!(path = evry_util_url_unescape(tmp, 0)))
873      return NULL;
874 
875    file->path = eina_stringshare_add(path);
876 
877    E_FREE(path);
878 
879    return file->path;
880 }
881 
882 const char *
evry_file_url_get(Evry_Item_File * file)883 evry_file_url_get(Evry_Item_File *file)
884 {
885    char dest[PATH_MAX * 3 + 7];
886    const char *p;
887    int i;
888 
889    if (file->url)
890      return file->url;
891 
892    if (!file->path)
893      return NULL;
894 
895    memset(dest, 0, sizeof(dest));
896 
897    snprintf(dest, 8, "file://");
898 
899    /* Most app doesn't handle the hostname in the uri so it's put to NULL */
900    for (i = 7, p = file->path; *p != '\0'; p++, i++)
901      {
902         if (isalnum(*p) || strchr("/$-_.+!*'()", *p))
903           dest[i] = *p;
904         else
905           {
906              snprintf(&(dest[i]), 4, "%%%02X", (unsigned char)*p);
907              i += 2;
908           }
909      }
910 
911    file->url = eina_stringshare_add(dest);
912 
913    return file->url;
914 }
915 
916 static void
_cb_free_item_changed(void * data EINA_UNUSED,void * event)917 _cb_free_item_changed(void *data EINA_UNUSED, void *event)
918 {
919    Evry_Event_Item_Changed *ev = event;
920 
921    evry_item_free(ev->item);
922    E_FREE(ev);
923 }
924 
925 void
evry_item_changed(Evry_Item * it,int icon,int selected)926 evry_item_changed(Evry_Item *it, int icon, int selected)
927 {
928    Evry_Event_Item_Changed *ev;
929    ev = E_NEW(Evry_Event_Item_Changed, 1);
930    ev->item = it;
931    ev->changed_selection = selected;
932    ev->changed_icon = icon;
933    evry_item_ref(it);
934    ecore_event_add(_evry_events[EVRY_EVENT_ITEM_CHANGED], ev, _cb_free_item_changed, NULL);
935 }
936 
937 static char thumb_buf[4096];
938 static const char hex[] = "0123456789abcdef";
939 
940 char *
evry_util_md5_sum(const char * str)941 evry_util_md5_sum(const char *str)
942 {
943    MD5_CTX ctx;
944    unsigned char hash[MD5_HASHBYTES];
945    int n;
946    char md5out[(2 * MD5_HASHBYTES) + 1];
947    MD5Init (&ctx);
948    MD5Update (&ctx, (unsigned char const *)str,
949               (unsigned)strlen (str));
950    MD5Final (hash, &ctx);
951 
952    for (n = 0; n < MD5_HASHBYTES; n++)
953      {
954         md5out[2 * n] = hex[hash[n] >> 4];
955         md5out[2 * n + 1] = hex[hash[n] & 0x0f];
956      }
957    md5out[2 * n] = '\0';
958 
959    return strdup(md5out);
960 }
961 
962