Tizen 2.1 base
[framework/uifw/ecore.git] / src / lib / ecore_desktop / ecore_desktop_menu.c
1 /*
2  * This conforms with the freedesktop.org Desktop Menu Specification version 0.92
3  *
4  * This is gonna be repetative and slow.  The idea is to first get it to correctly
5  * follow the spec in a way that is easy to check.  Only then can we start to 
6  * optomize into "ugly but fast".
7  *
8  * There are notes at the very end about all those nasty steps we need to do to 
9  * follow the spec.
10  */
11
12 #include <Ecore.h>
13
14 #include "Ecore_Desktop.h"
15 #include "ecore_desktop_private.h"
16 #include "ecore_private.h"
17
18 #include <dirent.h>
19 #include <sys/stat.h>
20 #include <libgen.h>
21
22 //#define DEBUG 1
23
24 struct _ecore_desktop_menu_expand_apps_data
25 {
26    char               *path;
27    Ecore_Hash         *pool;
28    int                 length;
29 };
30
31 struct _ecore_desktop_menu_unxml_data
32 {
33    char               *file;
34    char               *base;
35    char               *path;
36    Ecore_Desktop_Tree *stack, *merge_stack;
37    int                 unallocated, level;
38 };
39
40 struct _ecore_desktop_menu_generate_data
41 {
42    char               *name, *path;
43    Ecore_Desktop_Tree *rules;
44    Ecore_Hash         *pool, *apps;
45    int                 unallocated;
46
47    Ecore_Desktop_Tree *rule;
48    int                 include;
49 };
50
51 struct _ecore_desktop_menu_legacy_data
52 {
53    Ecore_Desktop_Tree *merge;
54    Ecore_Desktop_Tree *current;
55    char               *menu;
56    char               *prefix;
57    char               *path;
58    int                 length, menu_length, level;
59 };
60
61 static int          _ecore_desktop_menu_make_apps(const void *data,
62                                                   Ecore_Desktop_Tree * tree,
63                                                   int element, int level);
64 static Ecore_Desktop_Tree *_ecore_desktop_menu_get0(char *file,
65                                                     Ecore_Desktop_Tree *
66                                                     merge_stack, int level);
67 static Ecore_Desktop_Tree *_ecore_desktop_menu_create_menu(void);
68 static int          _ecore_desktop_menu_unxml(const void *data,
69                                               Ecore_Desktop_Tree * tree,
70                                               int element, int level);
71 static int          _ecore_desktop_menu_check_directory(void *data,
72                                                         const char *path);
73 static int          _ecore_desktop_menu_check_menu(void *data,
74                                                    const char *path);
75 static int          _ecore_desktop_menu_legacy_menu_dir(void *data,
76                                                         const char *path);
77 static int          _ecore_desktop_menu_legacy_menu(void *data,
78                                                     const char *path);
79 static void         _ecore_desktop_menu_unxml_rules(Ecore_Desktop_Tree * rules,
80                                                     Ecore_Desktop_Tree * tree,
81                                                     char type, char sub_type);
82 static void         _ecore_desktop_menu_unxml_moves(Ecore_Desktop_Tree * menu,
83                                                     Ecore_Desktop_Tree * tree);
84 static void         _ecore_desktop_menu_add_dirs(Ecore_Desktop_Tree * tree,
85                                                  Ecore_List * paths, const char *pre,
86                                                  const char *post, char *extra,
87                                                  int element);
88 static int          _ecore_desktop_menu_expand_apps(struct
89                                                     _ecore_desktop_menu_unxml_data
90                                                     *unxml_data, char *app_dir,
91                                                     Ecore_Hash * pool);
92 static int          _ecore_desktop_menu_check_app(void *data, const char *path);
93
94 static int          _ecore_desktop_menu_merge(const void *data,
95                                               Ecore_Desktop_Tree * tree,
96                                               int element, int level);
97 static int          _ecore_desktop_menu_expand_default_dirs(const void *data,
98                                                             Ecore_Desktop_Tree
99                                                             * tree,
100                                                             int element,
101                                                             int level);
102
103 static int          _ecore_desktop_menu_generate(const void *data,
104                                                  Ecore_Desktop_Tree * tree,
105                                                  int element, int level);
106 static int          _ecore_desktop_menu_is_include(const void *data, Ecore_Desktop_Tree * tree, int element, int level __UNUSED__);
107 static void         _ecore_desktop_menu_inherit_apps(void *value,
108                                                      void *user_data);
109 static void         _ecore_desktop_menu_select_app(void *value,
110                                                    void *user_data);
111 static int          _ecore_desktop_menu_apply_rules(struct
112                                                     _ecore_desktop_menu_generate_data
113                                                     *generate_data,
114                                                     Ecore_Desktop_Tree * rule,
115                                                     char *key,
116                                                     Ecore_Desktop * desktop);
117
118 /**
119  * @defgroup Ecore_Desktop_Menu_Group menu Functions
120  *
121  * Functions that deal with freedesktop.org menus.
122  */
123
124 EAPI void
125 ecore_desktop_menu_for_each(void (*func)
126                             (const char *name, const char *path, const char *directory, Ecore_Hash * apps))
127 {
128    char               *menu_file;
129
130    /* Find the main menu file. */
131    menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
132                                              "applications.menu", -1, NULL,
133                                              NULL);
134    if (!menu_file)
135      {
136         /* Try various quirks of various systems and other wms. */
137         menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
138                                                   "kde-applications.menu", -1, NULL,
139                                                   NULL);
140         if (!menu_file)
141           {
142              menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
143                                                   "debian-menu.menu", -1, NULL,
144                                                   NULL);
145              /* FIXME: If all else fails, run debians funky menu generator shit. */
146           }
147      }
148
149    if (menu_file)
150      {
151         Ecore_Desktop_Tree *menus;
152
153         /* convert the xml into menus */
154         menus = ecore_desktop_menu_get(menu_file);
155         if (menus)
156           {
157              /* create the .desktop and order files from the menu */
158              ecore_desktop_tree_foreach(menus, 0, _ecore_desktop_menu_make_apps,
159                                         func);
160 // FIXME: Can't free this just yet, causes major memory corruption.
161 // this leaks a LOT (428,200 bytes in one valgrind run...)
162 //               ecore_desktop_tree_del(menus);
163           }
164         free(menu_file);
165      }
166 }
167
168 static int
169 _ecore_desktop_menu_make_apps(const void *data, Ecore_Desktop_Tree * tree,
170                               int element, int level __UNUSED__)
171 {
172    if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
173      {
174         if (strncmp((char *)tree->elements[element].element, "<MENU ", 6) == 0)
175           {
176              int                 len;
177              char               *path;
178              char               *name;
179              char               *directory;
180              Ecore_Hash         *apps;
181              void                (*func) (char *name, char *path, char *directory,
182                                           Ecore_Hash * apps);
183
184              func = data;
185              name = (char *)tree->elements[element].element;
186              directory = name;
187              directory += 8;
188              path = strchr(directory, '<');
189              path++;
190              directory = strchr(path, '<');
191              directory++;
192              len = strlen(directory);
193              if (len > 2)
194                {
195                   path = strdup(directory);
196                   if (path)
197                     {
198                        path[len - 1] = '\0';
199                        /* FIXME: Figure out what to do if it's just ".directory". */
200                        directory = ecore_desktop_paths_file_find(ecore_desktop_paths_directories, path, 0, NULL, NULL);
201                        free(path);
202                     }
203                   else
204                      directory = NULL;
205                }
206              else
207                 directory = NULL;
208              path = (char *)tree->elements[element + 1].element;
209 //             pool = (Ecore_Hash *) tree->elements[element + 2].element;
210              apps = (Ecore_Hash *) tree->elements[element + 4].element;
211              path = &path[11];
212 #ifdef DEBUG
213              printf("OUTPUTTING MENU - %s \t\t%s \t\t%s\n", path, name, directory);
214 #endif
215              if (func)
216                 func(name, path, directory, apps);
217              free(directory);
218           }
219      }
220    return 0;
221 }
222
223 /**
224  * Decode a freedesktop.org menu XML jungle.
225  *
226  * Using the algorithm specified by freedesktop.org, fully decode
227  * a menu based on an initial menu file.
228  *
229  * @param   file The base file for the menu.
230  * @return  The resulting menu tree.
231  * @ingroup Ecore_Desktop_Menu_Group
232  */
233
234 Ecore_Desktop_Tree *
235 ecore_desktop_menu_get(char *file)
236 {
237    return _ecore_desktop_menu_get0(file, NULL, 0);
238 }
239
240 static Ecore_Desktop_Tree *
241 _ecore_desktop_menu_get0(char *file, Ecore_Desktop_Tree * merge_stack,
242                          int level)
243 {
244    Ecore_Desktop_Tree *menu_xml;
245    struct _ecore_desktop_menu_unxml_data data;
246    int                 oops = 0;
247
248    /* Preperation. */
249    data.stack = ecore_desktop_tree_new(NULL);
250    /* FIXME data.base and data.path leak */
251    data.base = ecore_file_strip_ext(ecore_file_file_get(file));
252    data.path = ecore_file_dir_get(file);
253    if ((level == 0) && (merge_stack == NULL))
254       merge_stack = ecore_desktop_tree_new(NULL);
255 #ifdef DEBUG
256 printf("MENU FILE %d - %s\n", level, file);
257 #endif
258    menu_xml = ecore_desktop_xmlame_get(file);
259    if ((data.stack) && (data.base) && (data.path) && (merge_stack)
260        && (menu_xml))
261      {
262         int                 i;
263
264         data.file = file;
265         data.level = level;
266         data.merge_stack = merge_stack;
267
268         /* Setup the merge stack. */
269         if (merge_stack->size <= level)
270           {
271       /* XXX storing a pointer to a static string probably isn't a good idea. this is most likely the cause of the valgrind error mentioned below. (once this function has exited that memory will no longer be valid) */
272              while (merge_stack->size < level)
273                 ecore_desktop_tree_add(merge_stack, "");
274              ecore_desktop_tree_add(merge_stack, file);
275           }
276         else
277            merge_stack->elements[level].element = file;
278
279         /* Find out if we are looping. */
280         for (i = 0; i < level; i++)
281           {
282              char               *text;
283
284              /* I can safely assume that they are all strings. */
285              text = (char *)merge_stack->elements[i].element;
286 /* printf("---  %s  ---  %s\n", text, file);
287              if (text)
288              if (file) */
289              if (strcmp(text, file) == 0)
290                {
291 /* FIXME: valgrind says -
292 ==5761== Conditional jump or move depends on uninitialised value(s)
293 ==5761==    at 0x1B90565C: strcmp (in /usr/lib/valgrind/vgpreload_memcheck.so)
294 ==5761==    by 0x1BA3E984: _ecore_desktop_menu_get0 (ecore_desktop_menu.c:176)
295 ==5761==    by 0x1BA3FE3F: _ecore_desktop_menu_merge (ecore_desktop_menu.c:1146)
296 ==5761==    by 0x1BA3D5A2: ecore_desktop_tree_foreach (ecore_desktop_tree.c:267)
297  * which is the strcmp just above.  But it doesn't complain about the first two if's, 
298  * or the printf, which I inserted to try and track this down.
299  * No idea what it actually is complaining about, so I'll comment it for future study.
300  *
301  * This is probably caused by the fact that a static string was passed into the
302  * tree. See the comment above where this occurs.
303  */
304                   fprintf(stderr,
305                           "\n### Oops, infinite menu merging loop detected at %s\n",
306                           file);
307                   oops++;
308                }
309           }
310
311         if (oops == 0)
312           {
313              /* Get on with it. */
314              ecore_desktop_tree_foreach(menu_xml, 0, _ecore_desktop_menu_unxml,
315                                         &data);
316              ecore_desktop_tree_foreach(menu_xml, 0, _ecore_desktop_menu_merge,
317                                         &data);
318
319              /* The rest of this is only done after ALL the menus have been merged. */
320              if (level == 0)
321                {
322                   ecore_desktop_tree_foreach(menu_xml, 0,
323                                              _ecore_desktop_menu_expand_default_dirs,
324                                              &data);
325
326 #ifdef DEBUG
327                   ecore_desktop_tree_dump(menu_xml, 0);
328                   printf("\n\n");
329 #endif
330
331                   data.unallocated = FALSE;
332                   ecore_desktop_tree_foreach(menu_xml, 0,
333                                              _ecore_desktop_menu_generate,
334                                              &data);
335
336                   data.unallocated = TRUE;
337                   ecore_desktop_tree_foreach(menu_xml, 0,
338                                              _ecore_desktop_menu_generate,
339                                              &data);
340
341 #ifdef DEBUG
342 //                ecore_desktop_tree_dump(menu_xml, 0);
343 //                printf("\n\n");
344 #endif
345                }
346           }
347      }
348    else
349       oops++;
350
351    if (oops)
352      {
353         E_FN_DEL(ecore_desktop_tree_del, (menu_xml));
354         if (level == 0)
355           {
356              E_FN_DEL(ecore_desktop_tree_del, (merge_stack));
357           }
358    E_FREE(data.path);
359    E_FREE(data.base);
360    E_FN_DEL(ecore_desktop_tree_del, (data.stack));
361      }
362
363
364    return menu_xml;
365 }
366
367 static int
368 _ecore_desktop_menu_unxml(const void *data, Ecore_Desktop_Tree * tree,
369                           int element, int level)
370 {
371    struct _ecore_desktop_menu_unxml_data *unxml_data;
372
373    unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
374    if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
375      {
376         if (strncmp((char *)tree->elements[element].element, "<!", 2) == 0)
377           {
378              tree->elements[element].type =
379                 ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
380              tree->elements[element].element = NULL;
381           }
382         else if (strcmp((char *)tree->elements[element].element, "<Menu") == 0)
383           {
384              Ecore_Desktop_Tree *menu;
385
386              menu = _ecore_desktop_menu_create_menu();
387              if (menu)
388                {
389                   int                 i;
390                   char               *flags = "    ", *name = "", *directory =
391                      "", *menu_path = "";
392                   char                temp[PATH_MAX];
393
394                   flags = (char *)menu->elements[0].element;
395                   flags += 7;
396                   tree->elements[element].element = menu;
397                   tree->elements[element].type =
398                      ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE;
399                   for (i = element + 1; i < tree->size; i++)
400                     {
401                        int                 result = 0;
402
403                        if (tree->elements[i].type ==
404                            ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
405                          {
406                             if (strncmp
407                                 ((char *)tree->elements[i].element, "<!",
408                                  2) == 0)
409                               {
410                                  tree->elements[i].type =
411                                     ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
412                                  tree->elements[i].element = NULL;
413                               }
414                             else
415                                if (strcmp
416                                    ((char *)tree->elements[i].element,
417                                     "<Deleted/") == 0)
418                               {
419                                  flags[1] = 'D';
420                                  result = 1;
421                               }
422                             else
423                                if (strcmp
424                                    ((char *)tree->elements[i].element,
425                                     "<NotDeleted/") == 0)
426                               {
427                                  flags[1] = ' ';
428                                  result = 1;
429                               }
430                             else
431                                if (strcmp
432                                    ((char *)tree->elements[i].element,
433                                     "<OnlyUnallocated/") == 0)
434                               {
435                                  flags[2] = 'O';
436                                  result = 1;
437                               }
438                             else
439                                if (strcmp
440                                    ((char *)tree->elements[i].element,
441                                     "<NotOnlyUnallocated/") == 0)
442                               {
443                                  flags[2] = ' ';
444                                  result = 1;
445                               }
446                             else
447                                if (strcmp
448                                    ((char *)tree->elements[i].element,
449                                     "</Menu") == 0)
450                               {
451                                  result = 1;
452                               }
453                             else
454                               {
455                                  ecore_desktop_tree_extend(menu,
456                                                            (char *)tree->
457                                                            elements[i].element);
458                                  result = 1;
459                               }
460                          }
461                        else if (tree->elements[i].type ==
462                                 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
463                          {
464                             Ecore_Desktop_Tree *sub;
465
466                             sub =
467                                (Ecore_Desktop_Tree *) tree->elements[i].element;
468                             if ((sub) && (sub->size))
469                               {
470                                  if (sub->elements[0].type ==
471                                      ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
472                                    {
473                                       if (strcmp
474                                           ((char *)sub->elements[0].element,
475                                            "<Name") == 0)
476                                         {
477                                            int                 i, length = 0;
478
479                                            name =
480                                               strdup((char *)sub->elements[1].
481                                                      element);
482                                            sprintf(temp,
483                                                    "<MENU <%.4s> <%s> <%s>",
484                                                    flags, name, directory);
485                                            menu->elements[0].element =
486                                               strdup(temp);
487                                            ecore_desktop_tree_track(menu, name);
488                                            ecore_desktop_tree_track(menu,
489                                                                     menu->
490                                                                     elements[0].
491                                                                     element);
492                                            flags =
493                                               (char *)menu->elements[0].element;
494                                            flags += 7;
495                                            /* The rest of this is probably not needed, except to ease debugging. */
496                                            if (unxml_data->stack->size <= level)
497                                              {
498                                                 while (unxml_data->stack->size <
499                                                        level)
500                                                    ecore_desktop_tree_add
501                                                       (unxml_data->stack, "");
502                                                 ecore_desktop_tree_add
503                                                    (unxml_data->stack, name);
504                                              }
505                                            else
506                                               unxml_data->stack->
507                                                  elements[level].element = name;
508                                            temp[0] = '\0';
509                                            for (i = 0; i <= level; i++)
510                                              {
511                                                 char               *text;
512
513                                                 /* I can safely assume that they are all strings. */
514                                                 text =
515                                                    (char *)unxml_data->stack->
516                                                    elements[i].element;
517                                                 if (text[0] != '\0')
518                                                   {
519                                                      sprintf(&temp[length],
520                                                              "%s%s",
521                                                              ((length) ? "/" :
522                                                               ""), text);
523                                                      length +=
524                                                         strlen(text) +
525                                                         ((length) ? 1 : 0);
526                                                   }
527                                              }
528                                            menu_path = strdup(temp);
529                                            sprintf(temp, "<MENU_PATH %s",
530                                                    menu_path);
531                                            menu->elements[1].element =
532                                               strdup(temp);
533                                            ecore_desktop_tree_track(menu,
534                                                                     menu_path);
535                                            ecore_desktop_tree_track(menu,
536                                                                     menu->
537                                                                     elements[1].
538                                                                     element);
539                                            result = 1;
540                                         }
541                                       /* FIXME: Move this to later in the sequence. */
542                                       else
543                                          if (strcmp
544                                              ((char *)sub->elements[0].element,
545                                               "<Directory") == 0)
546                                         {
547                                            directory =
548                                               strdup((char *)sub->elements[1].
549                                                      element);
550                                            sprintf(temp,
551                                                    "<MENU <%.4s> <%s> <%s>",
552                                                    flags, name, directory);
553                                            menu->elements[0].element =
554                                               strdup(temp);
555                                            ecore_desktop_tree_track(menu,
556                                                                     directory);
557                                            ecore_desktop_tree_track(menu,
558                                                                     menu->
559                                                                     elements[0].
560                                                                     element);
561                                            flags =
562                                               (char *)menu->elements[0].element;
563                                            flags += 7;
564                                            result = 1;
565                                         }
566                                       else
567                                          if (strcmp
568                                              ((char *)sub->elements[0].element,
569                                               "<Menu") == 0)
570                                         {
571                                            _ecore_desktop_menu_unxml(data, sub,
572                                                                      0,
573                                                                      level + 1);
574                                            ecore_desktop_tree_add_child(menu,
575                                                                         (Ecore_Desktop_Tree
576                                                                          *)
577                                                                         sub->
578                                                                         elements
579                                                                         [0].
580                                                                         element);
581                                            /* FIXME: Dunno if this causes a memory leak, but for now we play it safe. */
582                                            tree->elements[i].type =
583                                               ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
584                                            tree->elements[i].element = NULL;
585 //                                                 result = 1;
586                                         }
587                                       else
588                                          if (strcmp
589                                              ((char *)sub->elements[0].element,
590                                               "<Move") == 0)
591                                         {
592                                            _ecore_desktop_menu_unxml_moves
593                                               (menu, sub);
594                                            result = 1;
595                                         }
596                                       else
597                                         {
598                                            if ((sub->size == 3)
599                                                && (sub->elements[1].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
600                                                && (((char *)sub->elements[1].element)[0] != '<'))
601                                              {
602                                                 char temp[PATH_MAX];
603
604                                                 snprintf(temp, PATH_MAX, "%s %s",
605                                                         (char *)sub->elements[0].element,
606                                                         (char *)sub->elements[1].element);
607                                                 ecore_desktop_tree_extend(menu, temp);
608                                                 result = 1;
609                                              }
610                                            else
611                                              {
612                                                 ecore_desktop_tree_add_child
613                                                    (menu, sub);
614                                                 tree->elements[i].type =
615                                                    ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
616                                                 tree->elements[i].element =
617                                                    NULL;
618                                              }
619                                         }
620                                    }
621                               }
622                          }
623                        if (result)
624                          {
625                             if (tree->elements[i].type ==
626                                 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
627                                ecore_desktop_tree_del((Ecore_Desktop_Tree *)
628                                                       tree->elements[i].
629                                                       element);
630                             tree->elements[i].type =
631                                ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
632                             tree->elements[i].element = NULL;
633                          }
634                     }
635
636                   /* Add it if it has not been deleted. */
637                   if (flags[1] != 'D')
638                     {
639                        tree->elements[element].element = menu;
640                        tree->elements[element].type =
641                           ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE;
642                     }
643                }
644           }
645      }
646    return 0;
647 }
648
649 static Ecore_Desktop_Tree *
650 _ecore_desktop_menu_create_menu()
651 {
652    Ecore_Desktop_Tree *menu, *rules;
653    Ecore_Hash         *pool, *apps;
654
655    menu = ecore_desktop_tree_new(NULL);
656    rules = ecore_desktop_tree_new(NULL);
657    pool = ecore_hash_new(ecore_str_hash, ecore_str_compare);
658    apps = ecore_hash_new(ecore_str_hash, ecore_str_compare);
659    if ((menu) && (rules) && (pool) && (apps))
660      {
661         ecore_hash_free_key_cb_set(pool, free);
662         ecore_hash_free_value_cb_set(pool, free);
663         ecore_hash_free_key_cb_set(apps, free);
664         ecore_hash_free_value_cb_set(apps, free);
665         ecore_desktop_tree_extend(menu, "<MENU <    > <> <>");
666         ecore_desktop_tree_extend(menu, "<MENU_PATH ");
667         ecore_desktop_tree_add_hash(menu, pool);
668         ecore_desktop_tree_add_child(menu, rules);
669         ecore_desktop_tree_add_hash(menu, apps);
670      }
671    else
672      {
673         if (apps)
674            ecore_hash_destroy(apps);
675         if (pool)
676            ecore_hash_destroy(pool);
677         if (rules)
678            ecore_desktop_tree_del(rules);
679         if (menu)
680            ecore_desktop_tree_del(menu);
681         menu = NULL;
682      }
683
684    return menu;
685 }
686
687 static int
688 _ecore_desktop_menu_check_directory(void *data, const char *path)
689 {
690    char               *p;
691    Ecore_Desktop_Tree *merge;
692
693    merge = (Ecore_Desktop_Tree *) data;
694    p = strrchr(path, '.');
695    if (p)
696      {
697         if (strcmp(p, ".directory") == 0)
698           {
699              char                merge_file[PATH_MAX];
700
701              sprintf(merge_file, "<Directory %s", path);
702              ecore_desktop_tree_extend(merge, merge_file);
703           }
704      }
705
706    return 0;
707 }
708
709 static int
710 _ecore_desktop_menu_check_menu(void *data, const char *path)
711 {
712    char               *p;
713    Ecore_Desktop_Tree *merge;
714
715    merge = (Ecore_Desktop_Tree *) data;
716    p = strrchr(path, '.');
717    if (p)
718      {
719         if (strcmp(p, ".menu") == 0)
720           {
721              char                merge_file[PATH_MAX];
722
723              sprintf(merge_file, "<MergeFile type=\"path\" %s", path);
724              ecore_desktop_tree_extend(merge, merge_file);
725           }
726      }
727
728    return 0;
729 }
730
731 static int
732 _ecore_desktop_menu_legacy_menu_dir(void *data, const char *path)
733 {
734    struct _ecore_desktop_menu_legacy_data *legacy_data;
735    Ecore_Desktop_Tree *menu;
736    char               *temp_path;
737    int                 start;
738    int                 i, count = 0;
739
740    legacy_data = (struct _ecore_desktop_menu_legacy_data *)data;
741    for (i = legacy_data->length; path[i] != '\0'; i++)
742      {
743         if (path[i] == '/')
744            count++;
745      }
746    if (count == 1)
747      {
748         legacy_data->current = legacy_data->merge;
749         legacy_data->level = 0;
750      }
751    else if (count <= legacy_data->level)
752      {
753         if ((legacy_data->current) && (legacy_data->current->parent))
754           {
755              legacy_data->current = legacy_data->current->parent;
756              legacy_data->level--;
757           }
758      }
759
760    start = legacy_data->length;
761    count = 0;
762    temp_path = strdup(path);
763    if (temp_path)
764      {
765         for (i = legacy_data->length; temp_path[i] != '\0'; i++)
766           {
767              if (temp_path[i] == '/')
768                {
769                   if (count >= legacy_data->level)
770                     {
771                        temp_path[i] = '\0';
772                        menu = _ecore_desktop_menu_create_menu();
773                        if (menu)
774                          {
775                             char                temp[PATH_MAX];
776
777                             sprintf(temp, "<MENU <   L> <%s> <>",
778                                     &temp_path[start]);
779                             menu->elements[0].element = strdup(temp);
780                             ecore_desktop_tree_track(menu,
781                                                      menu->elements[0].element);
782                             sprintf(temp, "<MENU_PATH %s/%s", legacy_data->menu,
783                                     &temp_path[legacy_data->length]);
784                             menu->elements[1].element = strdup(temp);
785                             ecore_desktop_tree_track(menu,
786                                                      menu->elements[1].element);
787
788                             ecore_desktop_tree_add_child(legacy_data->current,
789                                                          menu);
790                             /*  This is not needed, but if it was, this is where it would go.
791                              * sprintf(temp, "<AppDir %s/", temp_path);
792                              * ecore_desktop_tree_extend(menu, temp);
793                              */
794                             sprintf(temp, "<DirectoryDir %s/", temp_path);
795                             ecore_desktop_tree_extend(menu, temp);
796
797                             legacy_data->current = menu;
798                             legacy_data->level++;
799                          }
800                        temp_path[i] = '/';
801                     }
802                   start = i + 1;
803                   count++;
804                }
805           }
806         free(temp_path);
807      }
808
809    legacy_data->level = count;
810
811    return 0;
812 }
813
814 static int
815 _ecore_desktop_menu_legacy_menu(void *data, const char *path)
816 {
817    struct _ecore_desktop_menu_legacy_data *legacy_data;
818    char               *menu_path;
819    char               *file;
820    char                temp[PATH_MAX];
821    int                 i, count = 0, menu_count = 0;
822
823    legacy_data = (struct _ecore_desktop_menu_legacy_data *)data;
824    if ((legacy_data->current) && (legacy_data->current->size > 0) &&
825        (legacy_data->current->elements[1].type ==
826         ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING))
827      {
828         menu_path = (char *)legacy_data->current->elements[1].element;
829         menu_path += 12 + legacy_data->menu_length;
830      }
831    else
832      {
833         menu_path = "";
834         printf("  PROBLEM IN LEGACYDIR FILE - %s - %s  %s\n",
835                legacy_data->prefix, legacy_data->path,
836                &path[legacy_data->length]);
837      }
838
839    for (i = legacy_data->length; path[i] != '\0'; i++)
840      {
841         if (path[i] == '/')
842            count++;
843      }
844    for (i = 0; menu_path[i] != '\0'; i++)
845      {
846         if (menu_path[i] == '/')
847            menu_count++;
848      }
849    /* FIXME: The (legacy_data->current) test is just to patch a seggie, find out why. */
850    while ((menu_count >= count) && (legacy_data->current))
851      {
852         legacy_data->current = legacy_data->current->parent;
853         menu_count--;
854      }
855    if ((legacy_data->current) && (legacy_data->current->size > 0) &&
856        (legacy_data->current->elements[1].type ==
857         ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING))
858      {
859         menu_path = (char *)legacy_data->current->elements[1].element;
860         menu_path += 12 + legacy_data->menu_length;
861      }
862    else
863      {
864         /* FIXME:  Bugger, a corner case.
865          * If this is .directory, then maybe ignore it?
866          * If this is a desktop, we need to put it into the pool of the menu that legacy_data->merge will get merged into.
867          */
868         menu_path = "";
869         printf("  LEGACYDIR FILE - %s - %s  %s\n", legacy_data->prefix,
870                legacy_data->path, &path[legacy_data->length]);
871         return 0;
872      }
873
874    menu_count = strlen(menu_path);
875    if (menu_count)
876       menu_count++;
877    file = (char *)&path[legacy_data->length + menu_count];
878    count = strlen(file);
879
880    if (strcmp(".directory", file) == 0)
881      {
882         menu_path = (char *)legacy_data->current->elements[0].element;
883         menu_count = strlen(menu_path);
884         menu_path[menu_count - 3] = '\0';
885         sprintf(temp, "%s <.directory>", menu_path);
886         legacy_data->current->elements[0].element = strdup(temp);
887         ecore_desktop_tree_track(legacy_data->current,
888                                  legacy_data->current->elements[0].element);
889      }
890    else if (strcmp(".desktop", &file[count - 8]) == 0)
891      {
892         Ecore_Hash         *pool;
893         Ecore_Desktop_Tree *rules;
894
895         pool = (Ecore_Hash *) legacy_data->current->elements[2].element;
896         rules =
897            (Ecore_Desktop_Tree *) legacy_data->current->elements[3].element;
898         if (rules->size == 0)
899           {
900              Ecore_Desktop_Tree *new_rules;
901
902              new_rules = ecore_desktop_tree_new(NULL);
903              if (new_rules)
904                 ecore_desktop_tree_add_child(rules, new_rules);
905           }
906         sprintf(temp, "%s%s", legacy_data->prefix, file);
907         ecore_hash_set(pool, strdup(temp), strdup(path));
908 #ifdef DEBUG
909 //      printf
910 //         ("POOLING - _ecore_desktop_menu_legacy_menu(void *data, %s) - %s - %s\n",
911 //          path, file, temp);
912 #endif
913         if (rules->size > 0)
914           {
915              rules = (Ecore_Desktop_Tree *) rules->elements[0].element;
916              sprintf(temp, "IOF %s%s", legacy_data->prefix, file);
917              ecore_desktop_tree_extend(rules, temp);
918           }
919      }
920 #ifdef DEBUG
921    else
922       printf("PROBLEM - _ecore_desktop_menu_legacy_menu(void *data, %s) - %s\n",
923              path, file);
924 #endif
925
926    return 0;
927 }
928
929 static void
930 _ecore_desktop_menu_unxml_rules(Ecore_Desktop_Tree * rules,
931                                 Ecore_Desktop_Tree * tree, char type,
932                                 char sub_type)
933 {
934    int                 i;
935    char                temp[PATH_MAX];
936
937    for (i = 0; i < tree->size; i++)
938      {
939         if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
940           {
941              /* FIXME <All /> or <All foo="bar"> would be valid input (which this won't catch) */
942              if (strcmp((char *)tree->elements[i].element, "<All/") == 0)
943                {
944                   sprintf(temp, "%c%cA", type, sub_type);
945                   ecore_desktop_tree_extend(rules, temp);
946                }
947              else if (strcmp((char *)tree->elements[i].element, "<Filename") ==
948                       0)
949                {
950                   sprintf(temp, "%c%cF %s", type, sub_type,
951                           (char *)tree->elements[i + 1].element);
952                   ecore_desktop_tree_extend(rules, temp);
953                }
954              else if (strcmp((char *)tree->elements[i].element, "<Category") ==
955                       0)
956                {
957                   sprintf(temp, "%c%cC %s", type, sub_type,
958                           (char *)tree->elements[i + 1].element);
959                   ecore_desktop_tree_extend(rules, temp);
960                }
961              else if (strcmp((char *)tree->elements[i].element, "<Or") == 0)
962                {
963                   _ecore_desktop_menu_unxml_rules(rules,
964                                                   (Ecore_Desktop_Tree *) tree->
965                                                   elements[i + 1].element,
966                                                   type, sub_type);
967                }
968              else if ((strcmp((char *)tree->elements[i].element, "<And") == 0)
969                       || (strcmp((char *)tree->elements[i].element, "<Not") ==
970                           0))
971                {
972                   char                this_type;
973                   Ecore_Desktop_Tree *sub;
974
975                   this_type = ((char *)tree->elements[i].element)[1];
976                   sub = ecore_desktop_tree_new(NULL);
977                   if (sub)
978                     {
979                        ecore_desktop_tree_add_child(rules, sub);
980                        for (i++; i < tree->size; i++)
981                          {
982                             if (tree->elements[i].type ==
983                                 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
984                                _ecore_desktop_menu_unxml_rules(sub,
985                                                                (Ecore_Desktop_Tree
986                                                                 *) tree->
987                                                                elements[i].
988                                                                element, type,
989                                                                this_type);
990                          }
991                     }
992                }
993           }
994         else if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
995           {
996              _ecore_desktop_menu_unxml_rules(rules,
997                                              (Ecore_Desktop_Tree *) tree->
998                                              elements[i].element, type,
999                                              sub_type);
1000           }
1001      }
1002 }
1003
1004 static void
1005 _ecore_desktop_menu_unxml_moves(Ecore_Desktop_Tree * menu,
1006                                 Ecore_Desktop_Tree * tree)
1007 {
1008    int                 i;
1009    char               *old = NULL;
1010    char               *new = NULL;
1011
1012    for (i = 0; i < tree->size; i++)
1013      {
1014         if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1015           {
1016              Ecore_Desktop_Tree *sub;
1017
1018              sub = (Ecore_Desktop_Tree *) tree->elements[i].element;
1019              if ((sub) && (sub->size))
1020                {
1021                   if (sub->elements[0].type ==
1022                       ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1023                     {
1024                        if (strcmp((char *)sub->elements[0].element, "<Old") ==
1025                            0)
1026                           old = strdup((char *)sub->elements[1].element);
1027                        if (strcmp((char *)sub->elements[0].element, "<New") ==
1028                            0)
1029                           new = strdup((char *)sub->elements[1].element);
1030                     }
1031                }
1032           }
1033         if ((old) && (new))
1034           {
1035              char                temp[PATH_MAX * 2];
1036
1037              sprintf(temp, "<MOVE <%s> <%s>", old, new);
1038              ecore_desktop_tree_extend(menu, temp);
1039           }
1040
1041         if (old) free(old);
1042         old = NULL;
1043         if (new) free(new);
1044         new = NULL;
1045      }
1046 }
1047
1048 static void
1049 _ecore_desktop_menu_add_dirs(Ecore_Desktop_Tree * tree, Ecore_List * paths,
1050                              const char *pre, const char *post __UNUSED__, char *extra, int element __UNUSED__)
1051 {
1052    char                t[PATH_MAX], *this_path;
1053
1054    /* reverse the order of the dirs. */
1055    ecore_list_first_goto(paths);
1056    while ((this_path = ecore_list_next(paths)) != NULL)
1057      {
1058         if (extra)
1059            sprintf(t, "%s %s%s-merged/", pre, this_path, extra);
1060         else
1061            sprintf(t, "%s %s", pre, this_path);
1062         if (tree)
1063            ecore_desktop_tree_extend(tree, t);
1064      }
1065 }
1066
1067 static int
1068 _ecore_desktop_menu_expand_apps(struct _ecore_desktop_menu_unxml_data
1069                                 *unxml_data, char *app_dir, Ecore_Hash * pool)
1070 {
1071    if (pool)
1072      {
1073         struct _ecore_desktop_menu_expand_apps_data our_data;
1074         char                dir[PATH_MAX];
1075
1076         our_data.pool = pool;
1077         sprintf(dir, "%s", app_dir);
1078         if (dir[0] != '/')
1079            sprintf(dir, "%s/%s", unxml_data->path, app_dir);
1080         our_data.path = dir;
1081         our_data.length = strlen(dir);
1082 #ifdef DEBUG
1083         printf
1084            ("EXPANDING - _ecore_desktop_menu_expand_apps(unxml_data, %s) - %s\n",
1085             app_dir, dir);
1086 #endif
1087         ecore_desktop_paths_recursive_search(dir, NULL, -1, NULL,
1088                                              _ecore_desktop_menu_check_app,
1089                                              &our_data);
1090      }
1091    return 0;
1092 }
1093
1094 static int
1095 _ecore_desktop_menu_check_app(void *data, const char *path)
1096 {
1097    char               *p;
1098    struct _ecore_desktop_menu_expand_apps_data *our_data;
1099
1100    our_data = (struct _ecore_desktop_menu_expand_apps_data *)data;
1101    p = strrchr(path, '.');
1102    if (p)
1103      {
1104         if (strcmp(p, ".desktop") == 0)
1105           {
1106              int                 i;
1107              char               *file;
1108
1109              file = strdup(path + our_data->length);
1110              if ((file) && (path))
1111                {
1112                   for (i = 0; file[i] != '\0'; i++)
1113                      if (file[i] == '/')
1114                         file[i] = '-';
1115                   ecore_hash_set(our_data->pool, file, strdup(path));
1116 #ifdef DEBUG
1117 //                printf
1118 //                   ("POOLING - _ecore_desktop_menu_check_app(void *data, %s) - %s\n",
1119 //                    path, file);
1120 #endif
1121                }
1122           }
1123 #ifdef DEBUG
1124         else
1125            printf("PROBLEM - _ecore_desktop_menu_check_app(void *data, %s)\n",
1126                   path);
1127 #endif
1128      }
1129
1130    return 0;
1131 }
1132
1133 static int
1134 _ecore_desktop_menu_merge(const void *data, Ecore_Desktop_Tree * tree,
1135                           int element, int level)
1136 {
1137    struct _ecore_desktop_menu_unxml_data *unxml_data;
1138    Ecore_Desktop_Tree *merge;
1139    int                 result = 0;
1140
1141    unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
1142    merge = ecore_desktop_tree_new(NULL);
1143    if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1144      {
1145         char               *string;
1146
1147         string = (char *)tree->elements[element].element;
1148         if (strcmp(string, "<DefaultMergeDirs/") == 0)
1149           {
1150              if (unxml_data->base)
1151                 _ecore_desktop_menu_add_dirs(merge, ecore_desktop_paths_menus,
1152                                              "<MergeDir", "</MergeDir",
1153                                              unxml_data->base, element);
1154              result = 1;
1155           }
1156         else if (strcmp(string, "<KDELegacyDirs/") == 0)
1157           {
1158              _ecore_desktop_menu_add_dirs(merge,
1159                                           ecore_desktop_paths_kde_legacy,
1160                                           "<LegacyDir prefix=\"kde-\"",
1161                                           "</LegacyDir", NULL, element);
1162              result = 1;
1163           }
1164         else if (strncmp(string, "<MergeDir ", 10) == 0)
1165           {
1166              char                merge_path[PATH_MAX];
1167
1168              if (string[10] == '/')
1169                 sprintf(merge_path, "%s", &string[10]);
1170              else
1171                 sprintf(merge_path, "%s%s", unxml_data->path, &string[10]);
1172              ecore_desktop_paths_recursive_search(merge_path, NULL, -1, NULL,
1173                                                   _ecore_desktop_menu_check_menu,
1174                                                   merge);
1175              result = 1;
1176           }
1177         else if (strncmp(string, "<LegacyDir ", 11) == 0)
1178           {
1179              char                merge_path[PATH_MAX];
1180              struct _ecore_desktop_menu_legacy_data legacy_data;
1181
1182              string += 11;
1183              legacy_data.prefix = NULL;
1184              legacy_data.merge = merge;
1185              legacy_data.current = merge;
1186              legacy_data.level = 0;
1187              legacy_data.menu = (char *)tree->elements[0].element;
1188              legacy_data.menu += 14;
1189              legacy_data.menu_length =
1190                 strchr(legacy_data.menu, '>') - legacy_data.menu;
1191              legacy_data.menu[legacy_data.menu_length] = '\0';
1192              if (strncmp(string, "prefix=\"", 8) == 0)
1193                {
1194                   string += 8;
1195                   legacy_data.prefix = string;
1196                   while ((*string != '"') && (*string != '\0'))
1197                      string++;
1198                   if (*string != '\0')
1199                      *string++ = '\0';
1200                   while ((*string == ' ') && (*string != '\0'))
1201                      string++;
1202                }
1203              if (string[0] == '/')
1204                 sprintf(merge_path, "%s", string);
1205              else
1206                 sprintf(merge_path, "%s%s", unxml_data->path, string);
1207              legacy_data.path = merge_path;
1208              legacy_data.length = strlen(merge_path);
1209 #ifdef DEBUG
1210              printf("<LEGACYDIR> - %s - %s\n", legacy_data.prefix, merge_path);
1211 #endif
1212              ecore_desktop_paths_recursive_search(merge_path, NULL, -1,
1213                                                   _ecore_desktop_menu_legacy_menu_dir,
1214                                                   _ecore_desktop_menu_legacy_menu,
1215                                                   &legacy_data);
1216              legacy_data.menu[legacy_data.menu_length] = '>';
1217              result = 1;
1218           }
1219         else if (strncmp(string, "<MergeFile ", 11) == 0)
1220           {
1221              char                merge_path[PATH_MAX];
1222              int                 path_type = 1;
1223
1224              /* FIXME: need to weed out duplicate <MergeFile's, use the last one. */
1225              string += 11;
1226              if (strncmp(string, "type=\"", 6) == 0)
1227                {
1228                   string += 6;
1229                   if (strncmp(string, "parent\"", 7) == 0)
1230                      path_type = 0;
1231                   while ((*string != '"') && (*string != '\0'))
1232                      string++;
1233                   if (*string != '\0')
1234                      string++;
1235                   while ((*string == ' ') && (*string != '\0'))
1236                      string++;
1237                }
1238              if (path_type)
1239                {
1240                   if (string[0] == '/')
1241                      snprintf(merge_path, PATH_MAX, "%s", string);
1242                   else
1243                      snprintf(merge_path, PATH_MAX, "%s/%s", unxml_data->path, string);
1244                }
1245              else               /* This is a parent type MergeFile. */
1246                {
1247                   char *xdg_path;
1248                   int found = -1;
1249
1250                   /* The spec is a little unclear, and the examples may look like they
1251                    * contradict the description, but it all makes sense if you cross
1252                    * reference it with the XDG Base Directory Specification (version 0.6).
1253                    * To make things harder, parent type MergeFiles never appear on my box.
1254                    *
1255                    * What you do is this.
1256                    *
1257                    * Just plain ignore the specified path in the MergeFile element, it's for 
1258                    * legacy apps that don't understand parent types.
1259                    *
1260                    * Take the XDG_CONFIG_DIRS stuff as a whole ($XDG_CONFIG_HOME, then 
1261                    * $XDG_CONFIG_DIRS), in this code that will be ecore_desktop_paths_config.
1262                    *
1263                    * If this menu file is from one of the directories in ecore_desktop_paths_config,
1264                    * scan the rest of ecore_desktop_paths_config looking for the new menu.  In other 
1265                    * words start searching in the next ecore_desktop_paths_config entry after the one
1266                    * that this menu is in.
1267                    *
1268                    * The file to look for is the path to this menu with the portion from
1269                    * ecore_desktop_paths_config stripped off the beginning.  For instance, the top level
1270                    * menu file is typically /etc/xdg/menus/applications.menu, and /etc/xdg is
1271                    * typically in ecore_desktop_paths_config, so search for menus/applications.menu.
1272                    *
1273                    * If this menu file is NOT from one of the directories in ecore_desktop_paths_menus,
1274                    * insert nothing.
1275                    *
1276                    * The first one found wins, if none are found, don't merge anything.
1277                    */
1278
1279                   merge_path[0] = '\0';
1280                   ecore_list_first_goto(ecore_desktop_paths_config);
1281                   while ((xdg_path = ecore_list_next(ecore_desktop_paths_config)) != NULL)
1282                     {
1283                        if (found < 0)
1284                          {
1285                             int length = strlen(xdg_path);
1286
1287                             if (strncmp(xdg_path, unxml_data->file, length) == 0)
1288                                found = length;
1289                          }
1290                        else
1291                          {
1292                             snprintf(merge_path, PATH_MAX, "%s%s", xdg_path, &(unxml_data->file)[found]);
1293                             if (ecore_file_exists(merge_path))
1294                                break;
1295                             merge_path[0] = '\0';
1296                          }
1297                     }
1298
1299                }
1300              if (merge_path[0] != '\0')
1301                {
1302                   Ecore_Desktop_Tree *new_menu;
1303
1304                   new_menu =
1305                      _ecore_desktop_menu_get0(merge_path,
1306                                               unxml_data->merge_stack,
1307                                               level + 1);
1308                   if (new_menu)
1309                     {
1310                        if (new_menu->size > 1)
1311                          {
1312                             if (new_menu->elements[1].type ==
1313                                 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1314                               {
1315                                  new_menu =
1316                                     (Ecore_Desktop_Tree *) new_menu->
1317                                     elements[1].element;
1318                                  if (new_menu->size > 0)
1319                                    {
1320                                       if (new_menu->elements[0].type ==
1321                                           ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1322                                         {
1323                                            merge =
1324                                               (Ecore_Desktop_Tree *) new_menu->
1325                                               elements[0].element;
1326                                            ecore_desktop_tree_remove(merge, 0);
1327                                            ecore_desktop_tree_remove(merge, 1);
1328                                            ecore_desktop_tree_remove(merge, 2);
1329                                            ecore_desktop_tree_remove(merge, 3);
1330                                            ecore_desktop_tree_remove(merge, 4);
1331                                            /* FIXME: The MENU_PATHs need to be prefixed. */
1332                                         }
1333                                       else
1334                                          printf
1335                                             ("FUCK an error in _ecore_desktop_menu_merge(%s)\n",
1336                                              merge_path);
1337                                    }
1338                                  else
1339                                     printf
1340                                        ("FUCK another error in _ecore_desktop_menu_merge(%s)\n",
1341                                         merge_path);
1342                               }
1343                             else
1344                                printf
1345                                   ("FUCK ME! An error in _ecore_desktop_menu_merge(%s)\n",
1346                                    merge_path);
1347                          }
1348                     }
1349                }
1350              result = 1;
1351           }
1352      }
1353
1354    if (result)
1355      {
1356         if ((merge) && (merge->size))
1357            ecore_desktop_tree_merge(tree, element + 1, merge);
1358
1359         tree->elements[element].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
1360         tree->elements[element].element = NULL;
1361      }
1362
1363    return 0;
1364 }
1365
1366 static int
1367 _ecore_desktop_menu_expand_default_dirs(const void *data,
1368                                         Ecore_Desktop_Tree * tree, int element,
1369                                         int level __UNUSED__)
1370 {
1371    struct _ecore_desktop_menu_unxml_data *unxml_data;
1372    Ecore_Desktop_Tree *merge;
1373    int                 result = 0;
1374
1375    unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
1376    merge = ecore_desktop_tree_new(NULL);
1377    if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1378      {
1379         char               *string;
1380
1381         string = (char *)tree->elements[element].element;
1382         if (strcmp(string, "<DefaultAppDirs/") == 0)
1383           {
1384              _ecore_desktop_menu_add_dirs(merge, ecore_desktop_paths_desktops,
1385                                           "<AppDir", "</AppDir", NULL, element);
1386              result = 1;
1387           }
1388         else if (strcmp(string, "<DefaultDirectoryDirs/") == 0)
1389           {
1390              _ecore_desktop_menu_add_dirs(merge,
1391                                           ecore_desktop_paths_directories,
1392                                           "<DirectoryDir", "</DirectoryDir",
1393                                           NULL, element);
1394              result = 1;
1395           }
1396      }
1397    if (result)
1398      {
1399         if ((merge) && (merge->size))
1400            ecore_desktop_tree_merge(tree, element + 1, merge);
1401
1402         tree->elements[element].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
1403         tree->elements[element].element = NULL;
1404      }
1405
1406    return 0;
1407 }
1408
1409 static int
1410 _ecore_desktop_menu_generate(const void *data, Ecore_Desktop_Tree * tree,
1411                              int element, int level)
1412 {
1413    struct _ecore_desktop_menu_unxml_data *unxml_data;
1414
1415    unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
1416    if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1417      {
1418         if (strncmp((char *)tree->elements[element].element, "<MENU ", 6) == 0)
1419           {
1420              int                 i;
1421              struct _ecore_desktop_menu_generate_data generate_data;
1422
1423              generate_data.unallocated = unxml_data->unallocated;
1424              generate_data.name = (char *)tree->elements[element].element;
1425              generate_data.path = (char *)tree->elements[element + 1].element;
1426              generate_data.pool = (Ecore_Hash *) tree->elements[element + 2].element;
1427              generate_data.rules = (Ecore_Desktop_Tree *) tree->elements[element + 3].element;
1428              generate_data.apps = (Ecore_Hash *) tree->elements[element + 4].element;
1429
1430              /* generate and inherit the pools on the first pass, and preparse the include/exclude logic. */
1431              if (!generate_data.unallocated)
1432                {
1433                   int                 i;
1434
1435                   for (i = element + 5; i < tree->size; i++)
1436                     {
1437                        int                 result = 0;
1438                        char               *string;
1439
1440                        if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1441                          {
1442                             string = (char *)tree->elements[i].element;
1443                             if (strncmp(string, "<AppDir ", 8) == 0)
1444                               {
1445                                  _ecore_desktop_menu_expand_apps(unxml_data, &string[8], generate_data.pool);
1446                                  result = 1;
1447                               }
1448                             else if (strncmp(string, "<DirectoryDir ", 14) == 0)
1449                               {
1450                                  char                merge_path[PATH_MAX];
1451                                  Ecore_Desktop_Tree *merge;
1452
1453                                  if (string[14] == '/')
1454                                     sprintf(merge_path, "%s", &string[14]);
1455                                  else
1456                                     sprintf(merge_path, "%s%s", unxml_data->path, &string[14]);
1457                                  merge = ecore_desktop_tree_new(NULL);
1458                                  if (merge)
1459                                    {
1460                                       ecore_desktop_paths_recursive_search(merge_path, NULL, -1, NULL, _ecore_desktop_menu_check_directory, merge);
1461                                       ecore_desktop_tree_merge(tree, i + 1, merge);
1462                                    }
1463                                  result = 1;
1464                               }
1465                          }
1466                        else if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1467                          {
1468                             Ecore_Desktop_Tree *sub;
1469
1470                             sub = (Ecore_Desktop_Tree *) tree->elements[i].element;
1471                             if ((sub) && (sub->size))
1472                               {
1473                                  if (sub->elements[0].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1474                                    {
1475                                       string = (char *)sub->elements[0].element;
1476                                       if ((strcmp(string, "<Include") == 0) || (strcmp(string, "<Exclude") == 0))
1477                                         {
1478                                            Ecore_Desktop_Tree *new_sub;
1479
1480                                            new_sub = ecore_desktop_tree_new(NULL);
1481                                            if (new_sub)
1482                                              {
1483                                                 ecore_desktop_tree_add_child(generate_data.rules, new_sub);
1484                                                 _ecore_desktop_menu_unxml_rules(new_sub, sub, string[1], 'O');
1485                                              }
1486                                            result = 1;
1487                                         }
1488                                    }
1489                               }
1490                          }
1491
1492                        if (result)
1493                          {
1494                             tree->elements[i].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
1495                             tree->elements[i].element = NULL;
1496                          }
1497                     }
1498
1499                   if (unxml_data->stack->size <= level)
1500                     {
1501                        while (unxml_data->stack->size < level)
1502                           ecore_desktop_tree_add_hash(unxml_data->stack, generate_data.pool);
1503                        ecore_desktop_tree_add_hash(unxml_data->stack, generate_data.pool);
1504                     }
1505                   else
1506                     {
1507                        unxml_data->stack->elements[level].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_HASH;
1508                        unxml_data->stack->elements[level].element = generate_data.pool;
1509                     }
1510                   for (i = level - 1; i >= 0; i--)
1511                     {
1512                        if (unxml_data->stack->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_HASH)
1513                          {
1514                             Ecore_Hash         *ancestor;
1515
1516                             ancestor = (Ecore_Hash *) unxml_data->stack->elements[i].element;
1517                             ecore_hash_for_each_node(ancestor, _ecore_desktop_menu_inherit_apps, generate_data.pool);
1518                          }
1519                     }
1520                }
1521
1522              /* Process the rules. */
1523              if (generate_data.name[9] == (generate_data.unallocated ? 'O' : ' '))
1524                {
1525 #ifdef DEBUG
1526                   printf("MAKING MENU - %s \t\t%s\n", generate_data.path, generate_data.name);
1527                   ecore_desktop_tree_dump(generate_data.rules, 0);
1528                   printf("\n\n");
1529 #endif
1530                   for (i = 0; i < generate_data.rules->size; i++)
1531                     {
1532                        if (generate_data.rules->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1533                          {
1534                             generate_data.rule = (Ecore_Desktop_Tree *) generate_data.rules->elements[i].element;
1535                             if (generate_data.rule->size > 0)
1536                               {
1537                                  char type = 'I';
1538
1539                                  /* Find out if this is an include or an exclude.  This info may be way down in the tree. */
1540                                  if (generate_data.rule->elements[0].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1541                                     ecore_desktop_tree_foreach(generate_data.rule, 0, _ecore_desktop_menu_is_include, &type);
1542                                  if (type == 'I')
1543                                    {
1544                                       generate_data.include = TRUE;
1545                                       ecore_hash_for_each_node(generate_data.pool, _ecore_desktop_menu_select_app, &generate_data);
1546                                    }
1547                                  else
1548                                    {
1549                                       generate_data.include = FALSE;
1550                                       ecore_hash_for_each_node(generate_data.apps, _ecore_desktop_menu_select_app, &generate_data);
1551                                    }
1552                               }
1553                          }
1554                        else
1555                           printf("Fuck, a bug in _ecore_desktop_menus.\n");
1556                     }
1557                }
1558           }
1559      }
1560    return 0;
1561 }
1562
1563 static int
1564 _ecore_desktop_menu_is_include(const void *data, Ecore_Desktop_Tree * tree, int element, int level __UNUSED__)
1565 {
1566    char *result;
1567
1568    result = (char *)data;
1569    if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1570      {
1571         *result = ((char *)tree->elements[element].element)[0];
1572         return 1;
1573      }
1574
1575    return 0;
1576 }
1577
1578 static void
1579 _ecore_desktop_menu_inherit_apps(void *value, void *user_data)
1580 {
1581    Ecore_Hash_Node    *node;
1582    Ecore_Hash         *pool;
1583    char               *key, *app;
1584
1585    pool = (Ecore_Hash *) user_data;
1586    node = (Ecore_Hash_Node *) value;
1587    key = (char *)node->key;
1588    app = (char *)node->value;
1589 #ifdef DEBUG
1590 //   printf("CHECKING %s - %s\n", app, key);
1591 #endif
1592    if (!ecore_hash_get(pool, key))
1593       ecore_hash_set(pool, strdup(key), strdup(app));
1594 }
1595
1596 static void
1597 _ecore_desktop_menu_select_app(void *value, void *user_data)
1598 {
1599    Ecore_Hash_Node    *node;
1600    Ecore_Desktop      *desktop;
1601    struct _ecore_desktop_menu_generate_data *generate_data;
1602    char               *key, *app;
1603
1604    node = (Ecore_Hash_Node *) value;
1605    generate_data = (struct _ecore_desktop_menu_generate_data *)user_data;
1606    key = (char *)node->key;
1607    app = (char *)node->value;
1608
1609    /* FIXME: pass an actuall language parameter. */
1610    desktop = ecore_desktop_get(app, NULL);
1611
1612    if (desktop)
1613      {
1614         if ((generate_data->unallocated) && (desktop->allocated))
1615            return;
1616
1617         if (_ecore_desktop_menu_apply_rules(generate_data, generate_data->rule, key, desktop))
1618           {
1619              desktop->allocated = TRUE;
1620              if (generate_data->include)
1621                {
1622                   ecore_hash_set(generate_data->apps, key, strdup(app));
1623 #ifdef DEBUG
1624                   printf("INCLUDING %s%s - %s\n", ((generate_data->unallocated) ? "UNALLOCATED " : ""), app, key);
1625 #endif
1626                }
1627              else
1628                {
1629                   ecore_hash_remove(generate_data->apps, key);
1630 #ifdef DEBUG
1631                   printf("EXCLUDING %s%s - %s\n", ((generate_data->unallocated) ? "UNALLOCATED " : ""), app, key);
1632 #endif
1633                }
1634           }
1635      }
1636 }
1637
1638 static int
1639 _ecore_desktop_menu_apply_rules(struct _ecore_desktop_menu_generate_data *generate_data, Ecore_Desktop_Tree * rule, char *key, Ecore_Desktop * desktop)
1640 {
1641    char                type = 'O';
1642    int                 result = FALSE;
1643    int                 i;
1644
1645    for (i = 0; i < rule->size; i++)
1646      {
1647         if (rule->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1648           {
1649              result = _ecore_desktop_menu_apply_rules(generate_data, (Ecore_Desktop_Tree *) rule->elements[i].element, key, desktop);
1650           }
1651         else
1652           {
1653              char               *rul;
1654              char                match;
1655              int                 sub_result = FALSE;
1656
1657              rul = (char *)rule->elements[i].element;
1658              type = rul[1];
1659              match = rul[2];
1660              switch (match)
1661                {
1662                case 'A':
1663                   {
1664                      sub_result = TRUE;
1665                      break;
1666                   }
1667
1668                case 'F':
1669                   {
1670                      if (strcmp(key, &rul[4]) == 0)
1671                         sub_result = TRUE;
1672                      break;
1673                   }
1674
1675                case 'C':
1676                   {
1677                      /* Try to match a category. */
1678                      if ((desktop->Categories) && (ecore_hash_get(desktop->Categories, &rul[4]) != NULL))
1679                         sub_result = TRUE;
1680                      break;
1681                   }
1682                }
1683              switch (type)
1684                {
1685                   case 'A':  result = TRUE;  if (!sub_result)  return FALSE;  break;
1686                   case 'N':  result = TRUE;  if (sub_result)   return FALSE;  break;
1687                   default:                   if (sub_result)   return TRUE;   break;
1688                }
1689           }
1690      }
1691
1692    return result;
1693 }
1694
1695 /*
1696
1697 <Menu (tree)
1698   name
1699   flags = "   " or "MDO" the first letter of - Marked, Deleted, OnlyUnallocated 
1700   pool (hash)
1701     id = path
1702     id = path
1703   rules (tree)
1704     rule
1705     rule
1706   menu (hash)
1707     id = path
1708     id = path
1709   <Menu (tree)
1710   <Menu (tree)
1711
1712 rules (tree)
1713   include/exclude or all/file/category x
1714   and/not (tree)
1715     include/exclude and/not all/file/category x
1716
1717 ALL
1718   return true
1719
1720 FILENAME
1721   if the rule string matches the desktop id return true
1722   otherwise return false
1723
1724 CATEGORY
1725   loop through the apps categories
1726   as soon as one matches the rule string, return true
1727   otherwise return false.
1728
1729 OR (implied)
1730   loop through the rules
1731   as soon as one matches, return true
1732   otherwise return false.
1733
1734 SUB RULES
1735   process the sub rules, return the result
1736
1737 AND
1738   loop through the rules
1739   as soon as one doesn't match, return false
1740   otherwise return true.
1741
1742 NOT (implied OR)
1743   loop through the rules
1744   as soon as one matches, return false
1745   otherwise return true.
1746  */
1747
1748 /*  FDO PARSING STEPS.
1749
1750 An asterisk in the first column marks those bits that are done.
1751 The spec is not numbered, so I have invented a numbering to help match the
1752 steps to the code.
1753
1754  10000000 merge menus
1755 *11000000   expand <KDELegacyDir>'s to <LegacyDir>.
1756 *12000000   expand <LegacyDir>'s
1757 *12100000     for each dir (recursive)
1758 *12110000       create recursively nested <MENU <   L> <dirname> <> element
1759 *12120000    //   <AppDir>dirpath</AppDir>
1760 *12130000       <DirectoryDir>dirpath</DirectoryDir>
1761 *12140000       if exist .directory
1762 *12141000         add <.directory> to name
1763 *12150000       <Include>
1764 *12160000       for each *.desktop
1765  12161000         if no categories in bar.desktop
1766 *12161100           <Filename>prefix-bar.desktop</Filename> 
1767  12162000         add "Legacy" to categories
1768 *12163000         add any prefix to the desktop ID.
1769 *12164000         add it to the pool
1770 *12170000       </Include>
1771 *13000000   for each <MergeFile> and <MergeDir> element
1772 *13100000     get the root <Menu> elements from that elements file/s.
1773 *13200000     remove the <Name> element from those root <Menu> elements.
1774 *13300000     replace that element with the child elements of those root <Menu> elements.
1775 *13400000?    expand the <DefaultMergeDirs> to <MergeDir>'s $XDG_CONFIG_DIRS/menus/foo-merged/ for the menu file foo.menu
1776 FIXME: The above should probably move to before 13000000
1777 *14000000   loop until all <MergeFile> and <MergeDir> elements are done,
1778 *14000000   careful to avoid infinite loops in files that reference each other.
1779 *15000000   for each <Menu> recursively
1780  15100000     consolidate duplicate child <Menu>s.
1781 *15200000     expand <DefaultAppDir>s and <DefaultDirectoryDir>s to <AppDir>s and <DirectoryDir>s.
1782  15300000     consolidate duplicate child <AppDir>s, <DirectoryDir>s, and <Directory>s.
1783  16000000   for each <Menu> recursively (but from the deepest level out)
1784  16100000     for each <Move>
1785  16110000       do the move.
1786  16120000       mark any <Menu> that has been affected.
1787  17000000   for each marked <Menu> recursively
1788  17100000     consolidate duplicate child <Menu>s.
1789 *18000000   for each <Menu> recursively
1790 *18100000     if there are <Deleted> elements that are not ovreridden by a <NotDelete> element
1791 *18110000       remove this <Menu> element and all it's children.
1792
1793 *20000000 generate menus
1794 *21000000   for each <Menu> that is <NotOnlyUnallocated> (which is the default)
1795 *21100000     for each <AppDir>
1796 *21110000       for each .desktop
1797 *21111000         if it exists in the pool, replace it.
1798 *21112000         else add it to the pool.
1799 *21200000     for each parent <Menu>
1800 *21210000       for each .desktop in the pool
1801 *21211000         if it doesn't exist in the child <Menu> pool
1802 *21211100           add it to the pool.
1803 *21300000     for each <Include> and <Exclude>
1804 *21310000       if rule is an <Include>
1805 *21311000         for each .desktop in pool
1806 *21311100           for each rule
1807 *21311110             if rule matches .desktop in pool
1808 *21311111               add .desktop to menu.
1809 *21311112               mark it as allocated
1810 *21320000       if rule is an <Exclude>
1811 *21321000         for each .desktop in menu
1812 *21321100           for each rule
1813 *21321110             if rule matches .desktop in menu
1814 *21321111               remove .desktop from menu.
1815 *21321112               leave it as allocated.
1816
1817 *30000000 generate unallocated menus
1818 *31000000   Same as for generate menus, but only the <OnlyUnallocated> ones.
1819 *32000000   Only the unallocated .desktop entries can be used.
1820
1821  40000000 generate menu layout
1822    This part of the spec is a whole other rabbit hole, and optional.  B-)
1823 */
1824
1825
1826 /*  THINGS TAHT CAN BE DUPLICATED
1827
1828 AppDir        last dup is used
1829 .desktop      last dup is used
1830 DirectoryDir  last dup is used
1831 .directory    last dup is used
1832 Directory     last dup is used, unless it points to a non existant entry, then work your way backwards
1833 LegacyDir     last dup is used
1834 MergeDir      last dup is used
1835 MergeFile     last dup is used
1836
1837 DefaultMergeDirs   dirs that are earlier in the path go later in the menu, so they have priority
1838 KDELegacyDirs      dirs that are earlier in the path go later in the menu, so they have priority
1839
1840 OnlyUnallocated & NotOnlyUnallocated   last one wins, default is NotOnlyUnallocated
1841 Deleted & NotDeleted                   last one wins
1842
1843 Include & Exclude  done in order
1844 */