summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2014-08-16 23:31:55 +0100
committerVincent Sanders <vince@kyllikki.org>2014-08-16 23:33:12 +0100
commitd0da99beca585f040d6564f2341949222afc6060 (patch)
tree5859eac1c6d395d4979ebe508093616e4a87327d /gtk
parentbf8896a4502ad750e818d4efa4c3810e930dc8cc (diff)
downloadnetsurf-d0da99beca585f040d6564f2341949222afc6060.tar.gz
netsurf-d0da99beca585f040d6564f2341949222afc6060.tar.bz2
extend gtk viewdata to open files in an editor using the freedesktop default app specification
Diffstat (limited to 'gtk')
-rw-r--r--gtk/Makefile.target2
-rw-r--r--gtk/gui.c2
-rw-r--r--gtk/viewdata.c315
3 files changed, 299 insertions, 20 deletions
diff --git a/gtk/Makefile.target b/gtk/Makefile.target
index f69a73a95..6e71ff3ee 100644
--- a/gtk/Makefile.target
+++ b/gtk/Makefile.target
@@ -48,7 +48,7 @@ GTKCFLAGS := -std=c99 -Dgtk -Dnsgtk \
$(GTKDEPFLAGS) \
-D_BSD_SOURCE \
-D_XOPEN_SOURCE=600 \
- -D_POSIX_C_SOURCE=200112L \
+ -D_POSIX_C_SOURCE=200809L \
-D_NETBSD_SOURCE \
-DGTK_RESPATH=\"$(NETSURF_GTK_RESOURCES)\" \
$(WARNFLAGS) -g
diff --git a/gtk/gui.c b/gtk/gui.c
index 4d2848366..bbe6cbaf2 100644
--- a/gtk/gui.c
+++ b/gtk/gui.c
@@ -976,7 +976,7 @@ check_dirname(const char *path, const char *leaf, char **dirname_out)
free(dirname);
- return ret;;
+ return ret;
}
/**
diff --git a/gtk/viewdata.c b/gtk/viewdata.c
index 6989b9a16..fd6a6b5bf 100644
--- a/gtk/viewdata.c
+++ b/gtk/viewdata.c
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
#include <gtk/gtk.h>
#include "utils/log.h"
@@ -35,6 +36,8 @@
#include "utils/url.h"
#include "utils/utils.h"
#include "utils/file.h"
+#include "utils/filepath.h"
+
#include "desktop/netsurf.h"
#include "desktop/browser.h"
#include "render/html.h"
@@ -553,41 +556,317 @@ tab_init(const char *title,
return ret;
}
-/**
- * open an editor from an existing file.
- */
-static nserror
-editor_init_fname(const char *title,
- const char *leafname,
- const char *fname)
-{
-/* find user configured app for opening text/plain */
-/*
- * serach path is ${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
+/**
+ * Build string vector of search path.
+ *
+ * ${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
*
* $XDG_DATA_HOME if empty use $HOME/.local/share
*
* XDG_DATA_DIRS if empty use /usr/local/share/:/usr/share/
*
- * search path looking for applications/defaults.list
+ * \return string vector of search pathnames or NULL on error.
+ */
+static char** xdg_data_strvec(void)
+{
+ const char *xdg_data_dirs;
+ const char *xdg_data_home;
+ const char *home_dir;
+ char *xdg_data_path;
+ int xdg_data_size;
+ char **svec;
+
+ xdg_data_dirs = getenv("XDG_DATA_DIRS");
+ if ((xdg_data_dirs == NULL) || (*xdg_data_dirs == 0)) {
+ xdg_data_dirs = "/usr/local/share/:/usr/share/";
+ }
+
+ xdg_data_home = getenv("XDG_DATA_HOME");
+ if ((xdg_data_home == NULL) || (*xdg_data_home == 0)) {
+ /* $XDG_DATA_HOME is empty use $HOME/.local/share */
+
+ home_dir = getenv("HOME");
+ if ((home_dir != NULL) && (*home_dir != 0)) {
+ xdg_data_size = strlen(home_dir) + SLEN("/.local/share:" ) + strlen(xdg_data_dirs) + 1;
+ xdg_data_path = malloc(xdg_data_size);
+ snprintf(xdg_data_path, xdg_data_size ,
+ "%s/.local/share/:%s", home_dir, xdg_data_dirs);
+ } else {
+ xdg_data_path = strdup(xdg_data_dirs);
+ }
+ } else {
+ xdg_data_size = strlen(xdg_data_home) + strlen(xdg_data_dirs) + 2;
+ xdg_data_path = malloc(xdg_data_size);
+ snprintf(xdg_data_path, xdg_data_size , "%s:%s", xdg_data_home, xdg_data_dirs);
+ }
+
+ LOG(("%s", xdg_data_path));
+
+ svec = filepath_path_to_strvec(xdg_data_path);
+ free(xdg_data_path);
+
+ return svec;
+}
+
+/**
+ * Search application defaults file for matching mime type.
+ *
+ * create filename form path and applications/defaults.list
*
* look for [Default Applications]
* search lines looking like mime/type=Desktop
*
- * if mimetype is found
- * use search path with applications/application.desktop
+ * \param path The base path.
+ * \param mimetype The mimetype to search for.
+ * \return The desktop file associated with the mime type or NULL if not found.
+ */
+static char *xdg_get_default_app(const char *path, const char *mimetype)
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t rd;
+ int fname_len;
+ char *fname;
+ int mimetype_len;
+ char *ret = NULL;
+
+ fname_len = strlen(path) + SLEN("/applications/defaults.list") + 1;
+ fname = malloc(fname_len);
+ snprintf(fname, fname_len, "%s/applications/defaults.list", path);
+
+ LOG(("Checking %s", fname));
+
+ fp = fopen(fname, "r");
+ free(fname);
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ mimetype_len = strlen(mimetype);
+ while ((rd = getline(&line, &len, fp)) != -1) {
+ /* line includes line endings if present, remove them */
+ while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) {
+ rd--;
+ }
+ line[rd] = 0;
+
+ /* look for mimetype */
+ if ((rd > mimetype_len) &&
+ (line[mimetype_len] == '=') &&
+ (strncmp(line, mimetype, mimetype_len) == 0)) {
+
+ ret = strdup(line + mimetype_len + 1);
+
+ LOG(("Found line match for %s length %zu\n", mimetype, rd));
+ LOG(("Result %s", ret));
+
+ break;
+ }
+ }
+
+ free(line);
+ fclose(fp);
+
+ return ret;
+}
+
+/**
+ * Search desktop file for an Exec line.
+ *
+ * search path is combined with applications/application.desktop to
+ * create a filename.
*
- * search desktop file for:
- * Exec=gedit %U
+ * Desktop file format http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
+ *
+ * \todo The parsing of the desktop file is badly incomplete and needs
+ * improving. For example the handling of the = delimiter is wrong and
+ * selection from the "Desktop Entry" group is completely absent.
*
- * execute target app on saved data
*/
+static char *xdg_get_exec_cmd(const char *path, const char *desktop)
+{
+ FILE *fp;
+ char *line = NULL;
+ size_t len = 0;
+ ssize_t rd;
+ int fname_len;
+ char *fname;
+ char *ret = NULL;
+
+ fname_len = strlen(path) + SLEN("/applications/") + strlen(desktop) + 1;
+ fname = malloc(fname_len);
+ snprintf(fname, fname_len, "%s/applications/%s", path, desktop);
+
+ LOG(("Checking %s", fname));
+
+ fp = fopen(fname, "r");
+ free(fname);
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ while ((rd = getline(&line, &len, fp)) != -1) {
+ /* line includes line endings if present, remove them */
+ while ((line[rd - 1] == '\n') || (line[rd - 1] == '\r')) {
+ rd--;
+ }
+ line[rd] = 0;
+
+ /* look for mimetype */
+ if ((rd > (ssize_t)SLEN("Exec=")) &&
+ (strncmp(line, "Exec=", SLEN("Exec=")) == 0)) {
+
+ ret = strdup(line + SLEN("Exec="));
+
+ LOG(("Found Exec length %zu", rd));
+ LOG(("Result %s", ret));
+
+ break;
+ }
+ }
+
+ free(line);
+ fclose(fp);
+
+ return ret;
+}
+
+static char *exec_arg(const char *arg, int len, const char *fname)
+{
+ char *res = NULL;
+
+ if (*arg == '%') {
+ arg++;
+ if ((*arg == 'f') || (*arg == 'F') ||
+ (*arg == 'u') || (*arg == 'U')) {
+ res = strdup(fname);
+ }
+ } else {
+ res = calloc(1, len + 1);
+ if (res != NULL) {
+ memcpy(res, arg, len);
+ }
+ }
+
+ return res;
+}
+
+/**
+ * Build vector for executing app.
+ */
+static char **build_exec_argv(const char *fname, const char *exec_cmd)
+{
+ char **argv;
+ const char *start; /* current arguments start */
+ const char *cur; /* current ptr within exec cmd */
+ int aidx = 0; /* argv index */
+
+ argv = calloc(10, sizeof(char *));
+ if (argv == NULL) {
+ return NULL;
+ }
+
+ cur = exec_cmd;
+ while (*cur != 0) {
+ /* skip whitespace */
+ while ((*cur != 0) && (*cur == ' ')) {
+ cur++;
+ }
+ if (*cur == 0) {
+ break;
+ }
+ start = cur;
+
+ /* find end of element */
+ while ((*cur != 0) && (*cur != ' ')) {
+ cur++;
+ }
+
+ argv[aidx] = exec_arg(start, cur - start, fname);
+ if (argv[aidx] != NULL) {
+ LOG(("adding \"%s\"", argv[aidx]));
+ aidx++;
+ }
+ }
+
+ /* if no arguments were found there was nothing to execute */
+ if (aidx == 0) {
+ free(argv);
+ return NULL;
+ }
+
+ return argv;
+}
+
+
+/**
+ * open an editor from an existing file.
+ */
+static nserror
+editor_init_fname(const char *title,
+ const char *leafname,
+ const char *fname)
+{
+ char **xdg_data_vec;
+ int veci;
+ char *default_app; /* desktop file of default app for mimetype */
+ char *exec_cmd;
+ char **argv;
+
+ /* build string vector of search path */
+ xdg_data_vec = xdg_data_strvec();
+
+ /* find user configured app for opening text/plain */
+ veci = 0;
+ while (xdg_data_vec[veci] != NULL) {
+ default_app = xdg_get_default_app(xdg_data_vec[veci],
+ "text/plain");
+ if (default_app != NULL) {
+ break;
+ }
+ veci++;
+ }
+
+ if (default_app == NULL) {
+ /* no default app */
+ filepath_free_strvec(xdg_data_vec);
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* find app to execute */
+ veci = 0;
+ while (xdg_data_vec[veci] != NULL) {
+ exec_cmd = xdg_get_exec_cmd(xdg_data_vec[veci], default_app);
+ if (exec_cmd != NULL) {
+ break;
+ }
+ veci++;
+ }
+ filepath_free_strvec(xdg_data_vec);
+
+ if (exec_cmd == NULL) {
+ /* no exec entry */
+ return NSERROR_NOT_FOUND;
+ }
+
+ /* build exec vector */
+ argv = build_exec_argv(fname, exec_cmd);
+ free(exec_cmd);
+
+ /* execute target app on saved data */
+ if (g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
+ NULL, NULL) != TRUE) {
+ return NSERROR_NOT_FOUND;
+ }
+ filepath_free_strvec(argv);
+
return NSERROR_OK;
}
/**
- * create a new tab with page source
+ * open an editor with data.
*/
static nserror
editor_init(const char *title,