1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include <Ecore.h>
6 #include <Ecore_File.h>
7 
8 /* TODO: Don't scan app dirs if they are cached, read values from cache */
9 
10 /* define macros and variable for using the eina logging system  */
11 #define EFREET_MODULE_LOG_DOM _efreet_menu_log_dom
12 static int _efreet_menu_log_dom = -1;
13 
14 #include "Efreet.h"
15 #include "efreet_private.h"
16 #include "efreet_xml.h"
17 #include <unistd.h>
18 
19 typedef struct Efreet_Menu_Move Efreet_Menu_Move;
20 
21 struct Efreet_Menu_Move
22 {
23     const char *old_name;     /**< The menu path to move from */
24     const char *new_name;     /**< The menu path to move too */
25 };
26 
27 typedef struct Efreet_Menu_Internal Efreet_Menu_Internal;
28 
29 struct Efreet_Menu_Internal
30 {
31     struct
32     {
33         const char *path;         /**< The base file path */
34         const char *name;         /**< The filename for this menu */
35     } file;                 /**< The menu file information */
36 
37     struct
38     {
39         const char *internal;     /**< The menu name */
40         const char *name;         /**< Name to use in the menus */
41     } name;                       /**< The names for this menu */
42 
43     Eina_Hash *efreet_merged_menus; /**< Merged menus */
44     Eina_Hash *efreet_merged_dirs; /**< Merged dirs */
45 
46     Efreet_Desktop *directory; /**< The directory */
47     Eina_List *directories;  /**< All the directories set in the menu file */
48 
49     Efreet_Menu_Move *current_move; /**< The current move */
50 
51     Eina_List *app_dirs;           /**< .desktop application directories */
52 
53     Eina_List *app_pool;           /**< application pool */
54     Eina_List *applications;       /**< applications in this menu */
55 
56     Eina_List *directory_dirs;    /**< .directory file directories */
57     Eina_Hash *directory_cache;    /**< .directory dirs */
58 
59     Eina_List *moves;              /**< List of moves to be handled by the menu */
60     Eina_List *filters;            /**< Include and Exclude filters */
61 
62     Efreet_Menu_Internal *parent;   /**< Our parent menu */
63     Eina_List *sub_menus;          /**< Our sub menus */
64 
65     Eina_List *layout;             /**< This menus layout */
66     Eina_List *default_layout;     /**< Default layout */
67     signed char show_empty;    /**< Whether to show empty menus */
68     signed char in_line;       /**< Whether this menu can be inlined */
69     signed char inline_limit;  /**< Number of elements which triggers inline */
70     signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
71     signed char inline_alias;  /**< Whether we should use the menu name when inlining */
72 
73     unsigned char seen_allocated:1;     /**< have we set the only_unallocated */
74     unsigned char only_unallocated:1;   /**< Show only unallocated .desktops */
75 
76     unsigned char seen_deleted:1;       /**< Have we seen the deleted item yet */
77     unsigned char deleted:1;            /**< The menu is deleted */
78 };
79 
80 typedef struct Efreet_Menu_App_Dir Efreet_Menu_App_Dir;
81 
82 struct Efreet_Menu_App_Dir
83 {
84     const char *path;           /**< directory path */
85     const char *prefix;         /**< If it's legacy it can have a prefix */
86     unsigned int legacy:1;      /**< is this a legacy dir */
87 };
88 
89 enum Efreet_Menu_Filter_Op_Type
90 {
91     EFREET_MENU_FILTER_OP_OR,
92     EFREET_MENU_FILTER_OP_AND,
93     EFREET_MENU_FILTER_OP_NOT
94 };
95 
96 typedef enum Efreet_Menu_Filter_Op_Type Efreet_Menu_Filter_Op_Type;
97 
98 enum Efreet_Menu_Filter_Type
99 {
100     EFREET_MENU_FILTER_INCLUDE,
101     EFREET_MENU_FILTER_EXCLUDE
102 };
103 
104 typedef enum Efreet_Menu_Filter_Type Efreet_Menu_Filter_Type;
105 
106 typedef struct Efreet_Menu_Filter_Op Efreet_Menu_Filter_Op;
107 
108 struct Efreet_Menu_Filter_Op
109 {
110     Efreet_Menu_Filter_Op_Type type; /**< The type of operation */
111     Eina_List *categories;          /**< The categories this op applies too */
112     Eina_List *filenames;           /**< The filenames this op applies too */
113 
114     Eina_List *filters;             /**< Child filters */
115 
116     unsigned char all:1;             /**< Applies to all .desktop files */
117 };
118 
119 typedef struct Efreet_Menu_Filter Efreet_Menu_Filter;
120 
121 struct Efreet_Menu_Filter
122 {
123     Efreet_Menu_Filter_Type type;   /**< The type of filter */
124     Efreet_Menu_Filter_Op *op;      /**< The filter operations */
125 };
126 
127 enum Efreet_Menu_Layout_Type
128 {
129     EFREET_MENU_LAYOUT_MENUNAME,
130     EFREET_MENU_LAYOUT_FILENAME,
131     EFREET_MENU_LAYOUT_SEPARATOR,
132     EFREET_MENU_LAYOUT_MERGE
133 };
134 
135 typedef enum Efreet_Menu_Layout_Type Efreet_Menu_Layout_Type;
136 
137 typedef struct Efreet_Menu_Layout Efreet_Menu_Layout;
138 
139 struct Efreet_Menu_Layout
140 {
141     Efreet_Menu_Layout_Type  type;   /**< The type of layout */
142     const char *name;                /**< The name of the element */
143 
144     /* The items below are for Menuname Layout elements */
145     signed char show_empty;    /**< Whether to show empty menus */
146     signed char in_line;       /**< Whether this menu can be inlined */
147     signed char inline_limit;  /**< Number of elements which triggers inline */
148     signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
149     signed char inline_alias;  /**< Whether we should use the menu name when inlining */
150 };
151 
152 typedef struct Efreet_Menu_Desktop Efreet_Menu_Desktop;
153 
154 struct Efreet_Menu_Desktop
155 {
156     Efreet_Desktop *desktop;   /**< The desktop we refer too */
157     const char *id;            /**< The desktop file id */
158     unsigned char allocated:1; /**< If this desktop has been allocated */
159 };
160 
161 typedef struct Efreet_Menu_Async Efreet_Menu_Async;
162 
163 struct Efreet_Menu_Async
164 {
165     Efreet_Menu_Cb    func;
166     void             *data;
167     Eina_Stringshare *path;
168     Efreet_Menu      *menu;
169 };
170 
171 static const char *efreet_menu_prefix = NULL; /**< The $XDG_MENU_PREFIX env var */
172 Eina_List *efreet_menu_kde_legacy_dirs = NULL; /**< The directories to use for KDELegacy entries */
173 static const char *efreet_tag_menu = NULL;
174 static const char *efreet_menu_file = NULL; /**< A menu file set explicityl as default */
175 
176 static Eina_Hash *efreet_menu_handle_cbs = NULL;
177 static Eina_Hash *efreet_menu_filter_cbs = NULL;
178 static Eina_Hash *efreet_menu_move_cbs = NULL;
179 static Eina_Hash *efreet_menu_layout_cbs = NULL;
180 
181 static Efreet_Menu_Internal *efreet_menu_by_name_find(Efreet_Menu_Internal *internal,
182                                                     const char *name,
183                                                     Efreet_Menu_Internal **parent);
184 static int efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name);
185 static int efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name);
186 
187 static int efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal);
188 static int efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop);
189 
190 #ifndef STRICT_SPEC
191 static int efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old);
192 #endif
193 
194 static int efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated);
195 static int efreet_menu_process_dirs(Efreet_Menu_Internal *internal);
196 static int efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal);
197 static int efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal,
198                                         const char *path,
199                                         const char *id,
200                                         int legacy);
201 static int efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal);
202 static int efreet_menu_directory_dir_scan(const char *path,
203                                             const char *relative_path,
204                                             Eina_Hash *cache);
205 static Efreet_Desktop *efreet_menu_directory_get(Efreet_Menu_Internal *internal,
206                                                     const char *path);
207 static void efreet_menu_process_filters(Efreet_Menu_Internal *internal,
208                                             unsigned int only_unallocated);
209 static Eina_List *efreet_menu_process_app_pool(Eina_List *pool,
210                                                Eina_List *applications,
211                                                Eina_Hash *matches,
212                                                Efreet_Menu_Filter *filter,
213                                                unsigned int only_unallocated);
214 static int efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op,
215                                         Efreet_Menu_Desktop *md);
216 static int efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op,
217                                             Efreet_Menu_Desktop *md);
218 static int efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op,
219                                             Efreet_Menu_Desktop *md);
220 static int efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op,
221                                             Efreet_Menu_Desktop *md);
222 
223 static Efreet_Menu *efreet_menu_layout_menu(Efreet_Menu_Internal *internal);
224 static Efreet_Menu *efreet_menu_layout_desktop(Efreet_Menu_Desktop *md);
225 static void efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
226                                             Efreet_Menu_Layout *layout);
227 static int efreet_menu_layout_is_empty(Efreet_Menu *entry);
228 
229 static Efreet_Menu_Internal *efreet_menu_internal_new(Efreet_Menu_Internal *parent);
230 static void efreet_menu_internal_free(Efreet_Menu_Internal *internal);
231 static void efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal);
232 static void efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal);
233 static void efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal);
234 static void efreet_menu_create_directories_list(Efreet_Menu_Internal *internal);
235 static void efreet_menu_create_move_list(Efreet_Menu_Internal *internal);
236 static void efreet_menu_create_filter_list(Efreet_Menu_Internal *internal);
237 static void efreet_menu_create_layout_list(Efreet_Menu_Internal *internal);
238 static void efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal);
239 static const char *efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix);
240 
241 static Efreet_Menu_App_Dir *efreet_menu_app_dir_new(void);
242 static void efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir);
243 
244 static Efreet_Menu_Move *efreet_menu_move_new(void);
245 static void efreet_menu_move_free(Efreet_Menu_Move *move);
246 
247 static Efreet_Menu_Filter *efreet_menu_filter_new(void);
248 static void efreet_menu_filter_free(Efreet_Menu_Filter *filter);
249 
250 static Efreet_Menu_Layout *efreet_menu_layout_new(void);
251 static void efreet_menu_layout_free(Efreet_Menu_Layout *layout);
252 
253 static Efreet_Menu_Filter_Op *efreet_menu_filter_op_new(void);
254 static void efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op);
255 
256 static Efreet_Menu_Desktop *efreet_menu_desktop_new(void);
257 static void efreet_menu_desktop_free(Efreet_Menu_Desktop *md);
258 
259 static Efreet_Menu *efreet_menu_entry_new(void);
260 
261 static int efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml);
262 static int efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
263 
264 static int efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
265 static int efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
266 static int efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
267 static int efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
268 static int efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
269 static int efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
270 static int efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
271 static int efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
272 static int efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
273 static int efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
274 static int efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
275 static int efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
276 static int efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
277 static int efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
278 static int efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
279 static int efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
280 static int efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
281 static int efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
282 static int efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
283 static int efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
284 static int efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
285 static int efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
286 static Efreet_Menu_Internal *efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
287                                                 Efreet_Menu_Internal *parent,
288                                                 const char *legacy_dir,
289                                                 const char *prefix);
290 static int efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
291 static int efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
292 static int efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
293 static int efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
294 static int efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
295 static int efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
296 
297 static int efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
298                                                     Efreet_Menu_Filter_Type type);
299 static int efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
300 static int efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
301                                                       Efreet_Menu_Filter_Op_Type type);
302 
303 static int efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
304 static int efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
305 static int efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
306 static int efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
307 
308 static int efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
309 static int efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
310 
311 static int efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b);
312 
313 static void efreet_menu_resolve_moves(Efreet_Menu_Internal *internal);
314 static void efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src);
315 
316 static int efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b);
317 static int efreet_menu_cb_md_compare(const Efreet_Menu_Desktop *a, const Efreet_Menu_Desktop *b);
318 
319 static void efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path);
320 
321 static int efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent);
322 static int efreet_menu_save_indent(FILE *f, int indent);
323 
324 int
efreet_menu_init(void)325 efreet_menu_init(void)
326 {
327     int i;
328 
329     struct
330     {
331         const char *key;
332         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
333     } menu_cbs[] = {
334         {"Menu", efreet_menu_handle_sub_menu},
335         {"AppDir", efreet_menu_handle_app_dir},
336         {"DefaultAppDirs", efreet_menu_handle_default_app_dirs},
337         {"DirectoryDir", efreet_menu_handle_directory_dir},
338         {"DefaultDirectoryDirs", efreet_menu_handle_default_directory_dirs},
339         {"Name", efreet_menu_handle_name},
340         {"Directory", efreet_menu_handle_directory},
341         {"OnlyUnallocated", efreet_menu_handle_only_unallocated},
342         {"NotOnlyUnallocated", efreet_menu_handle_not_only_unallocated},
343         {"Deleted", efreet_menu_handle_deleted},
344         {"NotDeleted", efreet_menu_handle_not_deleted},
345         {"Include", efreet_menu_handle_include},
346         {"Exclude", efreet_menu_handle_exclude},
347         {"MergeFile", efreet_menu_handle_merge_file},
348         {"MergeDir", efreet_menu_handle_merge_dir},
349         {"DefaultMergeDirs", efreet_menu_handle_default_merge_dirs},
350         {"LegacyDir", efreet_menu_handle_legacy_dir},
351         {"KDELegacyDirs", efreet_menu_handle_kde_legacy_dirs},
352         {"Move", efreet_menu_handle_move},
353         {"Layout", efreet_menu_handle_layout},
354         {"DefaultLayout", efreet_menu_handle_default_layout},
355         {NULL, NULL}
356     };
357 
358     struct
359     {
360         const char *key;
361         int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
362     } filter_cbs[] = {
363         {"Filename", efreet_menu_handle_filename},
364         {"Category", efreet_menu_handle_category},
365         {"All", efreet_menu_handle_all},
366         {"And", efreet_menu_handle_and},
367         {"Or", efreet_menu_handle_or},
368         {"Not", efreet_menu_handle_not},
369         {NULL, NULL}
370     };
371 
372     struct
373     {
374         const char *key;
375         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
376     } move_cbs[] = {
377         {"Old", efreet_menu_handle_old},
378         {"New", efreet_menu_handle_new},
379         {NULL, NULL}
380     };
381 
382     struct
383     {
384         const char *key;
385         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
386     } layout_cbs[] = {
387         {"Menuname", efreet_menu_handle_layout_menuname},
388         {"Filename", efreet_menu_handle_layout_filename},
389         {"Separator", efreet_menu_handle_layout_separator},
390         {"Merge", efreet_menu_handle_layout_merge},
391         {NULL, NULL}
392     };
393 
394     _efreet_menu_log_dom = eina_log_domain_register
395       ("efreet_menu", EFREET_DEFAULT_LOG_COLOR);
396     if (_efreet_menu_log_dom < 0)
397     {
398         EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_menu");
399         return 0;
400     }
401 
402 #if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
403    if (getuid() == geteuid())
404 #endif
405      efreet_menu_prefix = getenv("XDG_MENU_PREFIX");
406    if (!efreet_menu_prefix) efreet_menu_prefix = "";
407 
408     efreet_menu_handle_cbs = eina_hash_string_superfast_new(NULL);
409     efreet_menu_filter_cbs = eina_hash_string_superfast_new(NULL);
410     efreet_menu_move_cbs = eina_hash_string_superfast_new(NULL);
411     efreet_menu_layout_cbs = eina_hash_string_superfast_new(NULL);
412     if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs
413             || !efreet_menu_move_cbs || !efreet_menu_layout_cbs)
414     {
415         eina_log_domain_unregister(_efreet_menu_log_dom);
416         _efreet_menu_log_dom = -1;
417         return 0;
418     }
419 
420     /* set Menu into it's own so we can check the XML is valid before trying
421      * to handle it */
422     efreet_tag_menu = eina_stringshare_add(menu_cbs[0].key);
423 
424     for (i = 0; menu_cbs[i].key; i++)
425     {
426         eina_hash_set(efreet_menu_handle_cbs,
427                         menu_cbs[i].key,
428                         menu_cbs[i].cb);
429     }
430     for (i = 0; filter_cbs[i].key; i++)
431     {
432         eina_hash_set(efreet_menu_filter_cbs,
433                         filter_cbs[i].key,
434                         filter_cbs[i].cb);
435     }
436     for (i = 0; move_cbs[i].key; i++)
437     {
438         eina_hash_set(efreet_menu_move_cbs,
439                         move_cbs[i].key,
440                         move_cbs[i].cb);
441     }
442     for (i = 0; layout_cbs[i].key; i++)
443     {
444         eina_hash_set(efreet_menu_layout_cbs,
445                         layout_cbs[i].key,
446                         layout_cbs[i].cb);
447     }
448     return 1;
449 }
450 
451 EAPI int
efreet_menu_kde_legacy_init(void)452 efreet_menu_kde_legacy_init(void)
453 {
454     FILE *f;
455     char buf[PATH_MAX];
456     char *p, *s;
457 
458     IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
459 
460     f = popen("kde-config --path apps", "r");
461     if (!f) return 0;
462 
463     /* XXX if the return from kde-config is a line longer than PATH_MAX,
464      * this won't be correct (increase buffer and get the rest...) */
465     if (!fgets(buf, sizeof(buf), f))
466     {
467         ERR("Error initializing KDE legacy information");
468         pclose(f);
469         return 0;
470     }
471     s = buf;
472 
473     p = strchr(s, ':');
474     while (p)
475     {
476         *p = '\0';
477         efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
478                             (void *)eina_stringshare_add(s));
479         s = p + 1;
480         p = strchr(s, ':');
481     }
482 
483     if (*s)
484         efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
485                             (void *)eina_stringshare_add(s));
486 
487     pclose(f);
488     return 1;
489 }
490 
491 void
efreet_menu_shutdown(void)492 efreet_menu_shutdown(void)
493 {
494     IF_RELEASE(efreet_menu_file);
495 
496     IF_FREE_HASH(efreet_menu_handle_cbs);
497     IF_FREE_HASH(efreet_menu_filter_cbs);
498     IF_FREE_HASH(efreet_menu_move_cbs);
499     IF_FREE_HASH(efreet_menu_layout_cbs);
500 
501     IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
502 
503     IF_RELEASE(efreet_tag_menu);
504 
505     eina_log_domain_unregister(_efreet_menu_log_dom);
506     _efreet_menu_log_dom = -1;
507 }
508 
509 EAPI Efreet_Menu *
efreet_menu_new(const char * name)510 efreet_menu_new(const char *name)
511 {
512     Efreet_Menu *menu;
513 
514     EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
515 
516     menu = efreet_menu_entry_new();
517     menu->type = EFREET_MENU_ENTRY_MENU;
518     menu->name = eina_stringshare_add(name);
519     return menu;
520 }
521 
522 EAPI void
efreet_menu_file_set(const char * file)523 efreet_menu_file_set(const char *file)
524 {
525     IF_RELEASE(efreet_menu_file);
526     efreet_menu_file = NULL;
527     if (file) efreet_menu_file = eina_stringshare_add(file);
528 }
529 
530 /* deprecated */
531 EFREET_DEPRECATED_API EAPI void
efreet_menu_async_get(Efreet_Menu_Cb func EINA_UNUSED,const void * data EINA_UNUSED)532 efreet_menu_async_get(Efreet_Menu_Cb func EINA_UNUSED, const void *data EINA_UNUSED)
533 {
534     ERR("%s is deprecated and shouldn't be called", __func__);
535 
536     return;
537 }
538 
539 EAPI Efreet_Menu *
efreet_menu_get(void)540 efreet_menu_get(void)
541 {
542     char menu[PATH_MAX];
543     const char *dir;
544     Eina_List *config_dirs, *l;
545 
546 #ifndef STRICT_SPEC
547     /* prefer user set menu */
548     if (efreet_menu_file)
549     {
550         if (ecore_file_exists(efreet_menu_file))
551             return efreet_menu_parse(efreet_menu_file);
552     }
553 #endif
554 
555     /* check the users config directory first */
556     snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
557                         efreet_config_home_get(), efreet_menu_prefix);
558     if (ecore_file_exists(menu))
559         return efreet_menu_parse(menu);
560 
561     /* fallback to the XDG_CONFIG_DIRS */
562     config_dirs = efreet_config_dirs_get();
563     EINA_LIST_FOREACH(config_dirs, l, dir)
564     {
565         snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
566                                     dir, efreet_menu_prefix);
567         if (ecore_file_exists(menu))
568             return efreet_menu_parse(menu);
569     }
570 
571     return NULL;
572 }
573 
574 /* deprecated */
575 EFREET_DEPRECATED_API EAPI void
efreet_menu_async_parse(const char * path EINA_UNUSED,Efreet_Menu_Cb func EINA_UNUSED,const void * data EINA_UNUSED)576 efreet_menu_async_parse(const char *path EINA_UNUSED, Efreet_Menu_Cb func EINA_UNUSED, const void *data EINA_UNUSED)
577 {
578     ERR("%s is deprecated and shouldn't be called", __func__);
579 
580     return;
581 }
582 
583 EAPI Efreet_Menu *
efreet_menu_parse(const char * path)584 efreet_menu_parse(const char *path)
585 {
586     Efreet_Xml *xml;
587     Efreet_Menu_Internal *internal = NULL;
588     Efreet_Menu *entry = NULL;
589 
590     EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
591 
592     xml = efreet_xml_new(path);
593     if (!xml) return NULL;
594 
595     /* make sure we've got a <Menu> to start with */
596     if (xml->tag != efreet_tag_menu)
597     {
598         WRN("Menu file didn't start with <Menu> tag: '%s'", path);
599         efreet_xml_del(xml);
600         return NULL;
601     }
602 
603     /* split apart the filename and the path */
604     internal = efreet_menu_internal_new(NULL);
605     if (!internal)
606     {
607         efreet_xml_del(xml);
608         return NULL;
609     }
610     internal->efreet_merged_menus = eina_hash_string_superfast_new(NULL);
611     internal->efreet_merged_dirs = eina_hash_string_superfast_new(NULL);
612 
613     /* Set default values */
614     internal->show_empty = 0;
615     internal->in_line = 0;
616     internal->inline_limit = 4;
617     internal->inline_header = 1;
618     internal->inline_alias = 0;
619 
620     efreet_menu_path_set(internal, path);
621     if (!efreet_menu_handle_menu(internal, xml))
622     {
623         efreet_xml_del(xml);
624         goto error;
625     }
626     efreet_xml_del(xml);
627 
628     efreet_menu_resolve_moves(internal);
629 
630     if (!efreet_menu_process_dirs(internal))
631         goto error;
632 
633     /* handle all .desktops */
634     if (!efreet_menu_process(internal, 0))
635         goto error;
636 
637     /* handle menus with only unallocated .desktops */
638     if (!efreet_menu_process(internal, 1))
639         goto error;
640 
641     /* layout menu */
642     entry = efreet_menu_layout_menu(internal);
643 
644 error:
645     IF_FREE_HASH(internal->efreet_merged_menus);
646     IF_FREE_HASH(internal->efreet_merged_dirs);
647     efreet_menu_internal_free(internal);
648     return entry;
649 }
650 
651 EAPI int
efreet_menu_save(Efreet_Menu * menu,const char * path)652 efreet_menu_save(Efreet_Menu *menu, const char *path)
653 {
654     FILE *f;
655     int ret;
656 
657     EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
658     EINA_SAFETY_ON_NULL_RETURN_VAL(path, 0);
659 
660     f = fopen(path, "wb");
661     if (!f) return 0;
662     fprintf(f, "<?xml version=\"1.0\"?>\n");
663     fprintf(f, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" "
664                 "\"http://standards.freedesktop.org/menu-spec/menu-1.0.dtd\">\n");
665     ret = efreet_menu_save_menu(menu, f, 0);
666     fclose(f);
667     return ret;
668 }
669 
670 EAPI int
efreet_menu_desktop_insert(Efreet_Menu * menu,Efreet_Desktop * desktop,int pos)671 efreet_menu_desktop_insert(Efreet_Menu *menu, Efreet_Desktop *desktop, int pos)
672 {
673     Efreet_Menu *entry;
674     const char *id;
675 
676     EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
677     EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
678 
679     id = efreet_util_path_to_file_id(desktop->orig_path);
680     if (!id) return 0;
681 
682     entry = efreet_menu_entry_new();
683     entry->type = EFREET_MENU_ENTRY_DESKTOP;
684     entry->id = eina_stringshare_add(id);
685     entry->name = eina_stringshare_add(desktop->name);
686     if (desktop->icon) entry->icon = eina_stringshare_add(desktop->icon);
687     efreet_desktop_ref(desktop);
688     entry->desktop = desktop;
689 
690     if (pos < 0 || (unsigned int)pos >= eina_list_count(menu->entries))
691         menu->entries = eina_list_append(menu->entries, entry);
692     else
693     {
694         menu->entries = eina_list_append_relative(menu->entries, entry,
695                                                   eina_list_nth(menu->entries, pos));
696     }
697     return 1;
698 }
699 
700 EAPI int
efreet_menu_desktop_remove(Efreet_Menu * menu,Efreet_Desktop * desktop)701 efreet_menu_desktop_remove(Efreet_Menu *menu, Efreet_Desktop *desktop)
702 {
703     Efreet_Menu *entry;
704 
705     EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
706     EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
707 
708     entry = eina_list_search_unsorted(menu->entries,
709                                       EINA_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
710                             desktop);
711     if (entry)
712     {
713         menu->entries = eina_list_remove(menu->entries, entry);
714         efreet_menu_free(entry);
715         return 1;
716     }
717     return 0;
718 }
719 
720 EAPI void
efreet_menu_dump(Efreet_Menu * menu,const char * indent)721 efreet_menu_dump(Efreet_Menu *menu, const char *indent)
722 {
723     Eina_List *l;
724 
725     EINA_SAFETY_ON_NULL_RETURN(menu);
726     EINA_SAFETY_ON_NULL_RETURN(indent);
727 
728     INF("%s%s: ", indent, menu->name);
729     INF("%s", (menu->icon ? menu->icon : "No icon"));
730 
731     /* XXX dump the rest of the menu info */
732 
733     if (menu->entries)
734     {
735         Efreet_Menu *entry;
736         char *new_indent;
737         size_t len;
738 
739         len = strlen(indent) + 3;
740         new_indent = alloca(len);
741         snprintf(new_indent, len, "%s  ", indent);
742 
743         EINA_LIST_FOREACH(menu->entries, l, entry)
744         {
745             if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
746                 INF("%s|---", new_indent);
747             else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
748                 INF("%s|-%s", new_indent, entry->name);
749             else if (entry->type == EFREET_MENU_ENTRY_MENU)
750                 efreet_menu_dump(entry, new_indent);
751             else if (entry->type == EFREET_MENU_ENTRY_HEADER)
752                 INF("%s|---%s", new_indent, entry->name);
753         }
754     }
755 }
756 
757 /**
758  * @internal
759  * @return Returns a new Efreet_Menu_Internal struct
760  * @brief Allocates and initializes a new Efreet_Menu_Internal structure
761  */
762 static Efreet_Menu_Internal *
efreet_menu_internal_new(Efreet_Menu_Internal * parent)763 efreet_menu_internal_new(Efreet_Menu_Internal *parent)
764 {
765     Efreet_Menu_Internal *internal;
766 
767     internal = NEW(Efreet_Menu_Internal, 1);
768     if (!internal) return NULL;
769     internal->show_empty = -1;
770     internal->in_line = -1;
771     internal->inline_limit = -1;
772     internal->inline_header = -1;
773     internal->inline_alias = -1;
774 
775     if (parent)
776     {
777         internal->efreet_merged_menus = parent->efreet_merged_menus;
778         internal->efreet_merged_dirs = parent->efreet_merged_dirs;
779     }
780 
781     return internal;
782 }
783 
784 /**
785  * @param menu The menu to free
786  * @return Returns no value
787  * @brief Frees up the given menu structure
788  */
789 void
efreet_menu_internal_free(Efreet_Menu_Internal * internal)790 efreet_menu_internal_free(Efreet_Menu_Internal *internal)
791 {
792     if (!internal) return;
793 
794     IF_RELEASE(internal->file.path);
795     IF_RELEASE(internal->file.name);
796 
797     IF_RELEASE(internal->name.internal);
798     internal->name.name = NULL;
799 
800     internal->applications = eina_list_free(internal->applications);
801 
802     IF_FREE_LIST(internal->directories, eina_stringshare_del);
803     IF_FREE_LIST(internal->app_dirs, efreet_menu_app_dir_free);
804     IF_FREE_LIST(internal->app_pool, efreet_menu_desktop_free);
805     IF_FREE_LIST(internal->directory_dirs, eina_stringshare_del);
806     IF_FREE_HASH(internal->directory_cache);
807 
808     IF_FREE_LIST(internal->moves, efreet_menu_move_free);
809     IF_FREE_LIST(internal->filters, efreet_menu_filter_free);
810 
811     IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
812 
813     IF_FREE_LIST(internal->layout, efreet_menu_layout_free);
814     IF_FREE_LIST(internal->default_layout, efreet_menu_layout_free);
815 
816     FREE(internal);
817 }
818 
819 /**
820  * @internal
821  * @param menu The menu to populate
822  * @param xml The xml dom tree to populate from
823  * @return Returns 1 if this XML tree is valid, 0 otherwise
824  * @brief Populates the given menu from the given xml structure
825  *
826  * We walk the Menu children backwards. The reason for this is so that we
827  * can deal with all the things that make us select the 'last' element
828  * (MergeFile, Directory, etc). We'll see the last one first and can deal
829  * with it right away.
830  */
831 static int
efreet_menu_handle_menu(Efreet_Menu_Internal * internal,Efreet_Xml * xml)832 efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml)
833 {
834     Efreet_Xml *child;
835     Eina_List *l;
836     int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
837 
838     EINA_LIST_REVERSE_FOREACH(xml->children, l, child)
839     {
840         cb = eina_hash_find(efreet_menu_handle_cbs, child->tag);
841         if (cb)
842         {
843             if (!cb(internal, child))
844                 return 0;
845         }
846         else
847         {
848             WRN("Unknown XML tag '%s' in file '%s/%s'", child->tag, internal->file.path, internal->file.name);
849             return 0;
850         }
851     }
852     return 1;
853 }
854 
855 /**
856  * @internal
857  * @param parent The parent Menu
858  * @param xml The xml that defines the menu
859  * @return Returns 1 on success or 0 on failure
860  * @brief Handles the sub-menu nodes of the XML file
861  */
862 static int
efreet_menu_handle_sub_menu(Efreet_Menu_Internal * parent,Efreet_Xml * xml)863 efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
864 {
865     Efreet_Menu_Internal *internal, *match;
866 
867     efreet_menu_create_sub_menu_list(parent);
868 
869     internal = efreet_menu_internal_new(parent);
870     if (!internal) return 0;
871     internal->file.path = eina_stringshare_add(parent->file.path);
872     if (!efreet_menu_handle_menu(internal, xml))
873     {
874         efreet_menu_internal_free(internal);
875         return 0;
876     }
877 
878     /* if this menu already exists we just take this one and stick it on the
879      * start of the existing one */
880     if ((match = eina_list_search_unsorted(parent->sub_menus,
881                                            EINA_COMPARE_CB(efreet_menu_cb_menu_compare),
882                                            internal)))
883     {
884 
885         efreet_menu_concatenate(match, internal);
886         efreet_menu_internal_free(internal);
887     }
888     else
889         parent->sub_menus = eina_list_prepend(parent->sub_menus, internal);
890 
891     return 1;
892 }
893 
894 /**
895  * @internal
896  * @param parent The parent menu
897  * @param xml The xml tree
898  * @return Returns 1 on success or 0 on failure
899  * @brief Handles the AppDir tag
900  */
901 static int
efreet_menu_handle_app_dir(Efreet_Menu_Internal * parent,Efreet_Xml * xml)902 efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
903 {
904     const char *path;
905     Efreet_Menu_App_Dir *app_dir;
906 
907     if (!parent || !xml) return 0;
908 
909     efreet_menu_create_app_dirs_list(parent);
910     path = efreet_menu_path_get(parent, xml->text);
911     if (!path) return 0;
912 
913     /* we've already got this guy in our list we can skip it */
914     if (eina_list_search_unsorted(parent->app_dirs,
915                                   EINA_COMPARE_CB(efreet_menu_cb_app_dirs_compare),
916                                   path))
917     {
918         eina_stringshare_del(path);
919         return 1;
920     }
921 
922     app_dir = efreet_menu_app_dir_new();
923     app_dir->path = path;
924 
925     parent->app_dirs = eina_list_prepend(parent->app_dirs, app_dir);
926 
927     return 1;
928 }
929 
930 /**
931  * @internal
932  * @param parent The parent menu
933  * @param xml UNUSED
934  * @return Returns 1 on success or 0 on failure
935  * @brief Handles the DefaultAppDirs
936  */
937 static int
efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal * parent,Efreet_Xml * xml EINA_UNUSED)938 efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml EINA_UNUSED)
939 {
940     Eina_List *prepend = NULL;
941     Eina_List *dirs;
942     char *dir;
943 
944     if (!parent) return 0;
945 
946     efreet_menu_create_app_dirs_list(parent);
947     dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
948                                                                     "applications");
949     EINA_LIST_FREE(dirs, dir)
950     {
951         if (!eina_list_search_unsorted(parent->app_dirs,
952                                        EINA_COMPARE_CB(efreet_menu_cb_app_dirs_compare),
953                                        dir))
954         {
955             Efreet_Menu_App_Dir *app_dir;
956 
957             app_dir = efreet_menu_app_dir_new();
958             app_dir->path = eina_stringshare_ref(dir);
959 
960             prepend = eina_list_append(prepend, app_dir);
961         }
962 
963         eina_stringshare_del(dir);
964     }
965     parent->app_dirs = eina_list_merge(prepend, parent->app_dirs);
966 
967     return 1;
968 }
969 
970 /**
971  * @internal
972  * @param parent The parent menu
973  * @param xml The xml tree
974  * @return Returns 1 on success or 0 on failure
975  * @brief Handles the DirectoryDir tag
976  */
977 static int
efreet_menu_handle_directory_dir(Efreet_Menu_Internal * parent,Efreet_Xml * xml)978 efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
979 {
980     const char *path;
981 
982     if (!parent || !xml) return 0;
983 
984     efreet_menu_create_directory_dirs_list(parent);
985     path = efreet_menu_path_get(parent, xml->text);
986     if (!path) return 0;
987 
988     /* we've already got this guy in our list we can skip it */
989     if (eina_list_search_unsorted(parent->directory_dirs, EINA_COMPARE_CB(strcmp), path))
990     {
991         eina_stringshare_del(path);
992         return 1;
993     }
994 
995     parent->directory_dirs = eina_list_prepend(parent->directory_dirs, path);
996 
997     return 1;
998 }
999 
1000 /**
1001  * @internal
1002  * @param parent The parent menu
1003  * @param xml UNUSED
1004  * @return Returns 1 on success or 0 on failure
1005  * @brief Handles the DefaultDirectoryDirs tag
1006  */
1007 static int
efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal * parent,Efreet_Xml * xml EINA_UNUSED)1008 efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml EINA_UNUSED)
1009 {
1010     Eina_List *dirs;
1011     char *dir;
1012 
1013     if (!parent) return 0;
1014 
1015     efreet_menu_create_directory_dirs_list(parent);
1016     dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1017                                                             "desktop-directories");
1018     EINA_LIST_FREE(dirs, dir)
1019     {
1020         if (!eina_list_search_unsorted(parent->directory_dirs, EINA_COMPARE_CB(strcmp), dir))
1021             parent->directory_dirs = eina_list_prepend(parent->directory_dirs, eina_stringshare_ref(dir));
1022         eina_stringshare_del(dir);
1023     }
1024 
1025     return 1;
1026 }
1027 
1028 /**
1029  * @internal
1030  * @param parent The parent Menu
1031  * @param xml The xml to work with
1032  * @return Returns 1 on success or 0 on failure
1033  * @brief Sets the menu name from the given XML fragment.
1034  */
1035 static int
efreet_menu_handle_name(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1036 efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1037 {
1038     /* not allowed to have two Name settings in a menu */
1039     if (parent->name.internal)
1040     {
1041         INF("efreet_menu_handle_name() setting second name into menu: '%s/%s'", parent->file.path, parent->file.name);
1042         return 0;
1043     }
1044     /* ignore the name if it is empty */
1045     if (!xml->text) return 1;
1046 
1047     /* ignore the name if it contains a / */
1048     if (strchr(xml->text, '/')) return 1;
1049 
1050     parent->name.internal = eina_stringshare_add(xml->text);
1051 
1052     return 1;
1053 }
1054 
1055 /**
1056  * @internal
1057  * @param parent The parent menu
1058  * @param xml The xml tree
1059  * @return Returns 1 on success or 0 on failure
1060  * @brief Handles the Directory tag
1061  *
1062  * This just adds the given directory path to a list which we'll walk once
1063  * we've traversed the entire menu into memory.
1064  */
1065 static int
efreet_menu_handle_directory(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1066 efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1067 {
1068     if (!parent || !xml) return 0;
1069 
1070     efreet_menu_create_directories_list(parent);
1071     parent->directories = eina_list_prepend(parent->directories, eina_stringshare_add(xml->text));
1072 
1073     return 1;
1074 }
1075 
1076 /**
1077  * @internal
1078  * @param parent The parent menu
1079  * @param xml The xml tree
1080  * @return Returns 1 on success or 0 on failure
1081  * @brief Handles the OnlyUnallocated tag
1082  */
1083 static int
efreet_menu_handle_only_unallocated(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1084 efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1085 {
1086     if (!parent || !xml) return 0;
1087 
1088     /* a later instance has been seen so we can ignore this one */
1089     if (parent->seen_allocated) return 1;
1090 
1091     parent->seen_allocated = 1;
1092     parent->only_unallocated = 1;
1093 
1094     return 1;
1095 }
1096 
1097 /**
1098  * @internal
1099  * @param parent The parent menu
1100  * @param xml The xml tree
1101  * @return Returns 1 on success or 0 on failure
1102  * @brief Handles the NotOnlyUnallocated tag
1103  */
1104 static int
efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1105 efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1106 {
1107     if (!parent || !xml) return 0;
1108 
1109     /* a later instance has been seen so we can ignore this one */
1110     if (parent->seen_allocated) return 1;
1111 
1112     parent->seen_allocated = 1;
1113     parent->only_unallocated = 0;
1114 
1115     return 1;
1116 }
1117 
1118 /**
1119  * @internal
1120  * @param parent The parent menu
1121  * @param xml The xml tree
1122  * @return Returns 1 on success or 0 on failure
1123  * @brief Handles the Deleted tag
1124  */
1125 static int
efreet_menu_handle_deleted(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1126 efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1127 {
1128     if (!parent || !xml) return 0;
1129 
1130     /* a later instance has been seen so we can ignore this one */
1131     if (parent->seen_deleted) return 1;
1132 
1133     parent->seen_deleted = 1;
1134     parent->deleted = 1;
1135 
1136     return 1;
1137 }
1138 
1139 /**
1140  * @internal
1141  * @param parent The parent menu
1142  * @param xml The xml tree
1143  * @return Returns 1 on success or 0 on failure
1144  * @brief Handles the NotDeleted tag
1145  */
1146 static int
efreet_menu_handle_not_deleted(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1147 efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1148 {
1149     if (!parent || !xml) return 0;
1150 
1151     /* a later instance has been seen so we can ignore this one */
1152     if (parent->seen_deleted) return 1;
1153 
1154     parent->seen_deleted = 1;
1155     parent->deleted = 0;
1156 
1157     return 1;
1158 }
1159 
1160 /**
1161  * @internal
1162  * @param parent The parent menu
1163  * @param xml The XML tree to work with
1164  * @return Returns 1 on success or 0 on failure
1165  * @brief Handles parsing the Include tag and all subtags
1166  */
1167 static int
efreet_menu_handle_include(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1168 efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1169 {
1170     return efreet_menu_handle_filter(parent, xml,
1171                                 EFREET_MENU_FILTER_INCLUDE);
1172 }
1173 
1174 /**
1175  * @internal
1176  * @param parent The parent menu
1177  * @param xml The xml tree
1178  * @return Returns 1 on success or 0 on failure
1179  * @brief Handles the Exclude tag and all subtags
1180  */
1181 static int
efreet_menu_handle_exclude(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1182 efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1183 {
1184     return efreet_menu_handle_filter(parent, xml,
1185                                 EFREET_MENU_FILTER_EXCLUDE);
1186 }
1187 
1188 /**
1189  * @internal
1190  * @param op The filter operation
1191  * @param xml The xml tree
1192  * @return Returns 1 on success or 0 on failure
1193  * @brief Handles the Filename tag
1194  */
1195 static int
efreet_menu_handle_filename(Efreet_Menu_Filter_Op * op,Efreet_Xml * xml)1196 efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1197 {
1198     if (!op || !xml) return 0;
1199 
1200     op->filenames = eina_list_append(op->filenames, eina_stringshare_add(xml->text));
1201 
1202     return 1;
1203 }
1204 
1205 /**
1206  * @internal
1207  * @param op The filter operation
1208  * @param xml The xml tree
1209  * @return Returns 1 on success or 0 on failure
1210  * @brief Handles the Category tag
1211  */
1212 static int
efreet_menu_handle_category(Efreet_Menu_Filter_Op * op,Efreet_Xml * xml)1213 efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1214 {
1215     if (!op || !xml) return 0;
1216 
1217 
1218     op->categories = eina_list_append(op->categories, eina_stringshare_add(xml->text));
1219 
1220     return 1;
1221 }
1222 
1223 /**
1224  * @internal
1225  * @param op The filter operation
1226  * @param xml The xml tree
1227  * @return Returns 1 on success or 0 on failure
1228  * @brief Handles the All tag and all subtags
1229  */
1230 static int
efreet_menu_handle_all(Efreet_Menu_Filter_Op * op,Efreet_Xml * xml)1231 efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1232 {
1233     if (!op || !xml) return 0;
1234 
1235     op->all = 1;
1236 
1237     return 1;
1238 }
1239 
1240 /**
1241  * @internal
1242  * @param op The filter operation
1243  * @param xml The xml tree
1244  * @return Returns 1 on success or 0 on failure
1245  * @brief Handles the And tag and all subtags
1246  */
1247 static int
efreet_menu_handle_and(Efreet_Menu_Filter_Op * op,Efreet_Xml * xml)1248 efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1249 {
1250     if (!op || !xml) return 0;
1251 
1252     return efreet_menu_handle_filter_child_op(op, xml,
1253                             EFREET_MENU_FILTER_OP_AND);
1254 }
1255 
1256 /**
1257  * @internal
1258  * @param op The filter operation
1259  * @param xml The xml tree
1260  * @return Returns 1 on success or 0 on failure
1261  * @brief Handles the Or tag and all subtags
1262  */
1263 static int
efreet_menu_handle_or(Efreet_Menu_Filter_Op * op,Efreet_Xml * xml)1264 efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1265 {
1266     if (!op || !xml) return 0;
1267 
1268     return efreet_menu_handle_filter_child_op(op, xml,
1269                             EFREET_MENU_FILTER_OP_OR);
1270 }
1271 
1272 /**
1273  * @internal
1274  * @param op The filter operation
1275  * @param xml The xml tree
1276  * @return Returns 1 on success or 0 on failure
1277  * @brief Handles the Not tag and all subtags
1278  */
1279 static int
efreet_menu_handle_not(Efreet_Menu_Filter_Op * op,Efreet_Xml * xml)1280 efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1281 {
1282     if (!op || !xml) return 0;
1283 
1284     return efreet_menu_handle_filter_child_op(op, xml,
1285                             EFREET_MENU_FILTER_OP_NOT);
1286 }
1287 
1288 /**
1289  * @internal
1290  * @param parent The parent menu
1291  * @param xml The xml tree
1292  * @return Returns 1 on success or 0 on failure
1293  * @brief Handles the MergeFile tag
1294  */
1295 static int
efreet_menu_handle_merge_file(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1296 efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1297 {
1298     Eina_List *l;
1299     const char *path = NULL;
1300     const char *attr = NULL;
1301     int is_path = 1;
1302     int ret = 1;
1303 
1304     if (!parent || !xml) return 0;
1305 
1306     /* check to see if this is a path or parent type */
1307     attr = efreet_xml_attribute_get(xml, "type");
1308     if (attr && !strcmp(attr, "parent"))
1309         is_path = 0;
1310 
1311     /* we're given a path */
1312     if (is_path)
1313         path = efreet_menu_path_get(parent, xml->text);
1314 
1315     /* need to find the next menu with the same name as ours in the config
1316      * dir after ours (if we're in a config dir) */
1317     else
1318     {
1319         Eina_List *search_dirs;
1320         const char *dir, *p;
1321 
1322         if (!parent->file.path)
1323         {
1324             INF("efreet_menu_handle_merge_file() missing menu path ... '%s'", parent->file.name);
1325             return 0;
1326         }
1327 
1328         search_dirs = efreet_config_dirs_get();
1329 
1330         /* we need to find the next menu with the same name in the directory
1331          * after the on the the menu was found in. to do that we first check
1332          * if it's in the config_home_directory() if so we need to search
1333          * all of the dirs. If it isn't in the config home directory then we
1334          * scan the search dirs and look for it. The search_dirs list will
1335          * be left at the next pointer so we can start looking for the menu
1336          * from that point */
1337 
1338         dir = efreet_config_home_get();
1339         if (strncmp(dir, parent->file.path, eina_stringshare_strlen(dir)))
1340         {
1341             EINA_LIST_FOREACH(search_dirs, l, dir)
1342             {
1343                 if (!strncmp(dir, parent->file.path, eina_stringshare_strlen(dir)))
1344                     break;
1345             }
1346         }
1347 
1348         if (!dir)
1349         {
1350             INF("efreet_menu_handle_merge_file() failed to find "
1351                     "menu parent directory");
1352             return 0;
1353         }
1354 
1355         /* the parent file path may have more path then just the base
1356          * directory so we need to append that as well */
1357         p = parent->file.path + eina_stringshare_strlen(dir);
1358 
1359         /* whatever dirs are left in the search dir we need to look for the
1360          * menu with the same relative filename */
1361         EINA_LIST_FOREACH(search_dirs, l, dir)
1362         {
1363             char file[PATH_MAX];
1364 
1365             snprintf(file, sizeof(file), "%s/%s/%s", dir, p,
1366                                                         parent->file.name);
1367             if (ecore_file_exists(file))
1368             {
1369                 path = eina_stringshare_add(file);
1370                 break;
1371             }
1372         }
1373     }
1374 
1375     /* nothing to do if no file found */
1376     if (!path) return 1;
1377 
1378     if (!efreet_menu_merge(parent, xml, path))
1379         ret = 0;
1380 
1381     eina_stringshare_del(path);
1382 
1383     return ret;
1384 }
1385 
1386 /**
1387  * @internal
1388  * @param parent The parent menu to merge into
1389  * @param xml The XML to be merged
1390  * @param path The path to the .menu file to merge
1391  */
1392 static int
efreet_menu_merge(Efreet_Menu_Internal * parent,Efreet_Xml * xml,const char * path)1393 efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1394 {
1395     Efreet_Xml *merge_xml;
1396     Efreet_Menu_Internal *internal;
1397 
1398     if (!parent || !xml || !path) return 0;
1399 
1400     /* do nothing if the file doesn't exist */
1401     if (!ecore_file_exists(path)) return 1;
1402 
1403     /* don't merge the same path twice */
1404     if (eina_hash_find(parent->efreet_merged_menus, path))
1405     {
1406         return 1;
1407     }
1408 
1409     eina_hash_add(parent->efreet_merged_menus, path, (void *)1);
1410 
1411     merge_xml = efreet_xml_new(path);
1412 
1413     if (!merge_xml)
1414     {
1415         INF("efreet_menu_merge() failed to read in the "
1416                 "merge file '%s'", path);
1417         return 0;
1418     }
1419 
1420     internal = efreet_menu_internal_new(parent);
1421     if (!internal)
1422     {
1423         efreet_xml_del(merge_xml);
1424         return 0;
1425     }
1426     efreet_menu_path_set(internal, path);
1427     efreet_menu_handle_menu(internal, merge_xml);
1428     efreet_menu_concatenate(parent, internal);
1429     efreet_menu_internal_free(internal);
1430 
1431     efreet_xml_del(merge_xml);
1432 
1433     return 1;
1434 }
1435 
1436 /**
1437  * @internal
1438  * @param parent The parent menu
1439  * @param xml The xml tree
1440  * @return Returns 1 on success or 0 on failure
1441  * @brief Handles the MergeDir tag
1442  */
1443 static int
efreet_menu_handle_merge_dir(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1444 efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1445 {
1446     const char *path;
1447     int ret;
1448 
1449     if (!parent || !xml || !xml->text) return 0;
1450 
1451     path = efreet_menu_path_get(parent, xml->text);
1452     if (!path) return 1;
1453     if (!ecore_file_exists(path))
1454     {
1455         eina_stringshare_del(path);
1456         return 1;
1457     }
1458 
1459     ret = efreet_menu_merge_dir(parent, xml, path);
1460     eina_stringshare_del(path);
1461 
1462     return ret;
1463 }
1464 
1465 /**
1466  * @internal
1467  * @param parent the parent menu of the merge
1468  * @param xml The xml tree
1469  * @param path The path to the merge directory
1470  * @return Returns 1 on success or 0 on failure
1471  * @brief Find all of the .menu files in the given directory and merge them
1472  * into the @a parent menu.
1473  */
1474 static int
efreet_menu_merge_dir(Efreet_Menu_Internal * parent,Efreet_Xml * xml,const char * path)1475 efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1476 {
1477     Eina_Iterator *it;
1478     Eina_File_Direct_Info *info;
1479 
1480     if (!parent || !xml || !path) return 0;
1481 
1482     /* check to see if we've merged this directory already */
1483     if (eina_hash_find(parent->efreet_merged_dirs, path)) return 1;
1484     eina_hash_add(parent->efreet_merged_dirs, path, (void *)1);
1485 
1486     it = eina_file_direct_ls(path);
1487     if (!it) return 1;
1488 
1489     EINA_ITERATOR_FOREACH(it, info)
1490     {
1491         char *p;
1492 
1493         p = strrchr(info->path + info->name_start, '.');
1494         if (!p) continue;
1495         if (strcmp(p, ".menu")) continue;
1496 
1497         if (!efreet_menu_merge(parent, xml, info->path))
1498         {
1499             eina_iterator_free(it);
1500             return 0;
1501         }
1502     }
1503     eina_iterator_free(it);
1504 
1505     return 1;
1506 }
1507 
1508 /**
1509  * @internal
1510  * @param parent The parent menu
1511  * @param xml The xml tree
1512  * @return Returns 1 on success or 0 on failure
1513  * @brief Handles the DefaultMergeDirs tag
1514  */
1515 static int
efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1516 efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1517 {
1518     Eina_List *dirs;
1519     char path[PATH_MAX], *p = NULL;
1520     const char *pp;
1521 #ifndef STRICT_SPEC
1522     char parent_path[PATH_MAX + PATH_MAX + 128];
1523 #endif
1524 
1525     if (!parent || !xml) return 0;
1526 
1527     if ((!strcmp(parent->file.name, "gnome-applications.menu")) ||
1528         (!strcmp(parent->file.name, "kde-applications.menu")))
1529     {
1530         p = alloca(sizeof("applications"));
1531         memcpy(p, "applications", sizeof("applications"));
1532     }
1533     else
1534     {
1535         size_t len;
1536 
1537         len = strlen(efreet_menu_prefix);
1538         if (!strncmp(parent->file.name, efreet_menu_prefix, len))
1539         {
1540             pp = parent->file.name;
1541             pp += len;
1542             if (!strcmp(pp, "applications.menu"))
1543             {
1544                 p = alloca(sizeof("applications"));
1545                 memcpy(p, "applications", sizeof("applications"));
1546             }
1547         }
1548 
1549         if (!p)
1550         {
1551             char *s;
1552             size_t len2;
1553 
1554             len2 = strlen(parent->file.name) + 1;
1555             p = alloca(len2);
1556             memcpy(p, parent->file.name, len2);
1557             s = strrchr(p, '.');
1558             if (s) *s = '\0';
1559         }
1560     }
1561     snprintf(path, sizeof(path), "menus/%s-merged", p);
1562 
1563     dirs = efreet_default_dirs_get(efreet_config_home_get(),
1564                                     efreet_config_dirs_get(), path);
1565 
1566     EINA_LIST_FREE(dirs, pp)
1567     {
1568         efreet_menu_merge_dir(parent, xml, pp);
1569         eina_stringshare_del(pp);
1570     }
1571 #ifndef STRICT_SPEC
1572     /* Also check the path of the parent file */
1573     snprintf(parent_path, sizeof(parent_path), "%s/%s", parent->file.path, path);
1574     efreet_menu_merge_dir(parent, xml, parent_path);
1575 #endif
1576 
1577     return 1;
1578 }
1579 
1580 /**
1581  * @internal
1582  * @param parent The parent menu
1583  * @param xml The xml tree
1584  * @return Returns 1 on success or 0 on failure
1585  * @brief Handles the LegacyDir tag
1586  */
1587 static int
efreet_menu_handle_legacy_dir(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1588 efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1589 {
1590     Efreet_Menu_Internal *legacy;
1591 
1592     if (!parent || !xml) return 0;
1593 
1594     legacy = efreet_menu_handle_legacy_dir_helper(NULL, parent, xml->text,
1595                                 efreet_xml_attribute_get(xml, "prefix"));
1596     if (legacy)
1597     {
1598         efreet_menu_concatenate(parent, legacy);
1599         efreet_menu_internal_free(legacy);
1600     }
1601 
1602     return 1;
1603 
1604 }
1605 
1606 /**
1607  * @internal
1608  * @param parent The parent menu
1609  * @param legacy_dir The legacy directory path
1610  * @param prefix The legacy directory prefix if one set
1611  * @return Returns the Efreet_Menu_Internal representing the legacy hierarchy
1612  * @brief Handles the process of merging @a legacy_dir into @a parent menu
1613  */
1614 static Efreet_Menu_Internal *
efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal * root,Efreet_Menu_Internal * parent,const char * legacy_dir,const char * prefix)1615 efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
1616                                         Efreet_Menu_Internal *parent,
1617                                         const char *legacy_dir,
1618                                         const char *prefix)
1619 {
1620     const char *path;
1621     Efreet_Menu_Internal *legacy_internal;
1622     Efreet_Menu_Filter *filter;
1623     Efreet_Menu_App_Dir *app_dir;
1624     int count = 0;
1625     Eina_Iterator *it;
1626 
1627     if (!parent || !legacy_dir) return 0;
1628 
1629     path = efreet_menu_path_get(parent, legacy_dir);
1630 
1631     /* nothing to do if the legacy path doesn't exist */
1632     if (!path || !ecore_file_exists(path))
1633     {
1634         eina_stringshare_del(path);
1635         return NULL;
1636     }
1637 
1638     legacy_internal = efreet_menu_internal_new(parent);
1639     if (!legacy_internal)
1640         return NULL;
1641     legacy_internal->name.internal = eina_stringshare_add(ecore_file_file_get(path));
1642 
1643     /* add the legacy dir as an app dir */
1644     app_dir = efreet_menu_app_dir_new();
1645     app_dir->path = eina_stringshare_add(path);
1646     app_dir->legacy = 1;
1647     if (prefix && !strchr(prefix, '/')) app_dir->prefix = eina_stringshare_add(prefix);
1648 
1649     efreet_menu_create_app_dirs_list(legacy_internal);
1650     legacy_internal->app_dirs = eina_list_append(legacy_internal->app_dirs, app_dir);
1651 #ifndef STRICT_SPEC
1652     if (root)
1653     {
1654         /* XXX This seems wrong, but it makes efreet pass the fdo tests */
1655         app_dir = efreet_menu_app_dir_new();
1656         app_dir->path = eina_stringshare_add(path);
1657         app_dir->legacy = 1;
1658         if (prefix && !strchr(prefix, '/')) app_dir->prefix = eina_stringshare_add(prefix);
1659         root->app_dirs = eina_list_append(root->app_dirs, app_dir);
1660     }
1661 #endif
1662 
1663     /* add the legacy dir as a directory dir */
1664     efreet_menu_create_directory_dirs_list(legacy_internal);
1665     legacy_internal->directory_dirs = eina_list_append(legacy_internal->directory_dirs, eina_stringshare_add(path));
1666 
1667     /* setup a filter for all the conforming .desktop files in the legacy
1668      * dir */
1669     filter = efreet_menu_filter_new();
1670     if (!filter)
1671     {
1672         efreet_menu_internal_free(legacy_internal);
1673         return NULL;
1674     }
1675     filter->type = EFREET_MENU_FILTER_INCLUDE;
1676 
1677     filter->op->type = EFREET_MENU_FILTER_OP_OR;
1678 
1679     efreet_menu_create_filter_list(legacy_internal);
1680     legacy_internal->filters = eina_list_append(legacy_internal->filters, filter);
1681 
1682     it = eina_file_stat_ls(path);
1683     if (it)
1684     {
1685         Eina_File_Direct_Info *info;
1686 
1687         EINA_ITERATOR_FOREACH(it, info)
1688         {
1689             Efreet_Desktop *desktop = NULL;
1690             char buf[PATH_MAX];
1691             char *exten;
1692             const char *fname;
1693 
1694             fname = info->path + info->name_start;
1695             /* recurse into sub directories */
1696             if (info->type == EINA_FILE_DIR)
1697             {
1698                 Efreet_Menu_Internal *ret;
1699 
1700                 ret = efreet_menu_handle_legacy_dir_helper(root ? root : legacy_internal,
1701                         legacy_internal, info->path, prefix);
1702                 if (!ret)
1703                 {
1704                     efreet_menu_internal_free(legacy_internal);
1705                     eina_stringshare_del(path);
1706                     eina_iterator_free(it);
1707                     return NULL;
1708                 }
1709 
1710                 efreet_menu_create_sub_menu_list(legacy_internal);
1711                 legacy_internal->sub_menus = eina_list_prepend(legacy_internal->sub_menus, ret);
1712 
1713                 continue;
1714             }
1715 
1716             if (!strcmp(fname, ".directory"))
1717             {
1718                 legacy_internal->directory = efreet_desktop_get(info->path);
1719                 if (legacy_internal->directory
1720                         && legacy_internal->directory->type != EFREET_DESKTOP_TYPE_DIRECTORY)
1721                 {
1722                     efreet_desktop_free(legacy_internal->directory);
1723                     legacy_internal->directory = NULL;
1724                 }
1725                 continue;
1726             }
1727 
1728             exten = strrchr(fname, '.');
1729 
1730             if (exten && !strcmp(exten, ".desktop"))
1731                 desktop = efreet_desktop_get(info->path);
1732 
1733             if (!desktop) continue;
1734 
1735             /* if the .desktop has categories it isn't legacy */
1736             if (efreet_desktop_category_count_get(desktop) != 0)
1737             {
1738                 efreet_desktop_free(desktop);
1739                 continue;
1740             }
1741 
1742             /* XXX: This will disappear when the .desktop is free'd */
1743             efreet_desktop_category_add(desktop, "Legacy");
1744 
1745             if (prefix)
1746             {
1747                 snprintf(buf, sizeof(buf), "%s%s", prefix, fname);
1748                 filter->op->filenames = eina_list_append(filter->op->filenames, eina_stringshare_add(buf));
1749             }
1750             else
1751                 filter->op->filenames = eina_list_append(filter->op->filenames, eina_stringshare_add(fname));
1752 
1753             count++;
1754             efreet_desktop_free(desktop);
1755         }
1756         eina_iterator_free(it);
1757     }
1758 
1759     eina_stringshare_del(path);
1760     return legacy_internal;
1761 }
1762 
1763 /**
1764  * @internal
1765  * @param parent The parent menu
1766  * @param xml UNUSED
1767  * @return Returns 1 on success or 0 on failure
1768  * @brief Handles the KDELegacyDirs tag
1769  */
1770 static int
efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal * parent,Efreet_Xml * xml EINA_UNUSED)1771 efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml EINA_UNUSED)
1772 {
1773     Eina_List *l;
1774     const char *dir;
1775 
1776     if (!parent) return 0;
1777 
1778     if (!efreet_menu_kde_legacy_dirs) return 1;
1779 
1780     /* XXX if one _helper() call succeeds, we return success. should this be flipped?
1781      * (return fail if on of them failed) */
1782     EINA_LIST_FOREACH(efreet_menu_kde_legacy_dirs, l, dir)
1783     {
1784         Efreet_Menu_Internal *kde;
1785 
1786         kde = efreet_menu_handle_legacy_dir_helper(NULL, parent, dir, "kde");
1787         if (kde)
1788         {
1789             efreet_menu_concatenate(parent, kde);
1790             efreet_menu_internal_free(kde);
1791             return 1;
1792         }
1793     }
1794 
1795     return 0;
1796 }
1797 
1798 /**
1799  * @internal
1800  * @param parent The parent menu
1801  * @param xml The xml tree
1802  * @return Returns 1 on success or 0 on failure
1803  * @brief Handles the Move tag and all subtags
1804  */
1805 static int
efreet_menu_handle_move(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1806 efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1807 {
1808     Efreet_Xml *child;
1809     Eina_List *l;
1810 
1811     if (!parent || !xml) return 0;
1812 
1813     efreet_menu_create_move_list(parent);
1814 
1815     EINA_LIST_FOREACH(xml->children, l, child)
1816     {
1817         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
1818 
1819         cb = eina_hash_find(efreet_menu_move_cbs, child->tag);
1820         if (cb)
1821         {
1822             if (!cb(parent, child))
1823                 return 0;
1824         }
1825         else
1826         {
1827             INF("efreet_menu_handle_move() unknown tag found "
1828                     "in Move '%s' in file '%s/%s'", child->tag,
1829                     parent->file.path, parent->file.name);
1830             return 0;
1831         }
1832     }
1833 
1834     parent->current_move = NULL;
1835 
1836     return 1;
1837 }
1838 
1839 /**
1840  * @internal
1841  * @param parent The parent menu
1842  * @param xml The xml tree
1843  * @return Returns 1 on success or 0 on failure
1844  * @brief Handles the Old tag
1845  */
1846 static int
efreet_menu_handle_old(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1847 efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1848 {
1849     Efreet_Menu_Move *move;
1850 
1851     if (!parent || !xml || !xml->text) return 0;
1852 
1853     if (parent->current_move)
1854     {
1855         INF("efreet_menu_handle_old() saw second <Old> "
1856                 "before seeing <New> in file '%s/%s'",
1857                 parent->file.path, parent->file.name);
1858         return 0;
1859     }
1860 
1861     /* If we already moved this menu, remove the old move */
1862     /* XXX This seems wrong, but it makes efreet pass the fdo tests */
1863 #ifndef STRICT_SPEC
1864     move = eina_list_search_unsorted(parent->moves,
1865                                      EINA_COMPARE_CB(efreet_menu_cb_move_compare),
1866                                      xml->text);
1867     if (move)
1868     {
1869         parent->moves = eina_list_remove(parent->moves, move);
1870         efreet_menu_move_free(move);
1871     }
1872 #endif
1873 
1874     move = efreet_menu_move_new();
1875     move->old_name = eina_stringshare_add(xml->text);
1876 
1877     parent->current_move = move;
1878     parent->moves = eina_list_append(parent->moves, move);
1879 
1880     return 1;
1881 }
1882 
1883 /**
1884  * @internal
1885  * @param parent The parent menu
1886  * @param xml The xml tree
1887  * @return Returns 1 on success or 0 on failure
1888  * @brief Handles the New tag
1889  */
1890 static int
efreet_menu_handle_new(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1891 efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1892 {
1893     if (!parent || !xml || !xml->text) return 0;
1894 
1895     if (!parent->current_move)
1896     {
1897         INF("efreet_menu_handle_new() saw New before seeing Old in '%s/%s'",
1898             parent->file.path, parent->file.name);
1899         return 0;
1900     }
1901 
1902     parent->current_move->new_name = eina_stringshare_add(xml->text);
1903     parent->current_move = NULL;
1904 
1905     return 1;
1906 }
1907 
1908 /**
1909  * @internal
1910  * @param parent The parent menu
1911  * @param xml The xml tree
1912  * @return Returns 1 on success or 0 on failure
1913  * @brief Handles the Layout tag and all subtags
1914  */
1915 static int
efreet_menu_handle_layout(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1916 efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1917 {
1918     Efreet_Xml *child;
1919     Eina_List *l;
1920 
1921     if (!parent || !xml) return 0;
1922 
1923     /* We use the last existing layout */
1924     if (parent->layout) return 1;
1925 
1926     efreet_menu_create_layout_list(parent);
1927 
1928     EINA_LIST_FOREACH(xml->children, l, child)
1929     {
1930         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
1931 
1932         cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
1933         if (cb)
1934         {
1935             if (!cb(parent, child, 0))
1936                 return 0;
1937         }
1938         else
1939         {
1940             INF("efreet_menu_handle_move() unknown tag found "
1941                     "in Layout '%s' in file '%s/%s'", child->tag,
1942                     parent->file.path, parent->file.name);
1943             return 0;
1944         }
1945     }
1946 
1947     return 1;
1948 }
1949 
1950 /**
1951  * @internal
1952  * @param parent The parent menu
1953  * @param xml The xml tree
1954  * @return Returns 1 on success or 0 on failure
1955  * @brief Handles the DefaultLayout tag
1956  */
1957 static int
efreet_menu_handle_default_layout(Efreet_Menu_Internal * parent,Efreet_Xml * xml)1958 efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1959 {
1960     const char *val;
1961     Efreet_Xml *child;
1962     Eina_List *l;
1963 
1964     if (!parent || !xml) return 0;
1965 
1966     /* We use the last existing layout */
1967     if (parent->default_layout) return 1;
1968 
1969     val = efreet_xml_attribute_get(xml, "show_empty");
1970     if (val) parent->show_empty = !strcmp(val, "true");
1971 
1972     val = efreet_xml_attribute_get(xml, "inline");
1973     if (val) parent->in_line = !strcmp(val, "true");
1974 
1975     val = efreet_xml_attribute_get(xml, "inline_limit");
1976     if (val) parent->inline_limit = atoi(val);
1977 
1978     val = efreet_xml_attribute_get(xml, "inline_header");
1979     if (val) parent->inline_header = !strcmp(val, "true");
1980 
1981     val = efreet_xml_attribute_get(xml, "inline_alias");
1982     if (val) parent->inline_alias = !strcmp(val, "true");
1983 
1984     efreet_menu_create_default_layout_list(parent);
1985 
1986     EINA_LIST_FOREACH(xml->children, l, child)
1987     {
1988         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
1989 
1990         cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
1991         if (cb)
1992         {
1993             if (!cb(parent, child, 1))
1994                 return 0;
1995         }
1996         else
1997         {
1998             INF("efreet_menu_handle_move() unknown tag found in "
1999                     "DefaultLayout '%s' in file '%s/%s'", child->tag,
2000                     parent->file.path, parent->file.name);
2001             return 0;
2002         }
2003     }
2004 
2005     return 1;
2006 }
2007 
2008 static int
efreet_menu_handle_layout_menuname(Efreet_Menu_Internal * parent,Efreet_Xml * xml,int def)2009 efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2010 {
2011     Efreet_Menu_Layout *layout;
2012     const char *val;
2013 
2014     if (!parent || !xml) return 0;
2015 
2016     if (!xml->text)
2017     {
2018         INF("efreet_menu_handle_layout_menuname() The Menuname tag in "
2019                 "layout needs a filename in file '%s/%s'",
2020                 parent->file.path, parent->file.name);
2021         return 0;
2022     }
2023 
2024     layout = efreet_menu_layout_new();
2025     layout->type = EFREET_MENU_LAYOUT_MENUNAME;
2026     layout->name = eina_stringshare_add(xml->text);
2027 
2028     val = efreet_xml_attribute_get(xml, "show_empty");
2029     if (val) layout->show_empty = !strcmp(val, "true");
2030 
2031     val = efreet_xml_attribute_get(xml, "inline");
2032     if (val) layout->in_line = !strcmp(val, "true");
2033 
2034     val = efreet_xml_attribute_get(xml, "inline_limit");
2035     if (val) layout->inline_limit = atoi(val);
2036 
2037     val = efreet_xml_attribute_get(xml, "inline_header");
2038     if (val) layout->inline_header = !strcmp(val, "true");
2039 
2040     val = efreet_xml_attribute_get(xml, "inline_alias");
2041     if (val) layout->inline_alias = !strcmp(val, "true");
2042 
2043     if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2044     else parent->layout = eina_list_append(parent->layout, layout);
2045 
2046     return 1;
2047 }
2048 
2049 static int
efreet_menu_handle_layout_filename(Efreet_Menu_Internal * parent,Efreet_Xml * xml,int def)2050 efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2051 {
2052     Efreet_Menu_Layout *layout;
2053 
2054     if (!parent || !xml) return 0;
2055 
2056     if (!xml->text)
2057     {
2058         INF("efreet_menu_handle_layout_filename() The Filename tag in "
2059                 "layout needs a filename in file '%s/%s'",
2060                 parent->file.path, parent->file.name);
2061         return 0;
2062     }
2063 
2064     layout = efreet_menu_layout_new();
2065     layout->type = EFREET_MENU_LAYOUT_FILENAME;
2066     layout->name = eina_stringshare_add(xml->text);
2067 
2068     if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2069     else parent->layout = eina_list_append(parent->layout, layout);
2070 
2071     return 1;
2072 }
2073 
2074 static int
efreet_menu_handle_layout_separator(Efreet_Menu_Internal * parent,Efreet_Xml * xml,int def)2075 efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2076 {
2077     Efreet_Menu_Layout *layout;
2078 
2079     if (!parent || !xml) return 0;
2080 
2081     layout = efreet_menu_layout_new();
2082     layout->type = EFREET_MENU_LAYOUT_SEPARATOR;
2083     if (def)
2084         parent->default_layout = eina_list_append(parent->default_layout, layout);
2085     else
2086         parent->layout = eina_list_append(parent->layout, layout);
2087     return 1;
2088 }
2089 
2090 static int
efreet_menu_handle_layout_merge(Efreet_Menu_Internal * parent,Efreet_Xml * xml,int def)2091 efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2092 {
2093     Efreet_Menu_Layout *layout;
2094     const char *attr;
2095 
2096     if (!parent || !xml) return 0;
2097 
2098     attr = efreet_xml_attribute_get(xml, "type");
2099     if (!attr)
2100     {
2101         INF("efreet_menu_handle_layout_merge() The Merge tag in layout "
2102                 "needs a type attribute in file '%s/%s'",
2103                 parent->file.path, parent->file.name);
2104         return 0;
2105     }
2106 
2107     if (strcmp(attr, "files") && strcmp(attr, "menus") && strcmp(attr, "all"))
2108     {
2109         INF("efreet_menu_handle_layout_merge() The type attribute for "
2110                 "the Merge tag contains an unknown value '%s' in file '%s/%s'",
2111                 attr, parent->file.path, parent->file.name);
2112         return 0;
2113     }
2114 
2115     layout = efreet_menu_layout_new();
2116     layout->type = EFREET_MENU_LAYOUT_MERGE;
2117     layout->name = eina_stringshare_add(attr);
2118 
2119     if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2120     else parent->layout = eina_list_append(parent->layout, layout);
2121 
2122     return 1;
2123 }
2124 
2125 /**
2126  * @internal
2127  * @param parent The parent menu
2128  * @param xml The XML tree to parse
2129  * @param type The type of filter
2130  * @return Returns 1 on success or 0 on failure
2131  * @brief Parses the given XML tree and adds the filter to the parent menu
2132  */
2133 static int
efreet_menu_handle_filter(Efreet_Menu_Internal * parent,Efreet_Xml * xml,Efreet_Menu_Filter_Type type)2134 efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
2135                                         Efreet_Menu_Filter_Type type)
2136 {
2137     Efreet_Menu_Filter *filter;
2138 
2139     efreet_menu_create_filter_list(parent);
2140 
2141     /* filters have a default or relationship */
2142     filter = efreet_menu_filter_new();
2143     if (!filter) return 0;
2144     filter->type = type;
2145     filter->op->type = EFREET_MENU_FILTER_OP_OR;
2146 
2147     if (!efreet_menu_handle_filter_op(filter->op, xml))
2148     {
2149         efreet_menu_filter_free(filter);
2150         return 0;
2151     }
2152 
2153     parent->filters = eina_list_prepend(parent->filters, filter);
2154 
2155     return 1;
2156 }
2157 
2158 /**
2159  * @internal
2160  * @param op The operation to work with
2161  * @param xml The XML tree representing this operation
2162  * @return Returns 1 on success or 0 on failure
2163  * @brief Parses the given XML tree and populates the operation
2164  */
2165 static int
efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op * op,Efreet_Xml * xml)2166 efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
2167 {
2168     Efreet_Xml *child;
2169     Eina_List *l;
2170 
2171     EINA_LIST_FOREACH(xml->children, l, child)
2172     {
2173         int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
2174 
2175         cb = eina_hash_find(efreet_menu_filter_cbs, child->tag);
2176         if (cb)
2177         {
2178             if (!cb(op, child))
2179                 return 0;
2180         }
2181         else
2182         {
2183             INF("efreet_menu_handle_filter_op() unknown tag in filter '%s'",
2184                 child->tag);
2185             return 0;
2186         }
2187     }
2188     return 1;
2189 }
2190 
2191 /**
2192  * @internal
2193  * @return Returns a new Efreet_Menu_Filter on success or NULL on failure
2194  * @brief Creates and initializes an Efreet_Menu_Filter object
2195  */
2196 static Efreet_Menu_Filter *
efreet_menu_filter_new(void)2197 efreet_menu_filter_new(void)
2198 {
2199     Efreet_Menu_Filter *filter;
2200 
2201     filter = NEW(Efreet_Menu_Filter, 1);
2202     if (!filter) return NULL;
2203     filter->op = efreet_menu_filter_op_new();
2204     if (!filter->op)
2205     {
2206         FREE(filter);
2207         return NULL;
2208     }
2209 
2210     return filter;
2211 }
2212 
2213 /**
2214  * @internal
2215  * @param filter The filter to work with
2216  * @return Returns no data
2217  * @brief Frees the given filter and all data
2218  */
2219 static void
efreet_menu_filter_free(Efreet_Menu_Filter * filter)2220 efreet_menu_filter_free(Efreet_Menu_Filter *filter)
2221 {
2222     if (!filter) return;
2223 
2224     if (filter->op) efreet_menu_filter_op_free(filter->op);
2225     filter->op = NULL;
2226 
2227     FREE(filter);
2228 }
2229 
2230 /**
2231  * @internal
2232  * @return Returns a new Efreet_Menu_Layout on success or NULL on failure
2233  * @brief Creates and initializes an Efreet_Menu_Layout object
2234  */
2235 static Efreet_Menu_Layout *
efreet_menu_layout_new(void)2236 efreet_menu_layout_new(void)
2237 {
2238     Efreet_Menu_Layout *layout;
2239 
2240     layout = NEW(Efreet_Menu_Layout, 1);
2241     layout->show_empty = -1;
2242     layout->in_line = -1;
2243     layout->inline_limit = -1;
2244     layout->inline_header = -1;
2245     layout->inline_alias = -1;
2246 
2247     return layout;
2248 }
2249 
2250 /**
2251  * @internal
2252  * @param filter The filter to work with
2253  * @return Returns no data
2254  * @brief Frees the given filter and all data
2255  */
2256 static void
efreet_menu_layout_free(Efreet_Menu_Layout * layout)2257 efreet_menu_layout_free(Efreet_Menu_Layout *layout)
2258 {
2259     if (!layout) return;
2260 
2261     IF_RELEASE(layout->name);
2262     FREE(layout);
2263 }
2264 
2265 /**
2266  * @internal
2267  * @return Returns a new Efreet_Menu_Filter_Op on success or NULL on failure
2268  * @brief Creates and initializes an Efreet_Menu_Filter_Op structure
2269  */
2270 static Efreet_Menu_Filter_Op *
efreet_menu_filter_op_new(void)2271 efreet_menu_filter_op_new(void)
2272 {
2273     Efreet_Menu_Filter_Op *op;
2274 
2275     op = NEW(Efreet_Menu_Filter_Op, 1);
2276 
2277     return op;
2278 }
2279 
2280 /**
2281  * @internal
2282  * @param op The operation to work with
2283  * @return Returns no value.
2284  * @brief Frees the given operation and all sub data
2285  */
2286 static void
efreet_menu_filter_op_free(Efreet_Menu_Filter_Op * op)2287 efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op)
2288 {
2289     if (!op) return;
2290 
2291     IF_FREE_LIST(op->categories, eina_stringshare_del);
2292     IF_FREE_LIST(op->filenames, eina_stringshare_del);
2293     IF_FREE_LIST(op->filters, efreet_menu_filter_op_free);
2294 
2295     FREE(op);
2296 }
2297 
2298 /**
2299  * @internal
2300  * @return Returns a new Efreet_Menu_Desktop on success or NULL on failure
2301  * @brief Creates and returns an Efreet_Menu_Desktop
2302  */
2303 static Efreet_Menu_Desktop *
efreet_menu_desktop_new(void)2304 efreet_menu_desktop_new(void)
2305 {
2306     Efreet_Menu_Desktop *md;
2307 
2308     md = NEW(Efreet_Menu_Desktop, 1);
2309 
2310     return md;
2311 }
2312 
2313 /**
2314  * @internal
2315  * @param md The Efreet_Menu_Desktop to free
2316  * @return Returns no value
2317  * @brief Frees the given structure
2318  */
2319 static void
efreet_menu_desktop_free(Efreet_Menu_Desktop * md)2320 efreet_menu_desktop_free(Efreet_Menu_Desktop *md)
2321 {
2322     IF_RELEASE(md->id);
2323     if (md->desktop) efreet_desktop_free(md->desktop);
2324     FREE(md);
2325 }
2326 
2327 /**
2328  * @internal
2329  * @return Returns a new Efreet_Menu on success or NULL on failure
2330  * @brief Creates and returns an Efreet_Menu
2331  */
2332 static Efreet_Menu *
efreet_menu_entry_new(void)2333 efreet_menu_entry_new(void)
2334 {
2335     Efreet_Menu *entry;
2336 
2337     entry = NEW(Efreet_Menu, 1);
2338     entry->references = 1;
2339     return entry;
2340 }
2341 
2342 EAPI void
efreet_menu_free(Efreet_Menu * entry)2343 efreet_menu_free(Efreet_Menu *entry)
2344 {
2345     Efreet_Menu *sub;
2346 
2347     if (!entry) return;
2348 
2349     entry->references--;
2350     if (entry->references > 0) return;
2351     IF_RELEASE(entry->name);
2352     IF_RELEASE(entry->icon);
2353     EINA_LIST_FREE(entry->entries, sub)
2354         efreet_menu_free(sub);
2355     IF_RELEASE(entry->id);
2356     if (entry->desktop) efreet_desktop_free(entry->desktop);
2357     FREE(entry);
2358 }
2359 
2360 EAPI void
efreet_menu_ref(Efreet_Menu * entry)2361 efreet_menu_ref(Efreet_Menu *entry)
2362 {
2363     Efreet_Menu *sub;
2364     Eina_List *l;
2365 
2366     if (!entry) return;
2367 
2368     EINA_LIST_FOREACH(entry->entries, l, sub)
2369         efreet_menu_ref(sub);
2370     entry->references++;
2371 }
2372 
2373 EAPI void
efreet_menu_unref(Efreet_Menu * entry)2374 efreet_menu_unref(Efreet_Menu *entry)
2375 {
2376     efreet_menu_free(entry);
2377 }
2378 
2379 /**
2380  * @internal
2381  * @param op The op to add a child too
2382  * @param xml The XML tree of the child
2383  * @param type The type of child to add
2384  * @return Returns 1 on success or 0 on failure
2385  * @brief Parses the given XML tree and populates a new child operation.
2386  */
2387 static int
efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op * op,Efreet_Xml * xml,Efreet_Menu_Filter_Op_Type type)2388 efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
2389                                                 Efreet_Menu_Filter_Op_Type type)
2390 {
2391     Efreet_Menu_Filter_Op *child_op;
2392 
2393     child_op = efreet_menu_filter_op_new();
2394     child_op->type = type;
2395 
2396     if (!efreet_menu_handle_filter_op(child_op, xml))
2397     {
2398         efreet_menu_filter_op_free(child_op);
2399         return 0;
2400     }
2401 
2402     op->filters = eina_list_append(op->filters, child_op);
2403 
2404     return 1;
2405 }
2406 
2407 /**
2408  * @internal
2409  * @param menu The menu to work with
2410  * @param only_unallocated Do we only look for unallocated items?
2411  * @return Returns 1 if we've successfully processed the menu, 0 otherwise
2412  * @brief Handles the processing of the menu data to retrieve the .desktop
2413  * files for the menu
2414  */
2415 static int
efreet_menu_process(Efreet_Menu_Internal * internal,unsigned int only_unallocated)2416 efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2417 {
2418     Eina_List *l;
2419 
2420     /* a menu _MUST_ have a name */
2421     if (!internal->name.internal || (internal->name.internal[0] == '\0'))
2422         return 0;
2423 
2424     /* handle filtering out .desktop files as needed. This deals with all
2425      * .desktop files */
2426     efreet_menu_process_filters(internal, only_unallocated);
2427 
2428     if (internal->sub_menus)
2429     {
2430         Efreet_Menu_Internal *sub_internal;
2431 
2432         EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2433         {
2434             sub_internal->parent = internal;
2435             efreet_menu_process(sub_internal, only_unallocated);
2436         }
2437     }
2438 
2439     return 1;
2440 }
2441 
2442 /* This will walk through all of the app dirs and load all the .desktop
2443  * files into the cache for the menu. The .desktop files will have their
2444  * allocated flag set to 0 */
2445 static int
efreet_menu_process_dirs(Efreet_Menu_Internal * internal)2446 efreet_menu_process_dirs(Efreet_Menu_Internal *internal)
2447 {
2448     Eina_List *l;
2449 
2450     /* Scan application directories for .desktop files */
2451     if (!efreet_menu_app_dirs_process(internal))
2452         return 0;
2453 
2454     /* Scan directory directories for .directory file */
2455     if (!efreet_menu_directory_dirs_process(internal))
2456         return 0;
2457 
2458     if (internal->sub_menus)
2459     {
2460         Efreet_Menu_Internal *sub_internal;
2461 
2462         EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2463         {
2464             sub_internal->parent = internal;
2465             efreet_menu_process_dirs(sub_internal);
2466         }
2467     }
2468 
2469     return 1;
2470 }
2471 
2472 /**
2473  * @internal
2474  * @param menu the menu to process
2475  * @param only_unallocated Only handle menus that deal with unallocated items
2476  * @return Returns no value
2477  * @brief Handles the processing of the filters attached to the given menu.
2478  *
2479  * For each include filter we'll add the items to our applications array. Each
2480  * exclude filter will remove items from the applications array
2481  */
2482 static void
efreet_menu_process_filters(Efreet_Menu_Internal * internal,unsigned int only_unallocated)2483 efreet_menu_process_filters(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2484 {
2485     Efreet_Menu_Filter *filter;
2486     Efreet_Menu_Desktop *md;
2487     Eina_List *l, *ll;
2488 
2489     int included = 0;
2490 
2491     /* nothing to do if we're checking the other option */
2492     if (only_unallocated != internal->only_unallocated) return;
2493 
2494     internal->applications = eina_list_free(internal->applications);
2495 
2496     if (!internal->filters) return;
2497 
2498     EINA_LIST_FOREACH(internal->filters, l, filter)
2499     {
2500         /* skip excludes until we get an include */
2501         if (!included && (filter->type == EFREET_MENU_FILTER_EXCLUDE))
2502             continue;
2503         included = 1;
2504 
2505         if (filter->type == EFREET_MENU_FILTER_INCLUDE)
2506         {
2507             Eina_Hash *matches;
2508 
2509             matches = eina_hash_string_superfast_new(NULL);
2510             internal->applications = efreet_menu_process_app_pool(internal->app_pool, internal->applications,
2511                                         matches, filter, internal->only_unallocated);
2512             if (internal->parent)
2513             {
2514                 Efreet_Menu_Internal *parent;
2515 
2516                 parent = internal->parent;
2517                 do {
2518                     internal->applications = efreet_menu_process_app_pool(parent->app_pool,
2519                                                 internal->applications, matches, filter,
2520                                                 internal->only_unallocated);
2521                 } while ((parent = parent->parent));
2522             }
2523             eina_hash_free(matches);
2524         }
2525         else
2526         {
2527             /* check each item in our menu so far and see if it's excluded */
2528             l = internal->applications;
2529             while ((md = eina_list_data_get(l)))
2530             {
2531                 ll = eina_list_next(l);
2532                 if (efreet_menu_filter_matches(filter->op, md))
2533                     internal->applications = eina_list_remove_list(internal->applications, l);
2534                 l = ll;
2535             }
2536         }
2537     }
2538 
2539     /* sort the menu applications. we do this in process filters so it will only
2540      * be done once per menu.*/
2541     if (eina_list_count(internal->applications))
2542     {
2543         Eina_List           *l2;
2544 
2545         EINA_LIST_FOREACH_SAFE(internal->applications, l, l2, md)
2546         {
2547             if (md->desktop->no_display)
2548                 internal->applications = eina_list_remove_list(internal->applications, l);
2549         }
2550         internal->applications = eina_list_sort(internal->applications,
2551                                                 eina_list_count(internal->applications),
2552                                                 EINA_COMPARE_CB(efreet_menu_cb_md_compare));
2553     }
2554 }
2555 
2556 /**
2557  * @internal
2558  * @param pool The app pool to iterate
2559  * @param applications The list of applications to append too
2560  * @param matches The hash of previously matched ids
2561  * @param filter The menu filter to run on the pool items
2562  * @param only_unallocated Do we check only unallocated pool items?
2563  * @return Returns no value.
2564  * @brief This will iterate the items in @a pool and append them to @a
2565  * applications if they match the @a filter given and aren't previoulsy entered
2566  * in @a matches. If @a only_unallocated is set we'll only only at the
2567  * .desktop files that haven't been previoulsy matched
2568  */
2569 static Eina_List *
efreet_menu_process_app_pool(Eina_List * pool,Eina_List * applications,Eina_Hash * matches,Efreet_Menu_Filter * filter,unsigned int only_unallocated)2570 efreet_menu_process_app_pool(Eina_List *pool, Eina_List *applications,
2571                                 Eina_Hash *matches, Efreet_Menu_Filter *filter,
2572                                 unsigned int only_unallocated)
2573 {
2574     Efreet_Menu_Desktop *md;
2575     Eina_List *l;
2576 
2577     EINA_LIST_FOREACH(pool, l, md)
2578     {
2579         if (eina_hash_find(matches, md->id)) continue;
2580         if (only_unallocated && md->allocated) continue;
2581         if (efreet_menu_filter_matches(filter->op, md))
2582         {
2583             applications = eina_list_append(applications, md);
2584             eina_hash_direct_add(matches, (void *)md->id, md);
2585             md->allocated = 1;
2586         }
2587     }
2588     return applications;
2589 }
2590 
2591 /**
2592  * @internal
2593  * @param op The filter operation to execute
2594  * @param md The desktop to run the filter on
2595  * @return Returns 1 if this desktop matches the given filter, 0 otherwise
2596  * @brief This will execute the given @a filter on the given desktop
2597  */
2598 static int
efreet_menu_filter_matches(Efreet_Menu_Filter_Op * op,Efreet_Menu_Desktop * md)2599 efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2600 {
2601     if (op->type == EFREET_MENU_FILTER_OP_OR)
2602         return efreet_menu_filter_or_matches(op, md);
2603 
2604     if (op->type == EFREET_MENU_FILTER_OP_AND)
2605         return efreet_menu_filter_and_matches(op, md);
2606 
2607     if (op->type == EFREET_MENU_FILTER_OP_NOT)
2608         return efreet_menu_filter_not_matches(op, md);
2609 
2610     return 0;
2611 }
2612 
2613 /**
2614  * @internal
2615  * @param op The filter operation to execute
2616  * @param md The desktop to execute on
2617  * @return Returns 1 if the desktop matches, 0 otherwise
2618  * @brief Executes the OR operation, @a op, on the desktop, @a md.
2619  */
2620 static int
efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op * op,Efreet_Menu_Desktop * md)2621 efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2622 {
2623     Efreet_Menu_Filter_Op *child;
2624     Eina_List *l;
2625     char *t;
2626 
2627     if (op->all) return 1;
2628 
2629     if (op->categories && md->desktop->categories)
2630     {
2631         EINA_LIST_FOREACH(op->categories, l, t)
2632         {
2633             if (eina_list_search_unsorted(md->desktop->categories,
2634                                           EINA_COMPARE_CB(strcmp), t))
2635                 return 1;
2636         }
2637     }
2638 
2639     if (op->filenames)
2640     {
2641         EINA_LIST_FOREACH(op->filenames, l, t)
2642             if (t == md->id) return 1;
2643     }
2644 
2645     if (op->filters)
2646     {
2647         EINA_LIST_FOREACH(op->filters, l, child)
2648         {
2649             if (efreet_menu_filter_matches(child, md))
2650                 return 1;
2651         }
2652     }
2653 
2654     return 0;
2655 }
2656 
2657 /**
2658  * @internal
2659  * @param op The filter operation to execute
2660  * @param md The desktop to execute on
2661  * @return Returns 1 if the desktop matches, 0 otherwise
2662  * @brief Executes the AND operation, @a op, on the desktop, @a md.
2663  */
2664 static int
efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op * op,Efreet_Menu_Desktop * md)2665 efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2666 {
2667     Efreet_Menu_Filter_Op *child;
2668     Eina_List *l;
2669     char *t;
2670 
2671     if (op->categories)
2672     {
2673         if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2674             return 0;
2675 
2676         EINA_LIST_FOREACH(op->categories, l, t)
2677         {
2678             if (!eina_list_search_unsorted(md->desktop->categories,
2679                                            EINA_COMPARE_CB(strcmp), t))
2680                 return 0;
2681         }
2682     }
2683 
2684     if (op->filenames)
2685     {
2686         EINA_LIST_FOREACH(op->filenames, l, t)
2687         {
2688             if (t != md->id) return 0;
2689         }
2690     }
2691 
2692     if (op->filters)
2693     {
2694         EINA_LIST_FOREACH(op->filters, l, child)
2695         {
2696             if (!efreet_menu_filter_matches(child, md))
2697                 return 0;
2698         }
2699     }
2700 
2701     return 1;
2702 }
2703 
2704 /**
2705  * @internal
2706  * @param op The filter operation to execute
2707  * @param md The desktop to execute on
2708  * @return Returns 1 if the desktop matches, 0 otherwise
2709  * @brief Executes the NOT operation, @a op, on the desktop, @a md.
2710  */
2711 static int
efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op * op,Efreet_Menu_Desktop * md)2712 efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2713 {
2714     Efreet_Menu_Filter_Op *child;
2715     Eina_List *l;
2716     char *t;
2717 
2718     /* !all means no desktops match */
2719     if (op->all) return 0;
2720 
2721     if (op->categories)
2722     {
2723         if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2724             return 1;
2725 
2726         EINA_LIST_FOREACH(op->categories, l, t)
2727         {
2728             if (eina_list_search_unsorted(md->desktop->categories,
2729                                           EINA_COMPARE_CB(strcmp), t))
2730                 return 0;
2731         }
2732     }
2733 
2734     if (op->filenames)
2735     {
2736         EINA_LIST_FOREACH(op->filenames, l, t)
2737         {
2738             if (t == md->id) return 0;
2739         }
2740     }
2741 
2742     if (op->filters)
2743     {
2744         EINA_LIST_FOREACH(op->filters, l, child)
2745         {
2746             if (efreet_menu_filter_matches(child, md))
2747                 return 0;
2748         }
2749     }
2750 
2751     return 1;
2752 }
2753 
2754 /**
2755  * @internal
2756  * @param dest The destination menu
2757  * @param src The source menu
2758  * @return Returns no value
2759  * @brief Takes the child elements of the menu @a src and puts then on the
2760  * _start_ of the menu @a dest.
2761  */
2762 static void
efreet_menu_concatenate(Efreet_Menu_Internal * dest,Efreet_Menu_Internal * src)2763 efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src)
2764 {
2765     Efreet_Menu_Internal *submenu;
2766 
2767     if (!dest || !src) return;
2768 
2769     if (!dest->directory && src->directory)
2770     {
2771         dest->directory = src->directory;
2772         src->directory = NULL;
2773     }
2774 
2775     if (!dest->seen_allocated && src->seen_allocated)
2776     {
2777         dest->only_unallocated = src->only_unallocated;
2778         dest->seen_allocated = 1;
2779     }
2780 
2781     if (!dest->seen_deleted && src->seen_deleted)
2782     {
2783         dest->deleted = src->deleted;
2784         dest->seen_deleted = 1;
2785     }
2786 
2787     if (src->directories)
2788     {
2789         efreet_menu_create_directories_list(dest);
2790         dest->directories = eina_list_merge(src->directories, dest->directories);
2791         src->directories = NULL;
2792     }
2793 
2794     if (src->app_dirs)
2795     {
2796         efreet_menu_create_app_dirs_list(dest);
2797         dest->app_dirs = eina_list_merge(src->app_dirs, dest->app_dirs);
2798         src->app_dirs = NULL;
2799     }
2800 
2801     if (src->directory_dirs)
2802     {
2803         efreet_menu_create_directory_dirs_list(dest);
2804         dest->directory_dirs = eina_list_merge(src->directory_dirs, dest->directory_dirs);
2805         src->directory_dirs = NULL;
2806     }
2807 
2808     if (src->moves)
2809     {
2810         efreet_menu_create_move_list(dest);
2811         dest->moves = eina_list_merge(src->moves, dest->moves);
2812         src->moves = NULL;
2813     }
2814 
2815     if (src->filters)
2816     {
2817         efreet_menu_create_filter_list(dest);
2818         dest->filters = eina_list_merge(src->filters, dest->filters);
2819         src->filters = NULL;
2820     }
2821 
2822     if (src->sub_menus)
2823     {
2824         efreet_menu_create_sub_menu_list(dest);
2825 
2826         while ((submenu = eina_list_data_get(eina_list_last(src->sub_menus))))
2827         {
2828             Efreet_Menu_Internal *match;
2829 
2830             src->sub_menus = eina_list_remove(src->sub_menus, submenu);
2831             /* if this menu is in the list already we just add to that */
2832             if ((match = eina_list_search_unsorted(dest->sub_menus,
2833                                                    EINA_COMPARE_CB(efreet_menu_cb_menu_compare),
2834                                                    submenu)))
2835             {
2836                 efreet_menu_concatenate(match, submenu);
2837                 efreet_menu_internal_free(submenu);
2838             }
2839             else
2840                 dest->sub_menus = eina_list_prepend(dest->sub_menus, submenu);
2841         }
2842     }
2843 }
2844 
2845 /**
2846  * @internal
2847  * @param menu The menu to work with
2848  * @return Returns no value
2849  * @brief Handles any \<Move\> commands in the menus
2850  */
2851 static void
efreet_menu_resolve_moves(Efreet_Menu_Internal * internal)2852 efreet_menu_resolve_moves(Efreet_Menu_Internal *internal)
2853 {
2854     Efreet_Menu_Internal *child;
2855     Efreet_Menu_Move *move;
2856     Eina_List *l;
2857 
2858     /* child moves are handled before parent moves */
2859     if (internal->sub_menus)
2860     {
2861         EINA_LIST_FOREACH(internal->sub_menus, l, child)
2862             efreet_menu_resolve_moves(child);
2863     }
2864 
2865     /* nothing to do if this menu has no moves */
2866     if (!internal->moves) return;
2867 
2868     EINA_LIST_FOREACH(internal->moves, l, move)
2869     {
2870         Efreet_Menu_Internal *origin, *dest, *parent;
2871 
2872         /* if the origin path doesn't exist we do nothing */
2873         origin = efreet_menu_by_name_find(internal, move->old_name, &parent);
2874         if (!origin) continue;
2875 
2876         /* remove the origin menu from the parent */
2877         parent->sub_menus = eina_list_remove(parent->sub_menus, origin);
2878 
2879         /* if the destination path doesn't exist we just rename the origin
2880          * menu and append to the parents list of children */
2881         dest = efreet_menu_by_name_find(internal, move->new_name, &parent);
2882         if (!dest)
2883         {
2884             char *path, *tmp, *t;
2885             size_t len;
2886 
2887             /* if the dest path has /'s in it then we need to add menus to
2888              * fill out the paths */
2889             len = strlen(move->new_name) + 1;
2890             t = alloca(len);
2891             memcpy(t, move->new_name, len);
2892             tmp = t;
2893             path = strchr(tmp, '/');
2894             while (path)
2895             {
2896                 Efreet_Menu_Internal *ancestor;
2897 
2898                 *path = '\0';
2899 
2900                 ancestor = efreet_menu_internal_new(parent);
2901                 if (!ancestor) goto error;
2902                 ancestor->name.internal = eina_stringshare_add(tmp);
2903 
2904                 efreet_menu_create_sub_menu_list(parent);
2905                 parent->sub_menus = eina_list_append(parent->sub_menus, ancestor);
2906 
2907                 parent = ancestor;
2908                 tmp = ++path;
2909                 path = strchr(tmp, '/');
2910             }
2911 
2912             IF_RELEASE(origin->name.internal);
2913             origin->name.internal = eina_stringshare_add(tmp);
2914 
2915             efreet_menu_create_sub_menu_list(parent);
2916             parent->sub_menus = eina_list_append(parent->sub_menus, origin);
2917         }
2918         else
2919         {
2920             efreet_menu_concatenate(dest, origin);
2921             efreet_menu_internal_free(origin);
2922         }
2923     }
2924 error:
2925     IF_FREE_LIST(internal->moves, efreet_menu_move_free);
2926 }
2927 
2928 /**
2929  * @internal
2930  * @param menu The menu to start searching from
2931  * @param name The menu name to find
2932  * @param parent The parent of the found menu
2933  * @return Returns the menu with the given @a name or NULL if none found
2934  * @brief Searches the menu tree starting at @a menu looking for a menu with
2935  * @a name.
2936  */
2937 static Efreet_Menu_Internal *
efreet_menu_by_name_find(Efreet_Menu_Internal * internal,const char * name,Efreet_Menu_Internal ** parent)2938 efreet_menu_by_name_find(Efreet_Menu_Internal *internal, const char *name, Efreet_Menu_Internal **parent)
2939 {
2940     char *part, *tmp, *ptr;
2941     size_t len;
2942 
2943     if (parent) *parent = internal;
2944 
2945     /* find the correct parent menu */
2946     len = strlen(name) + 1;
2947     tmp = alloca(len);
2948     memcpy(tmp, name, len);
2949     ptr = tmp;
2950     part = strchr(ptr, '/');
2951     while (part)
2952     {
2953         *part = '\0';
2954 
2955         if (!(internal = eina_list_search_unsorted(internal->sub_menus,
2956                                                    EINA_COMPARE_CB(efreet_menu_cb_compare_names),
2957                                                    ptr)))
2958         {
2959             return NULL;
2960         }
2961 
2962         ptr = ++part;
2963         part = strchr(ptr, '/');
2964     }
2965 
2966     if (parent) *parent = internal;
2967 
2968     /* find the menu in the parent list */
2969     if (!(internal = eina_list_search_unsorted(internal->sub_menus,
2970                                                EINA_COMPARE_CB(efreet_menu_cb_compare_names),
2971                                                ptr)))
2972     {
2973         return NULL;
2974     }
2975 
2976     return internal;
2977 }
2978 
2979 /**
2980  * @internal
2981  * @return Returns a new Efreet_Menu_Move struct on success or NULL on failure
2982  * @brief Creates an returns a new Efreet_Menu_Move struct or NULL on failure
2983  */
2984 static Efreet_Menu_Move *
efreet_menu_move_new(void)2985 efreet_menu_move_new(void)
2986 {
2987     Efreet_Menu_Move *move;
2988 
2989     move = NEW(Efreet_Menu_Move, 1);
2990 
2991     return move;
2992 }
2993 
2994 /**
2995  * @internal
2996  * @param move The Efreet_Menu_Move to free
2997  * @return Returns no value.
2998  * @brief Frees the given move structure
2999  */
3000 static void
efreet_menu_move_free(Efreet_Menu_Move * move)3001 efreet_menu_move_free(Efreet_Menu_Move *move)
3002 {
3003     if (!move) return;
3004 
3005     IF_RELEASE(move->old_name);
3006     IF_RELEASE(move->new_name);
3007 
3008     FREE(move);
3009 }
3010 
3011 /**
3012  * @internal
3013  * @return Returns a new Efreet_Menu_App_Dir on success or NULL on failure
3014  * @brief Creates and initializes a new Efreet_Menu_App_Dir structure
3015  */
3016 static Efreet_Menu_App_Dir *
efreet_menu_app_dir_new(void)3017 efreet_menu_app_dir_new(void)
3018 {
3019     Efreet_Menu_App_Dir *dir;
3020 
3021     dir = NEW(Efreet_Menu_App_Dir, 1);
3022 
3023     return dir;
3024 }
3025 
3026 /**
3027  * @internal
3028  * @param dir The Efreet_Menu_App_Dir to free
3029  * @return Returns no value.
3030  * @brief Frees the given dir structure
3031  */
3032 static void
efreet_menu_app_dir_free(Efreet_Menu_App_Dir * dir)3033 efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir)
3034 {
3035     if (!dir) return;
3036 
3037     IF_RELEASE(dir->path);
3038     IF_RELEASE(dir->prefix);
3039     FREE(dir);
3040 }
3041 
3042 /**
3043  * @internal
3044  * @param a The app dir to compare too
3045  * @param b The path to compare too
3046  * @return Returns 0 if the strings are equals, != 0 otherwise
3047  * @brief Compares the too strings
3048  */
3049 static int
efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir * a,const char * b)3050 efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b)
3051 {
3052     if (!a->path || !b) return 1;
3053     if (a->path == b) return 0;
3054     return strcmp(a->path, b);
3055 }
3056 
3057 static void
efreet_menu_create_sub_menu_list(Efreet_Menu_Internal * internal)3058 efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal)
3059 {
3060     if (!internal || internal->sub_menus) return;
3061 
3062     internal->sub_menus = NULL;
3063 }
3064 
3065 static void
efreet_menu_create_app_dirs_list(Efreet_Menu_Internal * internal)3066 efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal)
3067 {
3068     if (!internal || internal->app_dirs) return;
3069 
3070     internal->app_dirs = NULL;
3071 }
3072 
3073 static void
efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal * internal)3074 efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal)
3075 {
3076     if (!internal || internal->directory_dirs) return;
3077 
3078     internal->directory_dirs = NULL;
3079 }
3080 
3081 static void
efreet_menu_create_move_list(Efreet_Menu_Internal * internal)3082 efreet_menu_create_move_list(Efreet_Menu_Internal *internal)
3083 {
3084     if (!internal || internal->moves) return;
3085 
3086     internal->moves = NULL;
3087 }
3088 
3089 static void
efreet_menu_create_filter_list(Efreet_Menu_Internal * internal)3090 efreet_menu_create_filter_list(Efreet_Menu_Internal *internal)
3091 {
3092     if (!internal || internal->filters) return;
3093 
3094     internal->filters = NULL;
3095 }
3096 
3097 static void
efreet_menu_create_layout_list(Efreet_Menu_Internal * internal)3098 efreet_menu_create_layout_list(Efreet_Menu_Internal *internal)
3099 {
3100     if (!internal || internal->layout) return;
3101 
3102     internal->layout = NULL;
3103 }
3104 
3105 static void
efreet_menu_create_default_layout_list(Efreet_Menu_Internal * internal)3106 efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal)
3107 {
3108     if (!internal || internal->default_layout) return;
3109 
3110     internal->default_layout = NULL;
3111 }
3112 
3113 static void
efreet_menu_create_directories_list(Efreet_Menu_Internal * internal)3114 efreet_menu_create_directories_list(Efreet_Menu_Internal *internal)
3115 {
3116     if (!internal || internal->directories) return;
3117 
3118     internal->directories = NULL;
3119 }
3120 
3121 static const char *
efreet_menu_path_get(Efreet_Menu_Internal * internal,const char * suffix)3122 efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix)
3123 {
3124     char path[PATH_MAX];
3125     size_t len;
3126 
3127     /* see if we've got an absolute or relative path */
3128     if (suffix[0] == '/')
3129         snprintf(path, sizeof(path), "%s", suffix);
3130 
3131     else
3132     {
3133         if (!internal->file.path)
3134         {
3135             INF("efreet_menu_handle_app_dir() missing menu path ... '%s'", internal->file.name);
3136             return NULL;
3137         }
3138         snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix);
3139     }
3140 
3141     len = strlen(path);
3142     while (path[len] == '/') path[len--] = '\0';
3143 
3144     return eina_stringshare_add(path);
3145 }
3146 
3147 static int
efreet_menu_cb_menu_compare(Efreet_Menu_Internal * a,Efreet_Menu_Internal * b)3148 efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b)
3149 {
3150     if (!a->name.internal || !b->name.internal) return 1;
3151     if (a->name.internal == b->name.internal) return 0;
3152     return strcmp(a->name.internal, b->name.internal);
3153 }
3154 
3155 static int
efreet_menu_app_dirs_process(Efreet_Menu_Internal * internal)3156 efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal)
3157 {
3158     Efreet_Menu_App_Dir *app_dir;
3159     Efreet_Menu_Desktop *md;
3160     Eina_List *l;
3161 
3162     EINA_LIST_FREE(internal->app_pool, md)
3163         efreet_menu_desktop_free(md);
3164 
3165     EINA_LIST_FOREACH(internal->app_dirs, l, app_dir)
3166         efreet_menu_app_dir_scan(internal, app_dir->path, app_dir->prefix, app_dir->legacy);
3167 
3168     return 1;
3169 }
3170 
3171 static int
efreet_menu_app_dir_scan(Efreet_Menu_Internal * internal,const char * path,const char * id,int legacy)3172 efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, const char *path, const char *id, int legacy)
3173 {
3174     Efreet_Desktop *desktop;
3175     Efreet_Menu_Desktop *menu_desktop;
3176     char buf2[PATH_MAX];
3177     Eina_Iterator *it;
3178     Eina_File_Direct_Info *info;
3179 
3180     it = eina_file_stat_ls(path);
3181     if (!it) return 1;
3182 
3183     EINA_ITERATOR_FOREACH(it, info)
3184     {
3185         const char *fname;
3186 
3187         fname = info->path + info->name_start;
3188         if (id)
3189             snprintf(buf2, sizeof(buf2), "%s-%s", id, fname);
3190         else
3191         {
3192             strncpy(buf2, fname, PATH_MAX);
3193             buf2[PATH_MAX - 1] = '\0';
3194         }
3195 
3196         if (info->type == EINA_FILE_DIR)
3197         {
3198             if (!legacy)
3199                 efreet_menu_app_dir_scan(internal, info->path, buf2, legacy);
3200         }
3201         else
3202         {
3203             const char *ext;
3204 
3205             ext = strrchr(fname, '.');
3206 
3207             if (!ext || strcmp(ext, ".desktop")) continue;
3208             desktop = efreet_desktop_get(info->path);
3209 
3210             if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
3211             {
3212                 if (desktop) efreet_desktop_free(desktop);
3213                 continue;
3214             }
3215             /* Don't add two files with the same id in the app pool */
3216             if (eina_list_search_unsorted(internal->app_pool,
3217                                           EINA_COMPARE_CB(efreet_menu_cb_md_compare_ids),
3218                                           buf2))
3219             {
3220                 if (desktop) efreet_desktop_free(desktop);
3221                 continue;
3222             }
3223 
3224             menu_desktop = efreet_menu_desktop_new();
3225             menu_desktop->desktop = desktop;
3226             menu_desktop->id = eina_stringshare_add(buf2);
3227             internal->app_pool = eina_list_prepend(internal->app_pool, menu_desktop);
3228         }
3229     }
3230     eina_iterator_free(it);
3231 
3232     return 1;
3233 }
3234 
3235 /**
3236  * @internal
3237  * @param menu The menu to work with
3238  * @return Returns 1 on success or 0 on failure
3239  * @brief Process the directory dirs in @a menu
3240  */
3241 static int
efreet_menu_directory_dirs_process(Efreet_Menu_Internal * internal)3242 efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal)
3243 {
3244     const char *path;
3245     Eina_List *l;
3246 
3247     if (internal->directory_dirs)
3248     {
3249         internal->directory_cache =
3250             eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
3251 
3252         EINA_LIST_REVERSE_FOREACH(internal->directory_dirs, l, path)
3253             efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache);
3254     }
3255 
3256     if (internal->directories)
3257     {
3258         EINA_LIST_REVERSE_FOREACH(internal->directories, l, path)
3259         {
3260             internal->directory = efreet_menu_directory_get(internal, path);
3261             if (internal->directory) break;
3262         }
3263     }
3264     if (!internal->directory)
3265         internal->name.name = internal->name.internal;
3266     else
3267         internal->name.name = internal->directory->name;
3268 
3269     return 1;
3270 }
3271 
3272 /**
3273  * @internal
3274  * @param path The path to scan
3275  * @param relative_path The relative portion of the path
3276  * @param cache The cache to populate
3277  * @return Returns 1 on success or 0 on failure
3278  * @brief Scans the given directory dir for .directory files and adds the
3279  * applications to the cache
3280  */
3281 static int
efreet_menu_directory_dir_scan(const char * path,const char * relative_path,Eina_Hash * cache)3282 efreet_menu_directory_dir_scan(const char *path, const char *relative_path,
3283                                 Eina_Hash *cache)
3284 {
3285     Efreet_Desktop *desktop;
3286     char buf2[PATH_MAX];
3287     Eina_Iterator *it;
3288     Eina_File_Direct_Info *info;
3289     char *ext;
3290 
3291     it = eina_file_stat_ls(path);
3292     if (!it) return 1;
3293 
3294     EINA_ITERATOR_FOREACH(it, info)
3295     {
3296         const char *fname;
3297 
3298         fname = info->path + info->name_start;
3299         if (relative_path)
3300             snprintf(buf2, sizeof(buf2), "%s/%s", relative_path, fname);
3301         else
3302         {
3303             strncpy(buf2, fname, PATH_MAX);
3304             buf2[PATH_MAX - 1] = '\0';
3305         }
3306 
3307         if (info->type == EINA_FILE_DIR)
3308             efreet_menu_directory_dir_scan(info->path, buf2, cache);
3309 
3310         else
3311         {
3312             ext = strrchr(fname, '.');
3313             if (!ext || strcmp(ext, ".directory")) continue;
3314 
3315             desktop = efreet_desktop_get(info->path);
3316             if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY)
3317             {
3318                 efreet_desktop_free(desktop);
3319                 continue;
3320             }
3321 
3322             eina_hash_del(cache, buf2, NULL);
3323             eina_hash_add(cache, buf2, desktop);
3324         }
3325     }
3326     eina_iterator_free(it);
3327 
3328     return 1;
3329 }
3330 
3331 /**
3332  * @internal
3333  * @param menu The menu to work with
3334  * @param path The path to work with
3335  * @return Returns the desktop file for this path or NULL if none exists
3336  * @brief Finds the desktop file for the given path.
3337  */
3338 static Efreet_Desktop *
efreet_menu_directory_get(Efreet_Menu_Internal * internal,const char * path)3339 efreet_menu_directory_get(Efreet_Menu_Internal *internal, const char *path)
3340 {
3341     Efreet_Desktop *dir;
3342 
3343     if (internal->directory_cache)
3344     {
3345         dir = eina_hash_find(internal->directory_cache, path);
3346         if (dir) return dir;
3347     }
3348 
3349     if (internal->parent)
3350         return efreet_menu_directory_get(internal->parent, path);
3351 
3352     return NULL;
3353 }
3354 
3355 /**
3356  * @internal
3357  * @param a The first desktop
3358  * @param b The second desktop
3359  * @return Returns the comparison of the desktop files
3360  * @brief Compares the desktop files.
3361  */
3362 static int
efreet_menu_cb_md_compare(const Efreet_Menu_Desktop * a,const Efreet_Menu_Desktop * b)3363 efreet_menu_cb_md_compare(const Efreet_Menu_Desktop *a, const Efreet_Menu_Desktop *b)
3364 {
3365 #ifdef STRICT_SPEC
3366     return strcmp(ecore_file_file_get(a->desktop->orig_path), ecore_file_file_get(b->desktop->orig_path));
3367 #else
3368     if (a->desktop->name == b->desktop->name) return 0;
3369     return strcasecmp(a->desktop->name, b->desktop->name);
3370 #endif
3371 }
3372 
3373 static int
efreet_menu_cb_compare_names(Efreet_Menu_Internal * internal,const char * name)3374 efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name)
3375 {
3376     if (internal->name.internal == name) return 0;
3377     return strcmp(internal->name.internal, name);
3378 }
3379 
3380 static int
efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop * md,const char * name)3381 efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name)
3382 {
3383     if (md->id == name) return 0;
3384     return strcmp(md->id, name);
3385 }
3386 
3387 static Efreet_Menu *
efreet_menu_layout_menu(Efreet_Menu_Internal * internal)3388 efreet_menu_layout_menu(Efreet_Menu_Internal *internal)
3389 {
3390     Efreet_Menu *entry;
3391     Eina_List *layout = NULL;
3392     Eina_List *l;
3393 
3394     if (internal->parent)
3395     {
3396         /* Copy default layout rules */
3397         if (internal->show_empty == -1)    internal->show_empty = internal->parent->show_empty;
3398         if (internal->in_line == -1)       internal->in_line = internal->parent->in_line;
3399         if (internal->inline_limit == -1)  internal->inline_limit = internal->parent->inline_limit;
3400         if (internal->inline_header == -1) internal->inline_header = internal->parent->inline_header;
3401         if (internal->inline_alias == -1)  internal->inline_alias = internal->parent->inline_alias;
3402     }
3403 
3404     if (internal->layout)
3405         layout = internal->layout;
3406 
3407     else if (internal->parent)
3408     {
3409         Efreet_Menu_Internal *parent;
3410         parent = internal->parent;
3411         do
3412         {
3413             layout = parent->default_layout;
3414             parent = parent->parent;
3415         } while (!layout && parent);
3416     }
3417 
3418     /* init entry */
3419     entry = efreet_menu_entry_new();
3420     entry->type = EFREET_MENU_ENTRY_MENU;
3421     entry->id = eina_stringshare_add(internal->name.internal);
3422     entry->name = eina_stringshare_add(internal->name.name);
3423     if (internal->directory)
3424     {
3425         entry->icon = eina_stringshare_add(internal->directory->icon);
3426         efreet_desktop_ref(internal->directory);
3427         entry->desktop = internal->directory;
3428     }
3429     entry->entries = NULL;
3430 
3431 #if 1 //STRICT_SPEC
3432     if (internal->sub_menus)
3433     {
3434         internal->sub_menus = eina_list_sort(internal->sub_menus,
3435                                              0,
3436                                              EINA_COMPARE_CB(efreet_menu_cb_menu_compare));
3437     }
3438 #endif
3439 
3440     if (layout)
3441     {
3442         Efreet_Menu_Layout *lay;
3443 
3444         EINA_LIST_FOREACH(layout, l, lay)
3445             efreet_menu_layout_entries_get(entry, internal, lay);
3446     }
3447     else
3448     {
3449         /* Default layout, first menus, then desktop */
3450         if (internal->sub_menus)
3451         {
3452             Efreet_Menu_Internal *sub;
3453 
3454             EINA_LIST_FOREACH(internal->sub_menus, l, sub)
3455             {
3456                 Efreet_Menu *sub_entry;
3457                 if ((sub->directory && sub->directory->no_display) || sub->deleted) continue;
3458                 sub_entry = efreet_menu_layout_menu(sub);
3459                 /* Don't show empty menus */
3460                 if (!sub_entry->entries)
3461                 {
3462                     efreet_menu_free(sub_entry);
3463                     continue;
3464                 }
3465                 entry->entries = eina_list_append(entry->entries, sub_entry);
3466             }
3467         }
3468 
3469         if (internal->applications)
3470         {
3471             Efreet_Menu_Desktop *md;
3472 
3473             EINA_LIST_FOREACH(internal->applications, l, md)
3474             {
3475                 Efreet_Menu *sub_entry;
3476                 sub_entry = efreet_menu_layout_desktop(md);
3477                 entry->entries = eina_list_append(entry->entries, sub_entry);
3478             }
3479         }
3480     }
3481 
3482     /* Don't keep this list around if it is empty */
3483 
3484     return entry;
3485 }
3486 
3487 static Efreet_Menu *
efreet_menu_layout_desktop(Efreet_Menu_Desktop * md)3488 efreet_menu_layout_desktop(Efreet_Menu_Desktop *md)
3489 {
3490     Efreet_Menu *entry;
3491 
3492     /* init entry */
3493     entry = efreet_menu_entry_new();
3494     entry->type = EFREET_MENU_ENTRY_DESKTOP;
3495     entry->id = eina_stringshare_add(md->id);
3496     entry->name = eina_stringshare_add(md->desktop->name);
3497     if (md->desktop->icon) entry->icon = eina_stringshare_add(md->desktop->icon);
3498     efreet_desktop_ref(md->desktop);
3499     entry->desktop = md->desktop;
3500 
3501     return entry;
3502 }
3503 
3504 static void
efreet_menu_layout_entries_get(Efreet_Menu * entry,Efreet_Menu_Internal * internal,Efreet_Menu_Layout * layout)3505 efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
3506         Efreet_Menu_Layout *layout)
3507 {
3508     Efreet_Menu *sub_entry;
3509 
3510     if (internal->sub_menus && layout->type == EFREET_MENU_LAYOUT_MENUNAME)
3511     {
3512         Efreet_Menu_Internal *sub;
3513 
3514         /* Efreet_Menu_Layout might be from DefaultLayout, so we need a local copy */
3515         int show_empty, in_line, inline_limit, inline_header, inline_alias;
3516 
3517         if (layout->show_empty == -1) show_empty = internal->show_empty;
3518         else show_empty = layout->show_empty;
3519 
3520         if (layout->in_line == -1) in_line = internal->in_line;
3521         else in_line = layout->in_line;
3522 
3523         if (layout->inline_limit == -1) inline_limit = internal->inline_limit;
3524         else inline_limit = layout->inline_limit;
3525 
3526         if (layout->inline_header == -1) inline_header = internal->inline_header;
3527         else inline_header = layout->inline_header;
3528 
3529         if (layout->inline_alias == -1) inline_alias = internal->inline_alias;
3530         else inline_alias = layout->inline_alias;
3531 
3532         sub = eina_list_search_unsorted(internal->sub_menus,
3533                                         EINA_COMPARE_CB(efreet_menu_cb_compare_names), layout->name);
3534         if (sub)
3535         {
3536             if (!(sub->directory && sub->directory->no_display) && !sub->deleted)
3537             {
3538                 sub_entry = efreet_menu_layout_menu(sub);
3539                 if (!show_empty && efreet_menu_layout_is_empty(sub_entry))
3540                     efreet_menu_free(sub_entry);
3541                 else if (in_line &&
3542                         ((inline_limit == 0) ||
3543                          (!sub_entry->entries ||
3544                           (inline_limit > 0 && eina_list_count(sub_entry->entries) <= (unsigned int)inline_limit))))
3545                 {
3546                     /* Inline */
3547                     if (!sub_entry->entries)
3548                     {
3549                         /* Can't inline an empty submenu */
3550                         entry->entries = eina_list_append(entry->entries, sub_entry);
3551                     }
3552                     else if (inline_alias && (eina_list_count(sub_entry->entries) == 1))
3553                     {
3554                         Efreet_Menu *tmp;
3555 
3556                         tmp = eina_list_data_get(sub_entry->entries);
3557                         sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3558                         IF_RELEASE(tmp->name);
3559                         tmp->name = sub_entry->name;
3560                         sub_entry->name = NULL;
3561                         IF_RELEASE(tmp->icon);
3562                         tmp->icon = sub_entry->icon;
3563                         sub_entry->icon = NULL;
3564                         entry->entries = eina_list_append(entry->entries, tmp);
3565                         efreet_menu_free(sub_entry);
3566                     }
3567                     else
3568                     {
3569                         Efreet_Menu *tmp;
3570 
3571                         if (inline_header)
3572                         {
3573                             tmp = efreet_menu_entry_new();
3574                             tmp->type = EFREET_MENU_ENTRY_HEADER;
3575                             tmp->name = sub_entry->name;
3576                             sub_entry->name = NULL;
3577                             tmp->icon = sub_entry->icon;
3578                             sub_entry->icon = NULL;
3579                             entry->entries = eina_list_append(entry->entries, tmp);
3580                         }
3581                         while ((tmp = eina_list_data_get(sub_entry->entries)))
3582                         {
3583                             sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3584                             entry->entries = eina_list_append(entry->entries, tmp);
3585                         }
3586                         efreet_menu_free(sub_entry);
3587                     }
3588                 }
3589                 else
3590                     entry->entries = eina_list_append(entry->entries, sub_entry);
3591             }
3592             internal->sub_menus = eina_list_remove(internal->sub_menus, sub);
3593             efreet_menu_internal_free(sub);
3594         }
3595     }
3596     else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME)
3597     {
3598         Efreet_Menu_Desktop *md;
3599         md = eina_list_search_unsorted(internal->applications,
3600                                        EINA_COMPARE_CB(efreet_menu_cb_md_compare_ids), layout->name);
3601         if (md)
3602         {
3603             sub_entry = efreet_menu_layout_desktop(md);
3604             entry->entries = eina_list_append(entry->entries, sub_entry);
3605             internal->applications = eina_list_remove(internal->applications, md);
3606         }
3607     }
3608     else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
3609     {
3610         if (internal->applications && !strcmp(layout->name, "files"))
3611         {
3612             Efreet_Menu_Desktop *md;
3613 
3614             while ((md = eina_list_data_get(internal->applications)))
3615             {
3616                 internal->applications = eina_list_remove_list(internal->applications,
3617                                                                internal->applications);
3618                 sub_entry = eina_list_search_unsorted(entry->entries,
3619                                                       EINA_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
3620                                                       md->desktop);
3621                 if (!sub_entry)
3622                 {
3623                     sub_entry = efreet_menu_layout_desktop(md);
3624                     entry->entries = eina_list_append(entry->entries, sub_entry);
3625                 }
3626             }
3627             internal->applications = eina_list_free(internal->applications);
3628         }
3629         else if (internal->sub_menus && !strcmp(layout->name, "menus"))
3630         {
3631             Efreet_Menu_Internal *sub;
3632 
3633             while ((sub = eina_list_data_get(internal->sub_menus)))
3634             {
3635                 internal->sub_menus = eina_list_remove_list(internal->sub_menus, internal->sub_menus);
3636                 if ((sub->directory && sub->directory->no_display) || sub->deleted)
3637                 {
3638                     efreet_menu_internal_free(sub);
3639                     continue;
3640                 }
3641                 sub_entry = eina_list_search_unsorted(entry->entries,
3642                                                       EINA_COMPARE_CB(efreet_menu_cb_entry_compare_menu),
3643                                                       sub);
3644                 if (!sub_entry)
3645                 {
3646                     sub_entry = efreet_menu_layout_menu(sub);
3647                     if (!internal->show_empty && efreet_menu_layout_is_empty(sub_entry))
3648                         efreet_menu_free(sub_entry);
3649                     else if (internal->in_line &&
3650                             ((internal->inline_limit == 0) ||
3651                              (!sub_entry->entries ||
3652                               (internal->inline_limit > 0 && eina_list_count(sub_entry->entries) <= (unsigned int)internal->inline_limit))))
3653                     {
3654                         /* Inline */
3655                         if (!sub_entry->entries)
3656                         {
3657                             /* Can't inline an empty submenu */
3658                             entry->entries = eina_list_append(entry->entries, sub_entry);
3659                         }
3660                         else if (internal->inline_alias && (eina_list_count(sub_entry->entries) == 1))
3661                         {
3662                             Efreet_Menu *tmp;
3663 
3664                             tmp = eina_list_data_get(sub_entry->entries);
3665                             sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3666                             eina_stringshare_del(tmp->name);
3667                             tmp->name = sub_entry->name;
3668                             sub_entry->name = NULL;
3669                             IF_RELEASE(tmp->icon);
3670                             if (sub_entry->icon)
3671                             {
3672                                 tmp->icon = sub_entry->icon;
3673                                 sub_entry->icon = NULL;
3674                             }
3675                             entry->entries = eina_list_append(entry->entries, tmp);
3676                             efreet_menu_free(sub_entry);
3677                         }
3678                         else
3679                         {
3680                             Efreet_Menu *tmp;
3681 
3682                             if (internal->inline_header)
3683                             {
3684                                 tmp = efreet_menu_entry_new();
3685                                 tmp->type = EFREET_MENU_ENTRY_HEADER;
3686                                 tmp->name = sub_entry->name;
3687                                 sub_entry->name = NULL;
3688                                 if (sub_entry->icon) tmp->icon = sub_entry->icon;
3689                                 sub_entry->icon = NULL;
3690                                 entry->entries = eina_list_append(entry->entries, tmp);
3691                             }
3692                             while ((tmp = eina_list_data_get(sub_entry->entries)))
3693                             {
3694                                 sub_entry->entries = eina_list_remove_list(sub_entry->entries,
3695                                                                            sub_entry->entries);
3696                                 entry->entries = eina_list_append(entry->entries, tmp);
3697                             }
3698                             efreet_menu_free(sub_entry);
3699                         }
3700                     }
3701                     else
3702                         entry->entries = eina_list_append(entry->entries, sub_entry);
3703                 }
3704                 efreet_menu_internal_free(sub);
3705             }
3706             IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
3707         }
3708         else if (internal->sub_menus && !strcmp(layout->name, "all"))
3709         {
3710             const char *orig;
3711 
3712             orig = layout->name;
3713             layout->name = "menus";
3714             efreet_menu_layout_entries_get(entry, internal, layout);
3715             layout->name = "files";
3716             efreet_menu_layout_entries_get(entry, internal, layout);
3717             layout->name = orig;
3718         }
3719     }
3720     else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
3721     {
3722         sub_entry = efreet_menu_entry_new();
3723         sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR;
3724         entry->entries = eina_list_append(entry->entries, sub_entry);
3725     }
3726 }
3727 
3728 static int
efreet_menu_cb_entry_compare_menu(Efreet_Menu * entry,Efreet_Menu_Internal * internal)3729 efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal)
3730 {
3731     if (entry->type != EFREET_MENU_ENTRY_MENU) return 1;
3732     if (!entry->name || !internal->name.name) return 1;
3733     if (entry->name == internal->name.name) return 0;
3734     return strcmp(entry->name, internal->name.name);
3735 }
3736 
3737 static int
efreet_menu_cb_entry_compare_desktop(Efreet_Menu * entry,Efreet_Desktop * desktop)3738 efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop)
3739 {
3740     if (entry->type != EFREET_MENU_ENTRY_DESKTOP) return -1;
3741     if (!entry->name || !desktop->name) return -1;
3742     if (entry->name == desktop->name) return 0;
3743     return strcmp(entry->name, desktop->name);
3744 }
3745 
3746 #ifndef STRICT_SPEC
3747 static int
efreet_menu_cb_move_compare(Efreet_Menu_Move * move,const char * old)3748 efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old)
3749 {
3750     if (!move->old_name || !old) return 1;
3751     if (move->old_name == old) return 0;
3752     return 1;
3753 }
3754 #endif
3755 
3756 static int
efreet_menu_layout_is_empty(Efreet_Menu * entry)3757 efreet_menu_layout_is_empty(Efreet_Menu *entry)
3758 {
3759     Efreet_Menu *sub_entry;
3760     Eina_List *l;
3761 
3762     if (!entry->entries) return 1;
3763 
3764     EINA_LIST_FOREACH(entry->entries, l, sub_entry)
3765     {
3766         if (sub_entry->type == EFREET_MENU_ENTRY_MENU) return 0;
3767         if (sub_entry->type == EFREET_MENU_ENTRY_DESKTOP) return 0;
3768     }
3769     return 1;
3770 }
3771 
3772 static void
efreet_menu_path_set(Efreet_Menu_Internal * internal,const char * path)3773 efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path)
3774 {
3775     char *tmp, *p;
3776     size_t len;
3777 
3778     len = strlen(path) + 1;
3779     tmp = alloca(len);
3780     memcpy(tmp, path, len);
3781     p = strrchr(tmp, '/');
3782     if (p)
3783     {
3784         *p = '\0';
3785         p++;
3786 
3787         internal->file.path = eina_stringshare_add(tmp);
3788         internal->file.name = eina_stringshare_add(p);
3789     }
3790 }
3791 
3792 static int
efreet_menu_save_menu(Efreet_Menu * menu,FILE * f,int indent)3793 efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent)
3794 {
3795     Eina_List *l;
3796 
3797     efreet_menu_save_indent(f, indent);
3798     fprintf(f, "<Menu>\n");
3799     if (menu->name)
3800     {
3801         efreet_menu_save_indent(f, indent + 1);
3802         fprintf(f, "<Name>%s</Name>\n", menu->name);
3803     }
3804 
3805     if (indent == 0)
3806     {
3807         /* Only save these for the root element */
3808         efreet_menu_save_indent(f, indent + 1);
3809         fprintf(f, "<DefaultAppDirs/>\n");
3810         efreet_menu_save_indent(f, indent + 1);
3811         fprintf(f, "<DefaultDirectoryDirs/>\n");
3812     }
3813 
3814     if (menu->desktop)
3815     {
3816         efreet_menu_save_indent(f, indent + 1);
3817         fprintf(f, "<Directory>%s</Directory>\n", menu->desktop->orig_path);
3818     }
3819 
3820     if (menu->entries)
3821     {
3822         Efreet_Menu *entry;
3823         int has_desktop = 0, has_menu = 0;
3824 
3825         efreet_menu_save_indent(f, indent + 1);
3826         fprintf(f, "<Layout>\n");
3827         EINA_LIST_FOREACH(menu->entries, l, entry)
3828         {
3829             if (entry->type == EFREET_MENU_ENTRY_MENU)
3830             {
3831                 efreet_menu_save_indent(f, indent + 2);
3832                 fprintf(f, "<Menuname>%s</Menuname>\n", entry->id);
3833                 has_menu = 1;
3834             }
3835             else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
3836             {
3837                 efreet_menu_save_indent(f, indent + 2);
3838                 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
3839                 has_desktop = 1;
3840             }
3841             else if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
3842             {
3843                 efreet_menu_save_indent(f, indent + 2);
3844                 fprintf(f, "<Separator/>\n");
3845             }
3846         }
3847         efreet_menu_save_indent(f, indent + 1);
3848         fprintf(f, "</Layout>\n");
3849 
3850         if (has_desktop)
3851         {
3852             efreet_menu_save_indent(f, indent + 1);
3853             fprintf(f, "<Include>\n");
3854             EINA_LIST_FOREACH(menu->entries, l, entry)
3855             {
3856                 if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
3857                 {
3858                     efreet_menu_save_indent(f, indent + 2);
3859                     fprintf(f, "<Filename>%s</Filename>\n", entry->id);
3860                 }
3861             }
3862             efreet_menu_save_indent(f, indent + 1);
3863             fprintf(f, "</Include>\n");
3864         }
3865 
3866         if (has_menu)
3867         {
3868             EINA_LIST_FOREACH(menu->entries, l, entry)
3869             {
3870                 if (entry->type == EFREET_MENU_ENTRY_MENU)
3871                     efreet_menu_save_menu(entry, f, indent + 1);
3872             }
3873         }
3874     }
3875     efreet_menu_save_indent(f, indent);
3876     fprintf(f, "</Menu>\n");
3877     return 1;
3878 }
3879 
3880 static int
efreet_menu_save_indent(FILE * f,int indent)3881 efreet_menu_save_indent(FILE *f, int indent)
3882 {
3883     int i;
3884 
3885     for (i = 0; i < indent; i++)
3886         fprintf(f, "  ");
3887     return 1;
3888 }
3889 
3890