Thread: [Vimprobable-users] [PATCH 0/3] add external file handlers
Vimprobable is a lean web browser optimised for full keyboard control
Brought to you by:
hanness
From: Steffen S. <ste...@gm...> - 2012-03-03 17:35:53
|
Hey Guys, I just implemented a feature that will e.g. automatically open your pdf viewer when you click on a pdf file. Also refactored the external URI handlers to be able to reuse most of their code. Cheers, Steffen Steffen Schuldenzucker (3): utilities: add spawn_command_on; add is_prefix_of; refactor open_handler external file handlers by MIME type. (e.g. auto-open a file when download is complete) fix compiler warnings: add some 'const'. config.h | 11 ++++++ main.c | 18 +++++++++ utilities.c | 112 +++++++++++++++++++++++++++++++++++--------------------- utilities.h | 5 +++ vimprobable.h | 5 +++ 5 files changed, 109 insertions(+), 42 deletions(-) -- 1.7.9.1 |
From: Steffen S. <ste...@gm...> - 2012-03-03 17:36:02
|
--- utilities.c | 80 ++++++++++++++++++++++++++++------------------------------ utilities.h | 2 + 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/utilities.c b/utilities.c index 9bb4f49..387a466 100644 --- a/utilities.c +++ b/utilities.c @@ -795,50 +795,48 @@ void make_uri_handlers_list(URIHandler *uri_handlers, int length) } } +/* spawn_command_on("xpdf %s", "file.pdf") -> spawn "xpdf \"file.pdf\"" + * todo: no way to escape whitespace in p_cmd. (ws in path is ok) + * Maybe just escape, printf, execute via bash? + * also todo: Will leak mem if %s is used more than once. + */ +void +spawn_command_on(const char* p_cmd, char* arg) { + char *argv[64], cmd[MAX_SETTING_SIZE]; + strncpy(cmd, p_cmd, MAX_SETTING_SIZE); + char *dynarg = NULL; + int j; + char *word = strtok(cmd, " "); + for(j = 0; j < 62 && word; ++j, word = strtok(NULL, " ")) + argv[j] = (strstr(word, "%s") + ? dynarg = g_strdup_printf(word, arg) + : word); + argv[j] = NULL; + g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); + if(dynarg) + g_free(dynarg); +} + +gboolean +is_prefix_of(char* s, char* t) { + return strlen(s) <= strlen(t) && !strncmp(s, t, strlen(s)); +} + gboolean open_handler(char *uri) { - char *argv[64]; - char *p = NULL, *arg, arg_temp[MAX_SETTING_SIZE], *temp, temp2[MAX_SETTING_SIZE] = "", *temp3; - int j; - GList *l; + if(!strchr(uri, ':')) + return FALSE; - p = strchr(uri, ':'); - if (p) { - if (dynamic_uri_handlers != NULL) { - for (l = dynamic_uri_handlers; l; l = g_list_next(l)) { - URIHandler *s = (URIHandler *)l->data; - if (strlen(uri) >= strlen(s->handle) && strncmp(s->handle, uri, strlen(s->handle)) == 0) { - if (strlen(s->handler) > 0) { - arg = (uri + strlen(s->handle)); - strncpy(temp2, s->handler, MAX_SETTING_SIZE); - temp = strtok(temp2, " "); - j = 0; - while (temp != NULL) { - if (strstr(temp, "%s")) { - temp3 = temp; - memset(arg_temp, 0, MAX_SETTING_SIZE); - while (strncmp(temp3, "%s", 2) != 0) { - strncat(arg_temp, temp3, 1); - temp3++; - } - strcat(arg_temp, arg); - temp3++; - temp3++; - strcat(arg_temp, temp3); - argv[j] = arg_temp; - } else { - argv[j] = temp; - } - temp = strtok(NULL, " "); - j++; - } - argv[j] = NULL; - g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL); - } - return TRUE; - } - } - } + if(dynamic_uri_handlers) { + GList *l; + for (l = dynamic_uri_handlers; l; l = g_list_next(l)) { + URIHandler *s = (URIHandler *)l->data; + if(is_prefix_of(s->handle, uri)) { + if (strlen(s->handler) > 0) + spawn_command_on(s->handler, uri + strlen(s->handle)); + return TRUE; + } + } } return FALSE; } diff --git a/utilities.h b/utilities.h index f9ac1ba..8cfd7b9 100644 --- a/utilities.h +++ b/utilities.h @@ -34,5 +34,7 @@ void free_list(Listelement *elementlist); char *find_uri_for_searchengine(const char *handle); void make_searchengines_list(Searchengine *searchengines, int length); void make_uri_handlers_list(URIHandler *uri_handlers, int length); +void spawn_command_on(const char* p_cmd, char* arg); gboolean open_handler(char *uri); +gboolean is_prefix_of(char* s, char* t); -- 1.7.9.1 |
From: Steffen S. <ste...@gm...> - 2012-03-03 17:36:05
|
--- utilities.c | 4 ++-- utilities.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/utilities.c b/utilities.c index 7d41867..bc70f88 100644 --- a/utilities.c +++ b/utilities.c @@ -802,7 +802,7 @@ void make_uri_handlers_list(URIHandler *uri_handlers, int length) * also todo: Will leak mem if %s is used more than once. */ void -spawn_command_on(const char* p_cmd, char* arg) { +spawn_command_on(const char* p_cmd, const char* arg) { char *argv[64], cmd[MAX_SETTING_SIZE]; strncpy(cmd, p_cmd, MAX_SETTING_SIZE); char *dynarg = NULL; @@ -819,7 +819,7 @@ spawn_command_on(const char* p_cmd, char* arg) { } gboolean -is_prefix_of(char* s, char* t) { +is_prefix_of(const char* s, const char* t) { return strlen(s) <= strlen(t) && !strncmp(s, t, strlen(s)); } diff --git a/utilities.h b/utilities.h index 67338c1..c9f0633 100644 --- a/utilities.h +++ b/utilities.h @@ -35,9 +35,9 @@ char *find_uri_for_searchengine(const char *handle); void make_searchengines_list(Searchengine *searchengines, int length); void make_uri_handlers_list(URIHandler *uri_handlers, int length); void make_file_handlers_list(FileHandler *file_handlers, int length); -void spawn_command_on(const char* p_cmd, char* arg); +void spawn_command_on(const char* p_cmd, const char* arg); gboolean open_handler(char *uri); gboolean open_file_handler(const char *dst_uri, const char* content_type); -gboolean is_prefix_of(char* s, char* t); +gboolean is_prefix_of(const char* s, const char* t); -- 1.7.9.1 |
From: Steffen S. <ste...@gm...> - 2012-03-03 17:36:04
|
--- config.h | 11 +++++++++++ main.c | 18 ++++++++++++++++++ utilities.c | 32 +++++++++++++++++++++++++++++++- utilities.h | 3 +++ vimprobable.h | 5 +++++ 5 files changed, 68 insertions(+), 1 deletions(-) diff --git a/config.h b/config.h index c4b5b27..876e190 100644 --- a/config.h +++ b/config.h @@ -67,6 +67,17 @@ static URIHandler uri_handlers[] = { { "ftp://", "x-terminal-emulator -e wget ftp://%s" }, }; + +/* external file handlers: + * when a download is finished, look for an entry where the handle (first + * string) matches the beginning of its MIME type (Content-Type header). Then + * call the handler (second string) on it. + */ +static FileHandler file_handlers[] = { + { "application/pdf", "xpdf %s" }, + { "application/postscript", "gv %s" }, +}; + /* cookies */ #define ENABLE_COOKIE_SUPPORT #define COOKIES_STORAGE_FILENAME "%s/vimprobable/cookies", config_base diff --git a/main.c b/main.c index 48e884f..e7c1aad 100644 --- a/main.c +++ b/main.c @@ -88,6 +88,7 @@ static void ascii_bar(int total, int state, char *string); static gchar *jsapi_ref_to_string(JSContextRef context, JSValueRef ref); static void jsapi_evaluate_script(const gchar *script, gchar **value, gchar **message); static void download_progress(WebKitDownload *d, GParamSpec *pspec); +static gboolean try_open_file_handler(WebKitDownload* d); static void set_widget_font_and_color(GtkWidget *widget, const char *font_str, const char *bg_color_str, const char *fg_color_str); @@ -333,6 +334,7 @@ download_progress(WebKitDownload *d, GParamSpec *pspec) { a.i = Info; a.s = g_strdup_printf("Download %s finished", webkit_download_get_suggested_filename(d)); echo(&a); + try_open_file_handler(d); } g_free(a.s); activeDownloads = g_list_remove(activeDownloads, d); @@ -340,6 +342,21 @@ download_progress(WebKitDownload *d, GParamSpec *pspec) { update_state(); } +gboolean +try_open_file_handler(WebKitDownload* d) { + WebKitNetworkResponse *resp = webkit_download_get_network_response(d); + SoupMessage *msg = webkit_network_response_get_message(resp); + SoupMessageHeaders *resp_headers = msg->response_headers; + + const char* content_type = soup_message_headers_get_one(resp_headers, + "Content-Type"); + const char* dst_uri = (const char*) + webkit_download_get_destination_uri(d); + + if(!content_type) + return FALSE; + return open_file_handler(dst_uri, content_type); +} gboolean process_keypress(GdkEventKey *event) { @@ -2556,6 +2573,7 @@ main(int argc, char *argv[]) { make_searchengines_list(searchengines, LENGTH(searchengines)); make_uri_handlers_list(uri_handlers, LENGTH(uri_handlers)); + make_file_handlers_list(file_handlers, LENGTH(file_handlers)); /* Check if the specified file exists. */ /* And only warn the user, if they explicitly asked for a config on the diff --git a/utilities.c b/utilities.c index 387a466..7d41867 100644 --- a/utilities.c +++ b/utilities.c @@ -20,7 +20,8 @@ extern Key keys[]; extern char *error_msg; extern gboolean complete_case_sensitive; extern char *config_base; -static GList *dynamic_searchengines = NULL, *dynamic_uri_handlers = NULL; +static GList *dynamic_searchengines = NULL, *dynamic_uri_handlers = NULL, + *dynamic_file_handlers = NULL; void add_modkeys(char key); @@ -841,3 +842,32 @@ open_handler(char *uri) { return FALSE; } +void make_file_handlers_list(FileHandler *file_handlers, int length) +{ + int i; + for (i = 0; i < length; i++, file_handlers++) { + dynamic_file_handlers = g_list_prepend(dynamic_file_handlers, file_handlers); + } +} + +gboolean open_file_handler(const char *dst_uri, const char* content_type) { + const char* strip = "file://"; + size_t striplen = strlen(strip); + if(!(strlen(dst_uri) >= striplen && !strncmp(dst_uri, strip, striplen))) + return FALSE; /* strange */ + const char* path = dst_uri + striplen; + + if(dynamic_file_handlers) { + GList *l; + for(l = dynamic_file_handlers; l; l = g_list_next(l)) { + FileHandler *s = (FileHandler*)l->data; + if(is_prefix_of(s->handle, content_type)) { + if(strlen(s->handler) > 0) + spawn_command_on(s->handler, path); + return TRUE; + } + } + } + return FALSE; +} + diff --git a/utilities.h b/utilities.h index 8cfd7b9..67338c1 100644 --- a/utilities.h +++ b/utilities.h @@ -34,7 +34,10 @@ void free_list(Listelement *elementlist); char *find_uri_for_searchengine(const char *handle); void make_searchengines_list(Searchengine *searchengines, int length); void make_uri_handlers_list(URIHandler *uri_handlers, int length); +void make_file_handlers_list(FileHandler *file_handlers, int length); void spawn_command_on(const char* p_cmd, char* arg); gboolean open_handler(char *uri); +gboolean open_file_handler(const char *dst_uri, const char* content_type); + gboolean is_prefix_of(char* s, char* t); diff --git a/vimprobable.h b/vimprobable.h index 5a6c2df..55fbd67 100644 --- a/vimprobable.h +++ b/vimprobable.h @@ -143,6 +143,11 @@ typedef struct { char *handler; } URIHandler; +typedef struct { + char *handle; + char *handler; +} FileHandler; + struct map_pair { char *line; char what[20]; -- 1.7.9.1 |
From: Andraž 'r. L. <ru...@co...> - 2012-03-03 18:16:10
|
:2012-03-03T18:35:Steffen Schuldenzucker: > Hey Guys, > > I just implemented a feature that will e.g. automatically open your pdf viewer > when you click on a pdf file. Also refactored the external URI handlers to be > able to reuse most of their code. > Steffen Schuldenzucker (3): > utilities: add spawn_command_on; add is_prefix_of; refactor > open_handler Why not reuse mailcap? -- Andraž 'ruskie' Levstik Source Mage GNU/Linux Games/Xorg grimoire guru Re-Alpine Coordinator http://sourceforge.net/projects/re-alpine/ Geek/Hacker/Tinker Communities that make few or no demands on their members cannot command allegiance. All else being equal, members who feel most needed have the strongest allegiance. |
From: Steffen S. <ssc...@un...> - 2012-03-03 19:27:49
|
On Sat, 3 Mar 2012, Andraž 'ruskie' Levstik wrote: > :2012-03-03T18:35:Steffen Schuldenzucker: > >> Hey Guys, >> >> I just implemented a feature that will e.g. automatically open your pdf viewer >> when you click on a pdf file. Also refactored the external URI handlers to be >> able to reuse most of their code. >> Steffen Schuldenzucker (3): >> utilities: add spawn_command_on; add is_prefix_of; refactor >> open_handler > > Why not reuse mailcap? In fact, my config.h looks like this: static FileHandler file_handlers[] = { { "application/pdf", "xdg-open %s" }, { "application/postscript", "xdg-open %s" }, ... } I had to set up xdg-open nevertheless because so many programs use it (acroread, chromium, ...) But you're probably more of an expert than me, so could you maybe explain how to handle this properly? Oh, btw. I just switched to alpine because my thunderbird was buggy :) -- Steffen |
From: Hannes S. <ha...@yl...> - 2012-03-04 18:02:13
Attachments:
signature.asc
|
Before we get into code details, I think the concept should be clear and agreed on. Apart from the mailcap question which ruskie already asked, I see the following potential irritation: As you currently implemented this, a file is first downloaded to the regular download location (which I would assume to be some sort of permanent storage space) and then, if a definition is found for this MIME type, opened. What other browsers do is offer a choice to *either* download a file to permanent storage *or* open it. Not getting into the discussion of "opening" also involving downloading the file, it is usually stored in non-permanent space for that purpose, because when a file is selected to be "opened", I guess the assumption is that the user does not want to store it permanently. The other way around, it could also get annoying not to be able to simply *save* a file anymore *without* opening it immediately afterwards. So, what's everyone's views on this? Hannes |
From: Andraž 'r. L. <ru...@co...> - 2012-03-04 18:04:40
|
:2012-03-04T19:02:Hannes Schüller: > Not getting into the discussion of "opening" also involving downloading > the file, it is usually stored in non-permanent space for that purpose, > because when a file is selected to be "opened", I guess the assumption > is that the user does not want to store it permanently. The other way > around, it could also get annoying not to be able to simply *save* a > file anymore *without* opening it immediately afterwards. > > So, what's everyone's views on this? I'm for download first then ask me what to do with it(i.e. nothing or open). But yes most browsers I believe will download to $TMP or something like that and then open it. -- Andraž 'ruskie' Levstik Source Mage GNU/Linux Games/Xorg grimoire guru Re-Alpine Coordinator http://sourceforge.net/projects/re-alpine/ Geek/Hacker/Tinker Tuam Libera Mentem. |
From: Steffen S. <ssc...@un...> - 2012-03-04 18:36:05
|
On 03/04/2012 07:02 PM, Hannes Schüller wrote: > Before we get into code details, I think the concept should be clear > and agreed on. Apart from the mailcap question which ruskie already > asked, I see the following potential irritation: > > As you currently implemented this, a file is first downloaded to the > regular download location (which I would assume to be some sort of > permanent storage space) and then, if a definition is found for this > MIME type, opened. What other browsers do is offer a choice to *either* > download a file to permanent storage *or* open it. > > Not getting into the discussion of "opening" also involving downloading > the file, it is usually stored in non-permanent space for that purpose, > because when a file is selected to be "opened", I guess the assumption > is that the user does not want to store it permanently. The other way > around, it could also get annoying not to be able to simply *save* a > file anymore *without* opening it immediately afterwards. > > So, what's everyone's views on this? My workflow with this is as follows: - My DOWNLOADS_PATH is pretty much a "temporary place": If I want to keep something permanently, I move it to a specialized location, like papers/, doc/, ... - It's wiped every now and then by myself, manually. - During a (work / browsing) "session", I like to keep all the stuff I downloaded, because that way, I can postpone the decision if it's worth to be kept permanently. - Of course one can argue that then, I'd want the same thing for web pages. - Maybe my next patch ;) I do very well see that this is my very personal way of working, which I don't want to force on anyone, but I've always been annoyed by this "open or save" question. For the "not being able to just save a file anymore" issue: Yes, that's probably annoying. We could set a flag to prevent opening when a user explicitly chose "Download linked file". Can we somehow attach a piece of arbitrary "payload" to a WebKitDownload? (other than storing a map (WebKitDownload*) -> MyPayload ...) -- Steffen |
From: Alexander F. <ale...@go...> - 2012-03-12 16:44:13
|
Hello, since it appears that there is not a uniform settlement upon how to do this I just want to report what I'd like to see. I don't want to imply that this is how I think it should be done since there's a lot of personal preference involved as pointed out by others. I'd rather see mailcap to be used than xdg-open from the freedesktop project. I consider opening a file and downloading a file to be distinct operations. Since I'd like to see a file which is downloaded only to be opened to be downloaded in a temporary place instead of the regular download folder. It might be nice to have options to *download* a file, *open* a file and *download and open* a file. However, I would not want to see a dialog showing up for this. Maybe this can be done by different hinting modes but maybe this might be overkill to have three different key combinations for this. Feel free to disagree with any point mentioned here. Just wanted to share my expectations. However, this seems to be the way most popular browsers handle this, including Firefox's vimperator plugin if I am not mistaken. Regards Alexander Foremny |
From: Hannes S. <ha...@yl...> - 2012-03-12 17:33:08
Attachments:
signature.asc
|
Hello Alexander! Alexander Foremny <ale...@go...> wrote: > It might be nice to have options to *download* a file, *open* a file > and *download and open* a file. However, I would not want to see a > dialog showing up for this. Maybe this can be done by different > hinting modes Thanks for this - sounds like a decent plan to me. The default (i.e. "clicking") could still be plain downloading, but with hinting, we can distinguish between these user intentions. Would that be alright with everyone? Hannes |
From: Matthew C. <je...@gm...> - 2012-03-15 02:20:24
|
Hi all, Hinting sounds good to me, you could always have it as an extra configuration option for the default behavior as well, since chances are each user has a preference for the majority of cases (I'm a download on click kinda guy myself). -Matt On Mon, Mar 12, 2012 at 06:33:29PM +0100, Hannes Schüller wrote: > Hello Alexander! > > Alexander Foremny <ale...@go...> wrote: > > It might be nice to have options to *download* a file, *open* a file > > and *download and open* a file. However, I would not want to see a > > dialog showing up for this. Maybe this can be done by different > > hinting modes > > Thanks for this - sounds like a decent plan to me. The default (i.e. > "clicking") could still be plain downloading, but with hinting, we can > distinguish between these user intentions. Would that be alright with > everyone? > > Hannes > ------------------------------------------------------------------------------ > Try before you buy = See our experts in action! > The most comprehensive online learning library for Microsoft developers > is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3, > Metro Style Apps, more. Free future releases when you subscribe now! > http://p.sf.net/sfu/learndevnow-dev2 > _______________________________________________ > Vimprobable-users mailing list > Vim...@li... > https://lists.sourceforge.net/lists/listinfo/vimprobable-users -- Matthew Carter je...@gm... |
From: Hannes S. <ha...@yl...> - 2012-04-10 18:57:42
Attachments:
signature.asc
|
Hannes Schüller <ha...@yl...> wrote: > Alexander Foremny <ale...@go...> wrote: > > It might be nice to have options to *download* a file, *open* a file > > and *download and open* a file. However, I would not want to see a > > dialog showing up for this. Maybe this can be done by different > > hinting modes > > Thanks for this - sounds like a decent plan to me. The default (i.e. > "clicking") could still be plain downloading, but with hinting, we can > distinguish between these user intentions. Would that be alright with > everyone? Steffen, any plans from your side to implement this? Hannes |