544 lines
13 KiB
C
544 lines
13 KiB
C
# ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
# endif
|
|
|
|
#include <Ecore_Getopt.h>
|
|
#include <Efreet.h>
|
|
#include <Efreet_Mime.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
|
|
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] <file-or-folder-or-url>",
|
|
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;
|
|
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 ret;
|
|
}
|
|
}
|
|
|