diff --git a/configure.ac b/configure.ac index d90ec0f08..0c88e559e 100644 --- a/configure.ac +++ b/configure.ac @@ -562,6 +562,12 @@ $udisks_mount \ $eeze_mount \ $device_backend" +PKG_CHECK_MODULES(E_OPEN, [ + ecore >= 1.2.0 + efreet >= 1.2.0 + efreet-mime >= 1.2.0 +]) + AC_E_CHECK_PKG(ECORE_IMF, [ ecore-imf >= 1.2.0 ecore-imf-evas >= 1.2.0 ], [], [:]) e_libs=$E_LIBS" "$LIBINTL" "$fnmatch_libs" "$ECORE_IMF_LIBS" "$execinfo_libs diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 0e2632b44..cfc36092e 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -22,7 +22,8 @@ bin_PROGRAMS = \ enlightenment \ enlightenment_imc \ enlightenment_start \ -enlightenment_filemanager +enlightenment_filemanager \ +enlightenment_open internal_bindir = $(libdir)/enlightenment/utils internal_bin_PROGRAMS = \ @@ -411,6 +412,10 @@ enlightenment_filemanager_SOURCES = e_fm_cmdline.c enlightenment_filemanager_LDADD = @E_FM_CMDLINE_LIBS@ enlightenment_filemanager_CFLAGS = @E_FM_CMDLINE_CFLAGS@ +enlightenment_open_SOURCES = e_open.c +enlightenment_open_LDADD = @E_OPEN_LIBS@ +enlightenment_open_CFLAGS = @E_OPEN_CFLAGS@ + # HACK! why install-data-hook? install-exec-hook is run after bin_PROGRAMS # and before internal_bin_PROGRAMS are installed. install-data-hook is # run after both diff --git a/src/bin/e_open.c b/src/bin/e_open.c new file mode 100644 index 000000000..370ac8873 --- /dev/null +++ b/src/bin/e_open.c @@ -0,0 +1,496 @@ +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif + +#include +#include +#include +#include +#include + +static const char * +xdg_defaults_get(const char *path, const char *mime) +{ + Efreet_Ini *ini; + const char *str; + + if (access(path, R_OK) != 0) + return NULL; + + ini = efreet_ini_new(path); + if (!ini) + return NULL; + + efreet_ini_section_set(ini, "Default Applications"); + str = eina_stringshare_add(efreet_ini_string_get(ini, mime)); + efreet_ini_free(ini); + + return str; +} + +static Efreet_Desktop * +xdg_desktop_from_string_list(const char *strlist) +{ + Efreet_Desktop *desktop = NULL; + char **array = eina_str_split(strlist, ";", 0); + unsigned int i; + + if (!array) + return NULL; + + for (i = 0; array[i] != NULL; i++) + { + const char *name = array[i]; + if (name[0] == '/') + desktop = efreet_desktop_new(name); + else + desktop = efreet_util_desktop_file_id_find(name); + + if (desktop) + { + if (desktop->exec) break; + else + { + efreet_desktop_free(desktop); + desktop = NULL; + } + } + } + + free(array[0]); + free(array); + + return desktop; +} + +static Efreet_Desktop * +desktop_first_free_others(Eina_List *lst) +{ + Efreet_Desktop *desktop, *d; + if (!lst) + return NULL; + + desktop = lst->data; + efreet_desktop_ref(desktop); + + EINA_LIST_FREE(lst, d) + efreet_desktop_free(d); + + return desktop; +} + +static Efreet_Desktop * +handler_find(const char *mime) +{ + Efreet_Desktop *desktop = NULL; + char path[PATH_MAX]; + const char *name; + + snprintf(path, sizeof(path), "%s/applications/defaults.list", + efreet_data_home_get()); + name = xdg_defaults_get(path, mime); + if (!name) + { + const Eina_List *n, *dirs = efreet_data_dirs_get(); + const char *d; + EINA_LIST_FOREACH(dirs, n, d) + { + snprintf(path, sizeof(path), "%s/applications/defaults.list", d); + name = xdg_defaults_get(path, mime); + if (name) + break; + } + } + + if (name) + { + desktop = xdg_desktop_from_string_list(name); + eina_stringshare_del(name); + } + + if (!desktop) + desktop = desktop_first_free_others(efreet_util_desktop_mime_list(mime)); + + return desktop; +} + +static void * +get_command(void *data, Efreet_Desktop *desktop __UNUSED__, char *command, int remaining __UNUSED__) +{ + Eina_List **p_cmd = data; + *p_cmd = eina_list_append(*p_cmd, command); + return NULL; +} + +static char ** +mime_open(const char *mime, const char * const *argv, int argc) +{ + Efreet_Desktop *desktop = handler_find(mime); + Eina_List *files = NULL; + Eina_List *cmds = NULL; + char **ret; + + if (!desktop) + return NULL; + + for (; argc > 0; argc--, argv++) + files = eina_list_append(files, *argv); + + efreet_desktop_command_get(desktop, files, get_command, &cmds); + + if (!cmds) ret = NULL; + else + { + char *c; + + ret = calloc(eina_list_count(cmds) + 1, sizeof(char *)); + if (ret) + { + unsigned int i = 0; + EINA_LIST_FREE(cmds, c) + { + ret[i] = c; /* was strdup by efreet_desktop_command_get() */ + i++; + } + ret[i] = NULL; + } + else + { + EINA_LIST_FREE(cmds, c) + free(c); + } + } + + eina_list_free(files); + + return ret; +} + +static void +append_single_quote_escaped(Eina_Strbuf *b, const char *str) +{ + const char *itr = str; + for (; *itr != '\0'; itr++) + { + if (*itr != '\'') + eina_strbuf_append_char(b, *itr); + else + eina_strbuf_append(b, "\\'"); + } +} + +static char ** +single_command_open(const char *command, const char * const *argv, int argc) +{ + char **ret = calloc(2, sizeof(char *)); + Eina_Strbuf *b; + int i; + + if (!ret) + return NULL; + + b = eina_strbuf_new(); + if (!b) + { + free(ret); + return NULL; + } + eina_strbuf_append(b, command); + + for (i = 0; i < argc; i++) + { + Eina_Bool has_space = EINA_FALSE; + int s_idx_sq = -1, s_idx_dq = -1; + int l_idx_sq = -1, l_idx_dq = -1; + int idx; + const char *itr; + + for (idx = 0, itr = argv[i]; *itr != '\0'; itr++, idx++) + { + if ((!has_space) && (isspace(*itr))) + has_space = EINA_TRUE; + + if (*itr == '\'') + { + l_idx_sq = idx; + if (s_idx_sq < 0) + s_idx_sq = idx; + } + + if (*itr == '\'') + { + l_idx_dq = idx; + if (s_idx_dq < 0) + s_idx_dq = idx; + } + } + idx--; + + eina_strbuf_append_char(b, ' '); + if ((!has_space) || + ((s_idx_sq == 0) && (l_idx_sq == idx)) || + ((s_idx_dq == 0) && (l_idx_dq == idx))) + eina_strbuf_append(b, argv[i]); + else + { + char c; + if ((s_idx_sq >= 0) && (s_idx_dq < 0)) + c = '"'; + else if ((s_idx_sq < 0) && (s_idx_dq >= 0)) + c = '\''; + else + c = 0; + + if (c) + { + eina_strbuf_append_char(b, c); + eina_strbuf_append(b, argv[i]); + eina_strbuf_append_char(b, c); + } + else + { + eina_strbuf_append_char(b, '\''); + append_single_quote_escaped(b, argv[i]); + eina_strbuf_append_char(b, '\''); + } + } + } + + ret[0] = eina_strbuf_string_steal(b); + eina_strbuf_free(b); + + return ret; +} + +static char ** +terminal_open(void) +{ + const char *generic_names[] = { + "[Tt]erminal", + "[Tt]erminal [Ee]mulator", + "[Tt]erminal *", + NULL + }; + const char **itr; + Eina_List *cmds = NULL; + char **ret; + Efreet_Desktop *desktop = NULL; + + for (itr = generic_names; (desktop == NULL) && (*itr != NULL); itr++) + desktop = desktop_first_free_others + (efreet_util_desktop_generic_name_glob_list(*itr)); + + if (!desktop) + desktop = desktop_first_free_others(efreet_util_desktop_category_list + ("TerminalEmulator")); + + if (!desktop) + return NULL; + + efreet_desktop_command_get(desktop, NULL, get_command, &cmds); + if (!cmds) ret = NULL; + else + { + char *c; + + ret = calloc(eina_list_count(cmds) + 1, sizeof(char *)); + if (ret) + { + unsigned int i = 0; + EINA_LIST_FREE(cmds, c) + { + ret[i] = c; /* was strdup by efreet_desktop_command_get() */ + i++; + } + ret[i] = NULL; + } + else + { + EINA_LIST_FREE(cmds, c) + free(c); + } + } + + return ret; +} + +static char ** +browser_open(const char * const *argv, int argc) +{ + const char *env = getenv("BROWSER"); + if (env) return single_command_open(env, argv, argc); + return mime_open("x-scheme-handler/http", argv, argc); +} + +static char ** +local_open(const char *path) +{ + const char *mime = efreet_mime_type_get(path); + if (mime) + { + char **ret = mime_open(mime, &path, 1); + if (ret) + return ret; + return single_command_open("enlightenment_filemanager", &path, 1); + } + + fprintf(stderr, "ERROR: Could not get mime type for: %s\n", path); + return NULL; +} + +static char ** +protocol_open(const char *str) +{ + Efreet_Uri *uri = efreet_uri_decode(str); + char **ret = NULL; + + if (!uri) + { + fprintf(stderr, "ERROR: Could not decode uri: %s\n", str); + return NULL; + } + + if (!uri->protocol) + fprintf(stderr, "ERROR: Could not get protocol from uri: %s\n", str); + else if (strcmp(uri->protocol, "file") == 0) + ret = local_open(uri->path); + else + { + char mime[256]; + snprintf(mime, sizeof(mime), "x-scheme-handler/%s", uri->protocol); + ret = mime_open(mime, &str, 1); + } + efreet_uri_free(uri); + return ret; +} + +static const struct type_mime { + const char *type; + const char *mime; +} type_mimes[] = { + /* {"browser", "x-scheme-handler/http"}, */ + {"mail", "x-scheme-handler/mailto"}, + /* {"terminal", NULL}, */ + {"filemanager", "x-scheme-handler/file"}, + {"image", "image/jpeg"}, + {"video", "video/x-mpeg"}, + {"music", "audio/mp3"}, + {NULL, NULL} +}; + +static const char *type_choices[] = { + "browser", + "mail", + "terminal", + "filemanager", + "image", + "video", + "music", + NULL +}; + +static const Ecore_Getopt options = { + "enlightenment_open", + "%prog [options] ", + PACKAGE_VERSION, + "(C) 2012 Gustavo Sverzut Barbieri and others", + "BSD 2-Clause", + "Opens the file using Enlightenment standards.", + EINA_FALSE, + { + ECORE_GETOPT_CHOICE('t', "type", "Choose program type to launch.", + type_choices), + ECORE_GETOPT_VERSION('V', "version"), + ECORE_GETOPT_COPYRIGHT('C', "copyright"), + ECORE_GETOPT_LICENSE('L', "license"), + ECORE_GETOPT_HELP('h', "help"), + ECORE_GETOPT_SENTINEL + } +}; + +EAPI int +main(int argc, char *argv[]) +{ + Eina_Bool quit_option = EINA_FALSE; + char *type = NULL; + Ecore_Getopt_Value values[] = { + ECORE_GETOPT_VALUE_STR(type), + ECORE_GETOPT_VALUE_BOOL(quit_option), + ECORE_GETOPT_VALUE_BOOL(quit_option), + ECORE_GETOPT_VALUE_BOOL(quit_option), + ECORE_GETOPT_VALUE_BOOL(quit_option), + ECORE_GETOPT_VALUE_NONE + }; + int args; + char **cmds; + + args = ecore_getopt_parse(&options, values, argc, argv); + if (args < 0) + { + fputs("ERROR: Could not parse command line options.\n", stderr); + return EXIT_FAILURE; + } + if (quit_option) return EXIT_SUCCESS; + + if ((type == NULL) && (args == argc)) + { + fputs("ERROR: Missing file, directory or URL or --type.\n", stderr); + return EXIT_FAILURE; + } + + efreet_init(); + efreet_mime_init(); + + if (type) + { + if (strcmp(type, "terminal") == 0) + cmds = terminal_open(); + else if (strcmp(type, "browser") == 0) + cmds = browser_open((const char * const *)argv + args, argc - args); + else + { + const struct type_mime *itr; + + for (itr = type_mimes; itr->type != NULL; itr++) + { + if (strcmp(type, itr->type) == 0) + { + cmds = mime_open(itr->mime, + (const char * const *)argv + args, + argc - args); + break; + } + } + if (!itr->type) + { + fprintf(stderr, "ERROR: type not supported %s\n", type); + cmds = NULL; + } + } + } + else if (strstr(argv[args], "://")) + cmds = protocol_open(argv[args]); + else + cmds = local_open(argv[args]); + + efreet_mime_shutdown(); + efreet_shutdown(); + + + /* No EFL, plain boring sequential system() calls */ + if (!cmds) + return EXIT_FAILURE; + else + { + char **itr; + + for (itr = cmds; *itr != NULL; itr++) + { + system(*itr); + free(*itr); + } + free(cmds); + } + + return EXIT_SUCCESS; +}