1 /* vim: set sw=4 ts=4 sts=4 et: */
3 #include "efreet_private.h"
5 #define DESKTOP_VERSION 1.0
8 * The current desktop environment (e.g. "Enlightenment" or "Gnome")
10 static const char *desktop_environment = NULL;
13 * A cache of all loaded desktops, hashed by file name.
14 * Values are Efreet_Desktop structures
16 static Eina_Hash *efreet_desktop_cache = NULL;
19 * A list of the desktop types available
21 static Eina_List *efreet_desktop_types = NULL;
24 * A unique id for each tmp file created while building a command
26 static int efreet_desktop_command_file_id = 0;
29 static int cache_flush = 0;
31 EAPI int EFREET_DESKTOP_TYPE_APPLICATION = 0;
32 EAPI int EFREET_DESKTOP_TYPE_LINK = 0;
33 EAPI int EFREET_DESKTOP_TYPE_DIRECTORY = 0;
37 * Information about custom types
39 typedef struct Efreet_Desktop_Type_Info Efreet_Desktop_Type_Info;
40 struct Efreet_Desktop_Type_Info
44 Efreet_Desktop_Type_Parse_Cb parse_func;
45 Efreet_Desktop_Type_Save_Cb save_func;
46 Efreet_Desktop_Type_Free_Cb free_func;
49 static int efreet_desktop_read(Efreet_Desktop *desktop);
50 static void efreet_desktop_clear(Efreet_Desktop *desktop);
51 static Efreet_Desktop_Type_Info *efreet_desktop_type_parse(const char *type_str);
52 static void *efreet_desktop_application_fields_parse(Efreet_Desktop *desktop,
54 static void efreet_desktop_application_fields_save(Efreet_Desktop *desktop,
56 static void *efreet_desktop_link_fields_parse(Efreet_Desktop *desktop,
58 static void efreet_desktop_link_fields_save(Efreet_Desktop *desktop,
60 static int efreet_desktop_generic_fields_parse(Efreet_Desktop *desktop,
62 static void efreet_desktop_generic_fields_save(Efreet_Desktop *desktop,
64 static Eina_Bool efreet_desktop_x_fields_parse(const Eina_Hash *hash,
68 static Eina_Bool efreet_desktop_x_fields_save(const Eina_Hash *hash,
72 static int efreet_desktop_environment_check(Efreet_Ini *ini);
73 static char *efreet_string_append(char *dest, int *size,
74 int *len, const char *src);
75 static char *efreet_string_append_char(char *dest, int *size,
77 static Eina_List *efreet_desktop_command_build(Efreet_Desktop_Command *command);
78 static void efreet_desktop_command_free(Efreet_Desktop_Command *command);
79 static char *efreet_desktop_command_append_quoted(char *dest, int *size,
81 static char *efreet_desktop_command_append_icon(char *dest, int *size, int *len,
82 Efreet_Desktop *desktop);
83 static char *efreet_desktop_command_append_single(char *dest, int *size, int *len,
84 Efreet_Desktop_Command_File *file,
86 static char *efreet_desktop_command_append_multiple(char *dest, int *size, int *len,
87 Efreet_Desktop_Command *command,
90 static char *efreet_desktop_command_path_absolute(const char *path);
91 static Efreet_Desktop_Command_File *efreet_desktop_command_file_process(
92 Efreet_Desktop_Command *command,
94 static const char *efreet_desktop_command_file_uri_process(const char *uri);
95 static void efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file);
97 static void efreet_desktop_cb_download_complete(void *data, const char *file,
99 static int efreet_desktop_cb_download_progress(void *data, const char *file,
100 long int dltotal, long int dlnow,
101 long int ultotal, long int ulnow);
104 static void *efreet_desktop_exec_cb(void *data, Efreet_Desktop *desktop,
105 char *exec, int remaining);
107 static void efreet_desktop_type_info_free(Efreet_Desktop_Type_Info *info);
108 static int efreet_desktop_command_flags_get(Efreet_Desktop *desktop);
109 static void *efreet_desktop_command_execs_process(Efreet_Desktop_Command *command, Eina_List *execs);
113 * @return Returns > 0 on success or 0 on failure
114 * @brief Initialize the Desktop parser subsystem
117 efreet_desktop_init(void)
119 if (init++) return init;
120 if (!eina_stringshare_init()) return --init;
121 if (!ecore_file_init()) return --init;
123 efreet_desktop_cache = eina_hash_string_superfast_new(NULL);
124 efreet_desktop_types = NULL;
126 EFREET_DESKTOP_TYPE_APPLICATION = efreet_desktop_type_add("Application",
127 efreet_desktop_application_fields_parse,
128 efreet_desktop_application_fields_save,
130 EFREET_DESKTOP_TYPE_LINK = efreet_desktop_type_add("Link",
131 efreet_desktop_link_fields_parse,
132 efreet_desktop_link_fields_save, NULL);
133 EFREET_DESKTOP_TYPE_DIRECTORY = efreet_desktop_type_add("Directory", NULL,
141 * @returns the number of initializations left for this system
142 * @brief Attempts to shut down the subsystem if nothing else is using it
145 efreet_desktop_shutdown(void)
147 Efreet_Desktop_Type_Info *info;
149 if (--init) return init;
150 ecore_file_shutdown();
151 eina_stringshare_shutdown();
153 IF_RELEASE(desktop_environment);
154 IF_FREE_HASH(efreet_desktop_cache);
155 while (efreet_desktop_types)
157 info = eina_list_data_get(efreet_desktop_types);
158 efreet_desktop_type_info_free(info);
159 efreet_desktop_types = eina_list_remove_list(efreet_desktop_types,
160 efreet_desktop_types);
168 * @param desktop: The desktop to check
169 * @return Returns 1 if the cache is still valid, 0 otherwise
170 * @brief This will check if the desktop cache is still valid.
173 efreet_desktop_cache_check(Efreet_Desktop *desktop)
175 if (!desktop) return 0;
177 /* have we modified this file since we last read it in? */
178 if ((desktop->cache_flush != cache_flush) ||
179 (ecore_file_mod_time(desktop->orig_path) != desktop->load_time))
186 * @param file: The file to get the Efreet_Desktop from
187 * @return Returns a reference to a cached Efreet_Desktop on success, NULL
188 * on failure. This reference should not be freed.
189 * @brief Gets a reference to an Efreet_Desktop structure representing the
190 * contents of @a file or NULL if @a file is not a valid .desktop file.
192 EAPI Efreet_Desktop *
193 efreet_desktop_get(const char *file)
195 Efreet_Desktop *desktop;
197 if (efreet_desktop_cache)
199 desktop = eina_hash_find(efreet_desktop_cache, file);
202 if (efreet_desktop_cache_check(desktop))
208 efreet_desktop_clear(desktop);
209 if (efreet_desktop_read(desktop))
212 desktop->cache_flush = cache_flush;
217 eina_hash_del(efreet_desktop_cache, file, NULL);
221 desktop = efreet_desktop_new(file);
222 if (!desktop) return NULL;
224 eina_hash_add(efreet_desktop_cache, file, desktop);
230 * @param desktop: The Efreet_Desktop to ref
231 * @return Returns the new reference count
234 efreet_desktop_ref(Efreet_Desktop *desktop)
236 if (!desktop) return 0;
242 * @param file: The file to create the Efreet_Desktop from
243 * @return Returns a new empty_Efreet_Desktop on success, NULL on failure
244 * @brief Creates a new empty Efreet_Desktop structure or NULL on failure
246 EAPI Efreet_Desktop *
247 efreet_desktop_empty_new(const char *file)
249 Efreet_Desktop *desktop;
251 desktop = NEW(Efreet_Desktop, 1);
252 if (!desktop) return NULL;
254 desktop->orig_path = strdup(file);
255 desktop->load_time = ecore_file_mod_time(file);
264 * @param file: The file to create the Efreet_Desktop from
265 * @return Returns a new Efreet_Desktop on success, NULL on failure
266 * @brief Creates a new Efreet_Desktop structure initialized from the
267 * contents of @a file or NULL on failure
269 EAPI Efreet_Desktop *
270 efreet_desktop_new(const char *file)
272 Efreet_Desktop *desktop;
274 if (!ecore_file_exists(file)) return NULL;
276 desktop = NEW(Efreet_Desktop, 1);
277 if (!desktop) return NULL;
279 desktop->orig_path = strdup(file);
281 if (!efreet_desktop_read(desktop))
283 efreet_desktop_free(desktop);
288 desktop->cache_flush = cache_flush;
295 * @param desktop: The desktop to fill
296 * @return Returns 1 on success, 0 on failure
297 * @brief initialize an Efreet_Desktop from the contents of @a file
300 efreet_desktop_read(Efreet_Desktop *desktop)
306 ini = efreet_ini_new(desktop->orig_path);
309 efreet_ini_free(ini);
313 ok = efreet_ini_section_set(ini, "Desktop Entry");
314 if (!ok) ok = efreet_ini_section_set(ini, "KDE Desktop Entry");
317 printf("efreet_desktop_new error: no Desktop Entry section\n");
323 Efreet_Desktop_Type_Info *info;
325 info = efreet_desktop_type_parse(efreet_ini_string_get(ini, "Type"));
328 desktop->type = info->id;
329 desktop->version = efreet_ini_double_get(ini, "Version");
331 if (info->parse_func)
332 desktop->type_data = info->parse_func(desktop, ini);
338 if (!error && !efreet_desktop_environment_check(ini)) error = 1;
339 if (!error && !efreet_desktop_generic_fields_parse(desktop, ini)) error = 1;
341 eina_hash_foreach(ini->section, efreet_desktop_x_fields_parse, desktop);
343 efreet_ini_free(ini);
345 desktop->load_time = ecore_file_mod_time(desktop->orig_path);
354 * @param desktop: The Efreet_Desktop to work with
355 * @return Returns no value
356 * @brief Frees the Efreet_Desktop's data
359 efreet_desktop_clear(Efreet_Desktop *desktop)
363 IF_FREE(desktop->name);
364 IF_FREE(desktop->generic_name);
365 IF_FREE(desktop->comment);
366 IF_FREE(desktop->icon);
367 IF_FREE(desktop->url);
369 IF_FREE(desktop->try_exec);
370 IF_FREE(desktop->exec);
371 IF_FREE(desktop->path);
372 IF_FREE(desktop->startup_wm_class);
374 IF_FREE_LIST(desktop->only_show_in, free);
375 IF_FREE_LIST(desktop->not_show_in, free);
376 while (desktop->categories)
378 data = eina_list_data_get(desktop->categories);
379 eina_stringshare_del(data);
380 desktop->categories = eina_list_remove_list(desktop->categories, desktop->categories);
382 while (desktop->mime_types)
384 data = eina_list_data_get(desktop->mime_types);
385 eina_stringshare_del(data);
386 desktop->mime_types = eina_list_remove_list(desktop->mime_types, desktop->mime_types);
389 IF_FREE_HASH(desktop->x);
391 if (desktop->type_data)
393 Efreet_Desktop_Type_Info *info;
394 info = eina_list_nth(efreet_desktop_types, desktop->type);
396 info->free_func(desktop->type_data);
401 * @param desktop: The desktop file to save
402 * @return Returns 1 on success or 0 on failure
403 * @brief Saves any changes made to @a desktop back to the file on the
407 efreet_desktop_save(Efreet_Desktop *desktop)
409 Efreet_Desktop_Type_Info *info;
413 ini = efreet_ini_new(desktop->orig_path);
414 efreet_ini_section_add(ini, "Desktop Entry");
415 efreet_ini_section_set(ini, "Desktop Entry");
417 info = eina_list_nth(efreet_desktop_types, desktop->type);
420 efreet_ini_string_set(ini, "Type", info->type);
421 if (info->save_func) info->save_func(desktop, ini);
430 if (desktop->only_show_in)
432 val = efreet_desktop_string_list_join(desktop->only_show_in);
433 efreet_ini_string_set(ini, "OnlyShowIn", val);
436 if (desktop->not_show_in)
438 val = efreet_desktop_string_list_join(desktop->not_show_in);
439 efreet_ini_string_set(ini, "NotShowIn", val);
442 efreet_desktop_generic_fields_save(desktop, ini);
443 /* When we save the file, it should be updated to the
444 * latest version that we support! */
445 efreet_ini_double_set(ini, "Version", DESKTOP_VERSION);
447 if (!efreet_ini_save(ini, desktop->orig_path)) ok = 0;
450 if (desktop != eina_hash_find(efreet_desktop_cache, desktop->orig_path))
453 eina_hash_del(efreet_desktop_cache, desktop->orig_path, NULL);
454 eina_hash_add(efreet_desktop_cache, desktop->orig_path,
459 efreet_ini_free(ini);
464 * @param desktop: The desktop file to save
465 * @param file: The filename to save as
466 * @return Returns 1 on success or 0 on failure
467 * @brief Saves @a desktop to @a file
470 efreet_desktop_save_as(Efreet_Desktop *desktop, const char *file)
472 if (desktop == eina_hash_find(efreet_desktop_cache, desktop->orig_path))
475 eina_hash_del(efreet_desktop_cache, desktop->orig_path, NULL);
477 FREE(desktop->orig_path);
478 desktop->orig_path = strdup(file);
479 return efreet_desktop_save(desktop);
484 * @param desktop: The Efreet_Desktop to work with
485 * @return Returns no value
486 * @brief Frees the Efreet_Desktop structure and all of it's data
489 efreet_desktop_free(Efreet_Desktop *desktop)
493 if (!desktop) return;
496 if (desktop->ref > 0) return;
498 if (desktop->cached && efreet_desktop_cache)
499 eina_hash_del(efreet_desktop_cache, desktop->orig_path, NULL);
501 IF_FREE(desktop->orig_path);
503 IF_FREE(desktop->name);
504 IF_FREE(desktop->generic_name);
505 IF_FREE(desktop->comment);
506 IF_FREE(desktop->icon);
507 IF_FREE(desktop->url);
509 IF_FREE(desktop->try_exec);
510 IF_FREE(desktop->exec);
511 IF_FREE(desktop->path);
512 IF_FREE(desktop->startup_wm_class);
514 IF_FREE_LIST(desktop->only_show_in, free);
515 IF_FREE_LIST(desktop->not_show_in, free);
517 EINA_LIST_FREE(desktop->categories, str)
518 eina_stringshare_del(str);
519 EINA_LIST_FREE(desktop->mime_types, str)
520 eina_stringshare_del(str);
522 IF_FREE_HASH(desktop->x);
524 if (desktop->type_data)
526 Efreet_Desktop_Type_Info *info;
527 info = eina_list_nth(efreet_desktop_types, desktop->type);
529 info->free_func(desktop->type_data);
536 * @param desktop: The desktop file to work with
537 * @param files: The files to be substituted into the exec line
538 * @param data: The data pointer to pass
539 * @return Returns the Ecore_Exce for @a desktop
540 * @brief Parses the @a desktop exec line and returns an Ecore_Exe.
543 efreet_desktop_exec(Efreet_Desktop *desktop, Eina_List *files, void *data)
545 efreet_desktop_command_get(desktop, files, efreet_desktop_exec_cb, data);
549 efreet_desktop_exec_cb(void *data, Efreet_Desktop *desktop __UNUSED__,
550 char *exec, int remaining __UNUSED__)
552 ecore_exe_run(exec, data);
558 * @param environment: the environment name
559 * @brief sets the global desktop environment name
562 efreet_desktop_environment_set(const char *environment)
564 if (desktop_environment) eina_stringshare_del(desktop_environment);
565 if (environment) desktop_environment = eina_stringshare_add(environment);
566 else desktop_environment = NULL;
570 * @return environment: the environment name
571 * @brief sets the global desktop environment name
574 efreet_desktop_environment_get(void)
576 return desktop_environment;
580 * @param desktop: The desktop to work with
581 * @return Returns the number of categories assigned to this desktop
582 * @brief Retrieves the number of categories the given @a desktop belongs
586 efreet_desktop_category_count_get(Efreet_Desktop *desktop)
588 if (!desktop || !desktop->categories) return 0;
589 return eina_list_count(desktop->categories);
593 * @param desktop: the desktop
594 * @param category: the category name
595 * @brief add a category to a desktop
598 efreet_desktop_category_add(Efreet_Desktop *desktop, const char *category)
600 if (!desktop) return;
602 if (eina_list_search_unsorted(desktop->categories,
603 (Eina_Compare_Cb)strcmp, category)) return;
605 desktop->categories = eina_list_append(desktop->categories,
606 (void *)eina_stringshare_add(category));
610 * @param desktop: the desktop
611 * @param category: the category name
612 * @brief removes a category from a desktop
613 * @return 1 if the desktop had his category listed, 0 otherwise
616 efreet_desktop_category_del(Efreet_Desktop *desktop, const char *category)
620 if (!desktop || !desktop->categories) return 0;
622 if ((found = eina_list_search_unsorted(desktop->categories,
623 (Eina_Compare_Cb)strcmp, category)))
625 eina_stringshare_del(found);
626 desktop->categories = eina_list_remove(desktop->categories, found);
635 * @param type: The type to add to the list of matching types
636 * @param parse_func: a function to parse out custom fields
637 * @param save_func: a function to save data returned from @a parse_func
638 * @param free_func: a function to free data returned from @a parse_func
639 * @return Returns the id of the new type
640 * @brief Adds the given type to the list of types in the system
643 efreet_desktop_type_add(const char *type, Efreet_Desktop_Type_Parse_Cb parse_func,
644 Efreet_Desktop_Type_Save_Cb save_func,
645 Efreet_Desktop_Type_Free_Cb free_func)
648 Efreet_Desktop_Type_Info *info;
650 info = NEW(Efreet_Desktop_Type_Info, 1);
653 id = eina_list_count(efreet_desktop_types);
656 info->type = strdup(type);
657 info->parse_func = parse_func;
658 info->save_func = save_func;
659 info->free_func = free_func;
661 efreet_desktop_types = eina_list_append(efreet_desktop_types, info);
667 * @brief Add an alias for an existing desktop type.
668 * @param from_type the type to alias (e.g. EFREE_DESKTOP_TYPE_APPLICATION)
669 * @param alias the alias
670 * @return the new type id, or -1 if @p from_type was not valid
672 * This allows applications to add non-standard types that behave exactly as standard types.
675 efreet_desktop_type_alias(int from_type, const char *alias)
677 Efreet_Desktop_Type_Info *info;
678 info = eina_list_nth(efreet_desktop_types, from_type);
679 if (!info) return -1;
681 return efreet_desktop_type_add(alias, info->parse_func, info->save_func, info->free_func);
686 * @brief Free an Efreet Desktop_Type_Info struct
689 efreet_desktop_type_info_free(Efreet_Desktop_Type_Info *info)
697 * @brief get type specific data for custom desktop types
698 * @param desktop the desktop
699 * @return type specific data, or NULL if there is none
702 efreet_desktop_type_data_get(Efreet_Desktop *desktop)
704 return desktop->type_data;
709 * @param type_str: the type as a string
710 * @return the parsed type
711 * @brief parse the type string into an Efreet_Desktop_Type
713 static Efreet_Desktop_Type_Info *
714 efreet_desktop_type_parse(const char *type_str)
716 Efreet_Desktop_Type_Info *info;
719 if (!type_str) return NULL;
721 EINA_LIST_FOREACH(efreet_desktop_types, l, info)
723 if (!strcmp(info->type, type_str))
731 * @param string: the raw string list
732 * @return an Eina_List of ecore string's
733 * @brief Parse ';' separate list of strings according to the desktop spec
736 efreet_desktop_string_list_parse(const char *string)
738 Eina_List *list = NULL;
742 if (!string) return NULL;
744 tmp = strdup(string);
747 while ((p = strchr(s, ';')))
749 if (p > tmp && *(p-1) == '\\') continue;
751 list = eina_list_append(list, (void *)eina_stringshare_add(s));
754 /* If this is true, the .desktop file does not follow the standard */
758 printf("[Efreet]: Found a string list without ';' "
759 "at the end: %s\n", string);
761 list = eina_list_append(list, (void *)eina_stringshare_add(s));
770 * @param list: Eina_List with strings
771 * @return a raw string list
772 * @brief Create a ';' separate list of strings according to the desktop spec
775 efreet_desktop_string_list_join(Eina_List *list)
780 size_t size, pos, len;
782 if (!list) return strdup("");
785 string = malloc(size);
788 EINA_LIST_FOREACH(list, l, tmp)
792 if ((len + pos + 1) >= size)
794 size = len + pos + 1024;
795 string = realloc(string, size);
797 strcpy(string + pos, tmp);
799 strcpy(string + pos, ";");
806 * @brief Tell Efreet to flush any cached desktop entries so it reloads on get.
808 * This flags the cache to be invalid, so next time a desktop file is fetched
809 * it will force it to be re-read off disk next time efreet_desktop_get() is
813 efreet_desktop_cache_flush(void)
820 * @param desktop: the Efreet_Desktop to store parsed fields in
821 * @param ini: the Efreet_Ini to parse fields from
823 * @brief Parse application specific desktop fields
826 efreet_desktop_application_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini)
830 val = efreet_ini_string_get(ini, "TryExec");
831 if (val) desktop->try_exec = strdup(val);
833 val = efreet_ini_string_get(ini, "Exec");
834 if (val) desktop->exec = strdup(val);
836 val = efreet_ini_string_get(ini, "Path");
837 if (val) desktop->path = strdup(val);
839 val = efreet_ini_string_get(ini, "StartupWMClass");
840 if (val) desktop->startup_wm_class = strdup(val);
842 desktop->categories = efreet_desktop_string_list_parse(
843 efreet_ini_string_get(ini, "Categories"));
844 desktop->mime_types = efreet_desktop_string_list_parse(
845 efreet_ini_string_get(ini, "MimeType"));
847 desktop->terminal = efreet_ini_boolean_get(ini, "Terminal");
848 desktop->startup_notify = efreet_ini_boolean_get(ini, "StartupNotify");
855 * @param desktop: the Efreet_Desktop to save fields from
856 * @param ini: the Efreet_Ini to save fields to
857 * @return Returns no value
858 * @brief Save application specific desktop fields
861 efreet_desktop_application_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini)
865 if (desktop->try_exec)
866 efreet_ini_string_set(ini, "TryExec", desktop->try_exec);
869 efreet_ini_string_set(ini, "Exec", desktop->exec);
872 efreet_ini_string_set(ini, "Path", desktop->path);
874 if (desktop->startup_wm_class)
875 efreet_ini_string_set(ini, "StartupWMClass", desktop->startup_wm_class);
877 if (desktop->categories)
879 val = efreet_desktop_string_list_join(desktop->categories);
880 efreet_ini_string_set(ini, "Categories", val);
884 if (desktop->mime_types)
886 val = efreet_desktop_string_list_join(desktop->mime_types);
887 efreet_ini_string_set(ini, "MimeType", val);
891 efreet_ini_boolean_set(ini, "Terminal", desktop->terminal);
892 efreet_ini_boolean_set(ini, "StartupNotify", desktop->startup_notify);
897 * @param desktop: the Efreet_Desktop to store parsed fields in
898 * @param ini: the Efreet_Ini to parse fields from
899 * @return Returns no value
900 * @brief Parse link specific desktop fields
903 efreet_desktop_link_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini)
907 val = efreet_ini_string_get(ini, "URL");
908 if (val) desktop->url = strdup(val);
914 * @param desktop: the Efreet_Desktop to save fields from
915 * @param ini: the Efreet_Ini to save fields in
916 * @return Returns no value
917 * @brief Save link specific desktop fields
920 efreet_desktop_link_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini)
922 if (desktop->url) efreet_ini_string_set(ini, "URL", desktop->url);
927 * @param desktop: the Efreet_Desktop to store parsed fields in
928 * @param ini: the Efreet_Ini to parse fields from
929 * @return 1 if parsed succesfully, 0 otherwise
930 * @brief Parse desktop fields that all types can include
933 efreet_desktop_generic_fields_parse(Efreet_Desktop *desktop, Efreet_Ini *ini)
937 val = efreet_ini_localestring_get(ini, "Name");
938 if (val) desktop->name = strdup(val);
941 printf("efreet_desktop_generic_fields_parse error: no Name\n");
945 val = efreet_ini_localestring_get(ini, "GenericName");
946 if (val) desktop->generic_name = strdup(val);
948 val = efreet_ini_localestring_get(ini, "Comment");
949 if (val) desktop->comment = strdup(val);
951 val = efreet_ini_localestring_get(ini, "Icon");
952 if (val) desktop->icon = strdup(val);
954 desktop->no_display = efreet_ini_boolean_get(ini, "NoDisplay");
955 desktop->hidden = efreet_ini_boolean_get(ini, "Hidden");
962 * @param desktop: the Efreet_Desktop to save fields from
963 * @param ini: the Efreet_Ini to save fields to
964 * @return Returns nothing
965 * @brief Save desktop fields that all types can include
968 efreet_desktop_generic_fields_save(Efreet_Desktop *desktop, Efreet_Ini *ini)
974 efreet_ini_localestring_set(ini, "Name", desktop->name);
975 val = efreet_ini_string_get(ini, "Name");
977 efreet_ini_string_set(ini, "Name", desktop->name);
979 if (desktop->generic_name)
981 efreet_ini_localestring_set(ini, "GenericName", desktop->generic_name);
982 val = efreet_ini_string_get(ini, "GenericName");
984 efreet_ini_string_set(ini, "GenericName", desktop->generic_name);
986 if (desktop->comment)
988 efreet_ini_localestring_set(ini, "Comment", desktop->comment);
989 val = efreet_ini_string_get(ini, "Comment");
991 efreet_ini_string_set(ini, "Comment", desktop->comment);
995 efreet_ini_localestring_set(ini, "Icon", desktop->icon);
996 val = efreet_ini_string_get(ini, "Icon");
998 efreet_ini_string_set(ini, "Icon", desktop->icon);
1001 efreet_ini_boolean_set(ini, "NoDisplay", desktop->no_display);
1002 efreet_ini_boolean_set(ini, "Hidden", desktop->hidden);
1004 if (desktop->x) eina_hash_foreach(desktop->x, efreet_desktop_x_fields_save,
1010 * @param node: The node to work with
1011 * @param desktop: The desktop file to work with
1012 * @return Returns always true, to be used in eina_hash_foreach()
1013 * @brief Parses out an X- key from @a node and stores in @a desktop
1016 efreet_desktop_x_fields_parse(const Eina_Hash *hash __UNUSED__, const void *key, void *value, void *fdata)
1018 Efreet_Desktop * desktop = fdata;
1020 if (!desktop) return EINA_TRUE;
1021 if (strncmp(key, "X-", 2)) return EINA_TRUE;
1024 desktop->x = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
1025 eina_hash_del(desktop->x, key, NULL);
1026 eina_hash_add(desktop->x, key, (void *)eina_stringshare_add(value));
1033 * @param node: The node to work with
1034 * @param ini: The ini file to work with
1035 * @return Returns no value
1036 * @brief Stores an X- key from @a node and stores in @a ini
1039 efreet_desktop_x_fields_save(const Eina_Hash *hash __UNUSED__, const void *key, void *value, void *fdata)
1041 Efreet_Ini *ini = fdata;
1042 efreet_ini_string_set(ini, key, value);
1050 * @param ini: The Efreet_Ini to parse values from
1051 * @return 1 if desktop should be included in current environement, 0 otherwise
1052 * @brief Determines if a desktop should be included in the current environment,
1053 * based on the values of the OnlyShowIn and NotShowIn fields
1056 efreet_desktop_environment_check(Efreet_Ini *ini)
1062 if (!desktop_environment)
1065 list = efreet_desktop_string_list_parse(efreet_ini_string_get(ini, "OnlyShowIn"));
1068 EINA_LIST_FREE(list, val)
1070 if (!strcmp(val, desktop_environment))
1072 eina_stringshare_del(val);
1078 list = efreet_desktop_string_list_parse(efreet_ini_string_get(ini, "NotShowIn"));
1079 EINA_LIST_FREE(list, val)
1081 if (!strcmp(val, desktop_environment))
1083 eina_stringshare_del(val);
1091 * @param desktop: the desktop entry
1092 * @param files: an ecore list of file names to execute, as either absolute paths,
1093 * relative paths, or uris
1094 * @param func: a callback to call for each prepared command line
1095 * @param data: user data passed to the callback
1096 * @return Returns the return value of @p func on success or NULL on failure
1097 * @brief Get a command to use to execute a desktop entry.
1100 efreet_desktop_command_get(Efreet_Desktop *desktop, Eina_List *files,
1101 Efreet_Desktop_Command_Cb func, void *data)
1103 return efreet_desktop_command_progress_get(desktop, files, func, NULL, data);
1107 * @param desktop: the desktop entry
1108 * @param files an ecore list of local files, as absolute paths, local paths, or file:// uris (or NULL to get exec string with no files appended)
1109 * @return Returns an ecore list of exec strings
1110 * @brief Get the command to use to execute a desktop entry
1112 * The returned list and each of its elements must be freed.
1115 efreet_desktop_command_local_get(Efreet_Desktop *desktop, Eina_List *files)
1117 Efreet_Desktop_Command *command;
1119 Eina_List *execs, *l;
1121 if (!desktop || !desktop->exec) return NULL;
1123 command = NEW(Efreet_Desktop_Command, 1);
1124 if (!command) return 0;
1126 command->desktop = desktop;
1128 command->flags = efreet_desktop_command_flags_get(desktop);
1129 /* get the required info for each file passed in */
1132 EINA_LIST_FOREACH(files, l, file)
1134 Efreet_Desktop_Command_File *dcf;
1136 dcf = efreet_desktop_command_file_process(command, file);
1140 efreet_desktop_command_file_free(dcf);
1143 command->files = eina_list_append(command->files, dcf);
1147 execs = efreet_desktop_command_build(command);
1148 efreet_desktop_command_free(command);
1155 * @param desktop: the desktop entry
1156 * @param files: an ecore list of file names to execute, as either absolute paths,
1157 * relative paths, or uris
1158 * @param cb_command: a callback to call for each prepared command line
1159 * @param cb_progress: a callback to get progress for the downloads
1160 * @param data: user data passed to the callback
1161 * @return Returns 1 on success or 0 on failure
1162 * @brief Get a command to use to execute a desktop entry, and receive progress
1163 * updates for downloading of remote URI's passed in.
1166 efreet_desktop_command_progress_get(Efreet_Desktop *desktop, Eina_List *files,
1167 Efreet_Desktop_Command_Cb cb_command,
1168 Efreet_Desktop_Progress_Cb cb_progress,
1171 Efreet_Desktop_Command *command;
1176 if (!desktop || !cb_command || !desktop->exec) return NULL;
1178 command = NEW(Efreet_Desktop_Command, 1);
1179 if (!command) return NULL;
1181 command->cb_command = cb_command;
1182 command->cb_progress = cb_progress;
1183 command->data = data;
1184 command->desktop = desktop;
1186 command->flags = efreet_desktop_command_flags_get(desktop);
1187 /* get the required info for each file passed in */
1190 EINA_LIST_FOREACH(files, l, file)
1192 Efreet_Desktop_Command_File *dcf;
1194 dcf = efreet_desktop_command_file_process(command, file);
1196 command->files = eina_list_append(command->files, dcf);
1197 command->num_pending += dcf->pending;
1201 if (command->num_pending == 0)
1204 execs = efreet_desktop_command_build(command);
1205 ret = efreet_desktop_command_execs_process(command, execs);
1206 eina_list_free(execs);
1207 efreet_desktop_command_free(command);
1216 * @brief Determine which file related field codes are present in the Exec string of a .desktop
1217 * @params desktop and Efreet Desktop
1218 * @return a bitmask of file field codes present in exec string
1221 efreet_desktop_command_flags_get(Efreet_Desktop *desktop)
1225 /* first, determine which fields are present in the Exec string */
1226 p = strchr(desktop->exec, '%');
1234 flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
1238 flags |= EFREET_DESKTOP_EXEC_FLAG_URI;
1242 flags |= EFREET_DESKTOP_EXEC_FLAG_DIR;
1246 flags |= EFREET_DESKTOP_EXEC_FLAG_FILE;
1258 /* NON-SPEC!!! this is to work around LOTS of 'broken' .desktop files that
1259 * do not specify %U/%u, %F/F etc. etc. at all. just a command. this is
1260 * unlikely to be fixed in distributions etc. in the long run as gnome/kde
1261 * seem to have workarounds too so no one notices.
1263 if (!flags) flags |= EFREET_DESKTOP_EXEC_FLAG_FULLPATH;
1273 * @brief Call the command callback for each exec in the list
1278 efreet_desktop_command_execs_process(Efreet_Desktop_Command *command, Eina_List *execs)
1285 num = eina_list_count(execs);
1286 EINA_LIST_FOREACH(execs, l, exec)
1288 ret = command->cb_command(command->data, command->desktop, exec, --num);
1295 * @brief Builds the actual exec string from the raw string and a list of
1296 * processed filename information. The callback passed in to
1297 * efreet_desktop_command_get is called for each exec string created.
1299 * @param command: the command to build
1300 * @return a list of executable strings
1303 efreet_desktop_command_build(Efreet_Desktop_Command *command)
1305 Eina_List *execs = NULL;
1309 /* if the Exec field appends multiple, that will run the list to the end,
1310 * causing this loop to only run once. otherwise, this loop will generate a
1311 * command for each file in the list. if the list is empty, this
1312 * will run once, removing any file field codes */
1318 int size = PATH_MAX;
1320 Efreet_Desktop_Command_File *file = eina_list_data_get(l);
1322 exec = malloc(size);
1323 p = command->desktop->exec;
1328 if (len >= size - 1)
1331 exec = realloc(exec, size);
1334 /* XXX handle fields inside quotes? */
1346 exec = efreet_desktop_command_append_single(exec, &size,
1357 exec = efreet_desktop_command_append_multiple(exec, &size,
1363 exec = efreet_desktop_command_append_icon(exec, &size, &len,
1367 exec = efreet_desktop_command_append_quoted(exec, &size, &len,
1368 command->desktop->name);
1371 exec = efreet_desktop_command_append_quoted(exec, &size, &len,
1372 command->desktop->orig_path);
1376 printf("[Efreet]: Deprecated conversion char: '%c' in file '%s'\n",
1377 *p, command->desktop->orig_path);
1384 printf("[Efreet]: Unknown conversion character: '%c'\n", *p);
1389 else exec[len++] = *p;
1394 /* NON-SPEC!!! this is to work around LOTS of 'broken' .desktop files that
1395 * do not specify %U/%u, %F/F etc. etc. at all. just a command. this is
1396 * unlikely to be fixed in distributions etc. in the long run as gnome/kde
1397 * seem to have workarounds too so no one notices.
1399 if ((file) && (!file_added))
1401 printf("[Efreet]: %s\n"
1403 " has no file path/uri spec info for executing this app WITH a\n"
1404 " file/uri as a parameter. This is unlikely to be the intent.\n"
1405 " please check the .desktop file and fix it by adding a %%U or %%F\n"
1406 " or something appropriate.",
1407 command->desktop->orig_path, command->desktop->exec);
1408 if (len >= size - 1)
1411 exec = realloc(exec, size);
1414 exec = efreet_desktop_command_append_multiple(exec, &size,
1415 &len, command, 'F');
1421 execs = eina_list_append(execs, exec);
1423 /* If no file was added, then the Exec field doesn't contain any file
1424 * fields (fFuUdDnN). We only want to run the app once in this case. */
1425 if (!file_added) break;
1427 while ((l = eina_list_next(l)) != NULL);
1433 efreet_desktop_command_free(Efreet_Desktop_Command *command)
1435 Efreet_Desktop_Command_File *dcf;
1437 if (!command) return;
1439 while (command->files)
1441 dcf = eina_list_data_get(command->files);
1442 efreet_desktop_command_file_free(dcf);
1443 command->files = eina_list_remove_list(command->files,
1450 efreet_desktop_command_append_quoted(char *dest, int *size, int *len, char *src)
1452 if (!src) return dest;
1453 dest = efreet_string_append(dest, size, len, "'");
1455 /* single quotes in src need to be escaped */
1456 if (strchr(src, '\''))
1463 dest = efreet_string_append(dest, size, len, "\'\\\'");
1465 dest = efreet_string_append_char(dest, size, len, *p);
1470 dest = efreet_string_append(dest, size, len, src);
1472 dest = efreet_string_append(dest, size, len, "'");
1478 efreet_desktop_command_append_multiple(char *dest, int *size, int *len,
1479 Efreet_Desktop_Command *command,
1482 Efreet_Desktop_Command_File *file;
1486 if (!command->files) return dest;
1488 EINA_LIST_FOREACH(command->files, l, file)
1493 dest = efreet_string_append_char(dest, size, len, ' ');
1495 dest = efreet_desktop_command_append_single(dest, size, len,
1496 file, tolower(type));
1503 efreet_desktop_command_append_single(char *dest, int *size, int *len,
1504 Efreet_Desktop_Command_File *file,
1511 str = file->fullpath;
1523 printf("Invalid type passed to efreet_desktop_command_append_single:"
1528 if (!str) return dest;
1530 dest = efreet_desktop_command_append_quoted(dest, size, len, str);
1536 efreet_desktop_command_append_icon(char *dest, int *size, int *len,
1537 Efreet_Desktop *desktop)
1539 if (!desktop->icon || !desktop->icon[0]) return dest;
1541 dest = efreet_string_append(dest, size, len, "--icon ");
1542 dest = efreet_desktop_command_append_quoted(dest, size, len, desktop->icon);
1549 * Append a string to a buffer, reallocating as necessary.
1552 efreet_string_append(char *dest, int *size, int *len, const char *src)
1557 l = ecore_strlcpy(dest + *len, src, *size - *len);
1559 while (l > *size - *len)
1561 /* we successfully appended this much */
1562 off += *size - *len - 1;
1565 dest = realloc(dest, *size);
1566 *(dest + *len) = '\0';
1568 l = ecore_strlcpy(dest + *len, src + off, *size - *len);
1576 efreet_string_append_char(char *dest, int *size, int *len, char c)
1578 if (*len >= *size - 1)
1581 dest = realloc(dest, *size);
1591 * @param command: the Efreet_Desktop_Comand that this file is for
1592 * @param file: the filname as either an absolute path, relative path, or URI
1594 static Efreet_Desktop_Command_File *
1595 efreet_desktop_command_file_process(Efreet_Desktop_Command *command, const char *file)
1597 Efreet_Desktop_Command_File *f;
1598 const char *uri, *base;
1601 printf("FLAGS: %d, %d, %d, %d\n",
1602 command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH ? 1 : 0,
1603 command->flags & EFREET_DESKTOP_EXEC_FLAG_URI ? 1 : 0,
1604 command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR ? 1 : 0,
1605 command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE ? 1 : 0);
1607 f = NEW(Efreet_Desktop_Command_File, 1);
1608 if (!f) return NULL;
1610 f->command = command;
1613 if(!strncmp(file, "http://", 7) || !strncmp(file, "ftp://", 6))
1616 base = ecore_file_file_get(file);
1620 else if (!strncmp(file, "file:", 5))
1622 file = efreet_desktop_command_file_uri_process(file);
1625 efreet_desktop_command_file_free(f);
1632 /* process non-local uri */
1633 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH)
1637 snprintf(buf, PATH_MAX, "/tmp/%d-%d-%s", getpid(),
1638 efreet_desktop_command_file_id++, base);
1639 f->fullpath = strdup(buf);
1642 ecore_file_download(uri, f->fullpath, efreet_desktop_cb_download_complete,
1643 efreet_desktop_cb_download_progress, f);
1646 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
1647 f->uri = strdup(uri);
1648 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR)
1649 f->dir = strdup("/tmp");
1650 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE)
1651 f->file = strdup(base);
1655 char *abs = efreet_desktop_command_path_absolute(file);
1656 /* process local uri/path */
1657 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FULLPATH)
1658 f->fullpath = strdup(abs);
1660 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_URI)
1663 snprintf(buf, sizeof(buf), "file://%s", abs);
1664 f->uri = strdup(buf);
1666 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_DIR)
1667 f->dir = ecore_file_dir_get(abs);
1668 if (command->flags & EFREET_DESKTOP_EXEC_FLAG_FILE)
1669 f->file = strdup(ecore_file_file_get(file));
1674 printf(" fullpath: %s\n", f->fullpath);
1675 printf(" uri: %s\n", f->uri);
1676 printf(" dir: %s\n", f->dir);
1677 printf(" file: %s\n", f->file);
1683 * @brief Find the local path portion of a file uri.
1684 * @param uri: a uri beginning with "file:"
1685 * @return the location of the path portion of the uri,
1686 * or NULL if the file is not on this machine
1689 efreet_desktop_command_file_uri_process(const char *uri)
1691 const char *path = NULL;
1692 int len = strlen(uri);
1694 /* uri:foo/bar => relative path foo/bar*/
1695 if (len >= 4 && uri[5] != '/')
1696 path = uri + strlen("file:");
1698 /* uri:/foo/bar => absolute path /foo/bar */
1699 else if (len >= 5 && uri[6] != '/')
1700 path = uri + strlen("file:");
1702 /* uri://foo/bar => absolute path /bar on machine foo */
1703 else if (len >= 6 && uri[7] != '/')
1706 char hostname[PATH_MAX];
1707 tmp = strdup(uri + 7);
1708 p = strchr(tmp, '/');
1712 if (!strcmp(tmp, "localhost"))
1713 path = uri + strlen("file://localhost");
1718 ret = gethostname(hostname, PATH_MAX);
1719 if ((ret == 0) && !strcmp(tmp, hostname))
1720 path = uri + strlen("file://") + strlen(hostname);
1726 /* uri:///foo/bar => absolute path /foo/bar on local machine */
1728 path = uri + strlen("file://");
1734 efreet_desktop_command_file_free(Efreet_Desktop_Command_File *file)
1738 IF_FREE(file->fullpath);
1741 IF_FREE(file->file);
1748 efreet_desktop_cb_download_complete(void *data, const char *file __UNUSED__,
1749 int status __UNUSED__)
1751 Efreet_Desktop_Command_File *f;
1755 /* XXX check status... error handling, etc */
1757 f->command->num_pending--;
1759 if (f->command->num_pending <= 0)
1762 execs = efreet_desktop_command_build(f->command);
1763 /* TODO: Need to handle the return value from efreet_desktop_command_execs_process */
1764 efreet_desktop_command_execs_process(f->command, execs);
1765 eina_list_free(execs);
1766 efreet_desktop_command_free(f->command);
1771 efreet_desktop_cb_download_progress(void *data,
1772 const char *file __UNUSED__,
1773 long int dltotal, long int dlnow,
1774 long int ultotal __UNUSED__,
1775 long int ulnow __UNUSED__)
1777 Efreet_Desktop_Command_File *dcf;
1780 if (dcf->command->cb_progress)
1781 return dcf->command->cb_progress(dcf->command->data,
1782 dcf->command->desktop,
1783 dcf->uri, dltotal, dlnow);
1789 * @brief Build an absolute path from an absolute or relative one.
1790 * @param path: an absolute or relative path
1791 * @return an allocated absolute path (must be freed)
1794 efreet_desktop_command_path_absolute(const char *path)
1797 int size = PATH_MAX;
1803 if (!(buf = malloc(size))) return NULL;
1804 if (!getcwd(buf, size))
1811 if (buf[len-1] != '/') buf = efreet_string_append(buf, &size, &len, "/");
1812 buf = efreet_string_append(buf, &size, &len, path);
1817 /* just dup an already absolute buffer */
1818 return strdup(path);