# ifdef HAVE_CONFIG_H # include "config.h" # endif #include #include #include #include #include #include #include # ifdef E_API # undef E_API # endif # ifdef WIN32 # ifdef BUILDING_DLL # define E_API __declspec(dllexport) # else # define E_API __declspec(dllimport) # endif # else # ifdef __GNUC__ # if __GNUC__ >= 4 /* BROKEN in gcc 4 on amd64 */ # if 0 # pragma GCC visibility push(hidden) # endif # define E_API __attribute__ ((visibility("default"))) # else # define E_API # endif # else # define E_API # endif # endif 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 EINA_UNUSED, char *command, int remaining EINA_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 Efreet_Desktop * _terminal_get(const char *defaults_list) { Efreet_Desktop *tdesktop = NULL; Efreet_Ini *ini; const char *s; ini = efreet_ini_new(defaults_list); if ((ini) && (ini->data) && (efreet_ini_section_set(ini, "Default Applications")) && (ini->section)) { s = efreet_ini_string_get(ini, "x-scheme-handler/terminal"); if (s) tdesktop = efreet_util_desktop_file_id_find(s); } if (ini) efreet_ini_free(ini); return tdesktop; } static char ** terminal_open(void) { const char *terms[] = { "terminology.desktop", "rxvt.desktop", "gnome-terimnal.desktop", "konsole.desktop", NULL }; const char *s; char buf[PATH_MAX], **ret; Efreet_Desktop *tdesktop = NULL, *td; Eina_List *l; int i; s = efreet_data_home_get(); if (s) { snprintf(buf, sizeof(buf), "%s/applications/defaults.list", s); tdesktop = _terminal_get(buf); } if (tdesktop) goto have_desktop; EINA_LIST_FOREACH(efreet_data_dirs_get(), l, s) { snprintf(buf, sizeof(buf), "%s/applications/defaults.list", s); tdesktop = _terminal_get(buf); if (tdesktop) goto have_desktop; } for (i = 0; terms[i]; i++) { tdesktop = efreet_util_desktop_file_id_find(terms[i]); if (tdesktop) goto have_desktop; } if (!tdesktop) { l = efreet_util_desktop_category_list("TerminalEmulator"); if (l) { // just take first one since above list doesn't work. tdesktop = l->data; EINA_LIST_FREE(l, td) { // free/unref the desktosp we are not going to use if (td != tdesktop) efreet_desktop_free(td); } } } if (!tdesktop) return NULL; have_desktop: if (!tdesktop->exec) { efreet_desktop_free(tdesktop); return NULL; } ret = malloc(sizeof(char *) * 2); if (!ret) return NULL; ret[0] = strdup(tdesktop->exec); ret[1] = NULL; if (!ret[0]) { free(ret); efreet_desktop_free(tdesktop); return NULL; } efreet_desktop_free(tdesktop); 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 } }; E_API 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; int ret = EXIT_SUCCESS; for (itr = cmds; *itr != NULL; itr++) { /* Question: should we execute them in parallel? */ int r = system(*itr); if (r < 0) fprintf(stderr, "ERROR: %s executing %s\n", strerror(errno), *itr); free(*itr); if (r > 0) /* Question: should we stop the loop on first faiure? */ ret = r; } free(cmds); return WEXITSTATUS(ret); } }