From 64b58bb09452ae9799c477aef2767f9aecb981d9 Mon Sep 17 00:00:00 2001 From: Boris Faure Date: Wed, 12 Dec 2018 21:42:42 +0100 Subject: [PATCH] termio/win: handle hyperlinks on right click Fix segfault --- src/bin/termio.c | 135 +++++++++++++++++++++++++++++++++++------------ src/bin/win.c | 17 ++++-- 2 files changed, 114 insertions(+), 38 deletions(-) diff --git a/src/bin/termio.c b/src/bin/termio.c index f24a0faf..abf6ae6c 100644 --- a/src/bin/termio.c +++ b/src/bin/termio.c @@ -51,7 +51,7 @@ struct _Termio int button; } mouse; struct { - char *string; + const char *string; int x1, y1, x2, y2; int suspend; uint16_t id; @@ -819,12 +819,40 @@ _should_inline(const Evas_Object *obj) return EINA_TRUE; } -const char * +/* Need to be freed */ +const char * termio_link_get(const Evas_Object *obj) { Termio *sd = evas_object_smart_data_get(obj); EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL); - return sd->link.string; + const char *link; + + if (!sd->link.string && !sd->link.id) + return NULL; + link = sd->link.string; + if (sd->link.id) + { + Term_Link *hl = &sd->pty->hl.links[sd->link.id]; + if (!hl->url) + return NULL; + link = hl->url; + } + if (link_is_url(link)) + { + if (casestartswith(link, "file://")) + { + // TODO: decode string: %XX -> char + link = link + sizeof("file://") - 1; + /* Handle cases where / is ommitted: file://HOSTNAME/home/ */ + if (link[0] != '/') + { + link = strchr(link, '/'); + if (!link) + return NULL; + } + } + } + return strdup(link); } static void @@ -834,33 +862,32 @@ _activate_link(Evas_Object *obj, Eina_Bool may_inline) Config *config; char buf[PATH_MAX], *s, *escaped; const char *path = NULL, *cmd = NULL; + const char *link = NULL; Eina_Bool url = EINA_FALSE, email = EINA_FALSE, handled = EINA_FALSE; EINA_SAFETY_ON_NULL_RETURN(sd); config = sd->config; if (!config) return; if (!config->active_links) return; - if (!sd->link.string) return; - if (link_is_url(sd->link.string)) - { - if (casestartswith(sd->link.string, "file://")) - // TODO: decode string: %XX -> char - path = sd->link.string + sizeof("file://") - 1; - else - url = EINA_TRUE; - } - else if (sd->link.string[0] == '/') - path = sd->link.string; - else if (link_is_email(sd->link.string)) + + link = termio_link_get(obj); + if (!link) + return; + + if (link_is_url(link)) + url = EINA_TRUE; + else if (link[0] == '/') + path = link; + else if (link_is_email(link)) email = EINA_TRUE; - if (url && casestartswith(sd->link.string, "mailto:")) + if (url && casestartswith(link, "mailto:")) { email = EINA_TRUE; url = EINA_FALSE; } - s = eina_str_escape(sd->link.string); + s = eina_str_escape(link); if (!s) return; if (email) { @@ -888,10 +915,10 @@ _activate_link(Evas_Object *obj, Eina_Bool may_inline) // locally accessible file cmd = "xdg-open"; - escaped = ecore_file_escape_name(s); + escaped = ecore_file_escape_name(path); if (escaped) { - Media_Type type = media_src_type_get(sd->link.string); + Media_Type type = media_src_type_get(path); if (may_inline && _should_inline(obj)) { if ((type == MEDIA_TYPE_IMG) || @@ -942,7 +969,7 @@ _activate_link(Evas_Object *obj, Eina_Bool may_inline) escaped = ecore_file_escape_name(s); if (escaped) { - Media_Type type = media_src_type_get(sd->link.string); + Media_Type type = media_src_type_get(link); if (may_inline && _should_inline(obj)) { evas_object_smart_callback_call(obj, "popup", NULL); @@ -1050,9 +1077,20 @@ _cb_ctxp_link_content_copy(void *data, size_t len; EINA_SAFETY_ON_NULL_RETURN(sd); - raw_link = termio_selection_get(term, sd->link.x1, sd->link.y1, sd->link.x2, sd->link.y2, &len, EINA_FALSE); + if (sd->link.id) + { + Term_Link *hl = &sd->pty->hl.links[sd->link.id]; - _take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, raw_link); + if (!hl->url) + return; + _take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, hl->url); + } + else + { + raw_link = termio_selection_get(term, sd->link.x1, sd->link.y1, sd->link.x2, sd->link.y2, &len, EINA_FALSE); + + _take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, raw_link); + } sd->ctxpopup = NULL; evas_object_del(obj); @@ -1082,6 +1120,16 @@ _cb_link_down(void *data, Evas_Event_Mouse_Down *ev = event; Termio *sd = evas_object_smart_data_get(data); EINA_SAFETY_ON_NULL_RETURN(sd); + Term_Link *hl = NULL; + + + if (!sd->link.string && sd->link.id) + { + hl = &sd->pty->hl.links[sd->link.id]; + if (!hl->url) + return; + sd->link.string = eina_stringshare_add(hl->url); + } if (ev->button == 1) { @@ -1093,7 +1141,7 @@ _cb_link_down(void *data, { Evas_Object *ctxp; Eina_Bool absolut = EINA_FALSE; - char *raw_link; + const char *raw_link; size_t len; if (sd->pty->selection.is_active) @@ -1121,7 +1169,20 @@ _cb_link_down(void *data, } elm_ctxpopup_item_append(ctxp, _("Open"), NULL, _cb_ctxp_link_open, sd->self); - raw_link = termio_selection_get(sd->self, sd->link.x1, sd->link.y1, sd->link.x2, sd->link.y2, &len, EINA_FALSE); + if (hl) + { + raw_link = hl->url; + } + else + { + raw_link = termio_selection_get(sd->self, + sd->link.x1, + sd->link.y1, + sd->link.x2, + sd->link.y2, + &len, + EINA_FALSE); + } if (len > 0 && raw_link[0] == '/') absolut = EINA_TRUE; @@ -1145,7 +1206,8 @@ _cb_link_down(void *data, _cb_ctxp_dismissed, sd); evas_object_event_callback_add(ctxp, EVAS_CALLBACK_DEL, _cb_ctxp_del, sd); - free(raw_link); + if (!hl) + free((void*)raw_link); } } @@ -1257,7 +1319,8 @@ _cb_link_move(void *data, Evas_Coord dx, dy; EINA_SAFETY_ON_NULL_RETURN(sd); - if (!sd->link.down.down) return; + if (!sd->link.down.down) + return; dx = abs(ev->cur.canvas.x - sd->link.down.x); dy = abs(ev->cur.canvas.y - sd->link.down.y); if ((sd->config->drag_links) && @@ -1384,7 +1447,7 @@ _remove_links(Termio *sd) if (sd->link.string) { - free(sd->link.string); + eina_stringshare_del(sd->link.string); sd->link.string = NULL; } sd->link.x1 = -1; @@ -2511,7 +2574,7 @@ termio_take_selection(Evas_Object *obj, Elm_Sel_Type type) { Termio *sd = evas_object_smart_data_get(obj); int start_x = 0, start_y = 0, end_x = 0, end_y = 0; - char *s = NULL; + const char *s = NULL; size_t len = 0; EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); @@ -2533,7 +2596,7 @@ termio_take_selection(Evas_Object *obj, Elm_Sel_Type type) if (sd->link.string) { len = strlen(sd->link.string); - s = strndup(sd->link.string, len); + s = eina_stringshare_add_length(sd->link.string, len); } goto end; } @@ -2560,12 +2623,14 @@ termio_take_selection(Evas_Object *obj, Elm_Sel_Type type) } len = eina_strbuf_length_get(sb); s = eina_strbuf_string_steal(sb); + s = eina_stringshare_add_length(s, len); eina_strbuf_free(sb); } else { s = termio_selection_get(obj, start_x, start_y, end_x, end_y, &len, EINA_TRUE); + s = eina_stringshare_add_length(s, len); } end: @@ -2573,7 +2638,7 @@ end: { if ((sd->win) && (len > 0)) _take_selection_text(sd, type, s); - free(s); + eina_stringshare_del(s); return EINA_TRUE; } return EINA_FALSE; @@ -3711,7 +3776,8 @@ _smart_mouseover_apply(Evas_Object *obj) EINA_SAFETY_ON_NULL_RETURN(sd); config = sd->config; - if (!config->active_links) return; + if (!config->active_links) + return; if ((sd->mouse.cx < 0) || (sd->mouse.cy < 0) || (sd->link.suspend) || (!evas_object_focus_get(obj))) @@ -3741,8 +3807,8 @@ _smart_mouseover_apply(Evas_Object *obj) } if (sd->link.string) - free(sd->link.string); - sd->link.string = s; + eina_stringshare_del(sd->link.string); + sd->link.string = eina_stringshare_add(s); if ((x1 == sd->link.x1) && (y1 == sd->link.y1) && (x2 == sd->link.x2) && (y2 == sd->link.y2)) @@ -5531,7 +5597,8 @@ _smart_del(Evas_Object *obj) if (sd->mouseover_delay) ecore_timer_del(sd->mouseover_delay); if (sd->font.name) eina_stringshare_del(sd->font.name); if (sd->pty) termpty_free(sd->pty); - if (sd->link.string) free(sd->link.string); + if (sd->link.string) + eina_stringshare_del(sd->link.string); if (sd->glayer) evas_object_del(sd->glayer); if (sd->win) evas_object_event_callback_del_full(sd->win, EVAS_CALLBACK_DEL, diff --git a/src/bin/win.c b/src/bin/win.c index d6c4b759..5b3dd013 100644 --- a/src/bin/win.c +++ b/src/bin/win.c @@ -4401,9 +4401,13 @@ _cb_popup(void *data, Term *term = data; const char *src = event; - if (!src) src = termio_link_get(term->termio); - if (!src) return; + if (!src) + src = termio_link_get(term->termio); + if (!src) + return; _popmedia(term, src); + if (!event) + free((void*)src); } static void @@ -4413,9 +4417,14 @@ _cb_popup_queue(void *data, { Term *term = data; const char *src = event; - if (!src) src = termio_link_get(term->termio); - if (!src) return; + + if (!src) + src = termio_link_get(term->termio); + if (!src) + return; _popmedia_queue_add(term, src); + if (!event) + free((void*)src); } static void