parse hyperlinks

Ref: T6329
This commit is contained in:
Boris Faure 2018-09-26 19:59:33 +02:00
parent 43ad696570
commit cc83e6b6f2
6 changed files with 243 additions and 1 deletions

View File

@ -1,5 +1,6 @@
#include "private.h"
#include <Elementary.h>
#include "termiolink.h"
#include "termio.h"
#include "sb.h"
#include "utf8.h"
@ -429,3 +430,101 @@ end:
ty_sb_free(&sb);
return s;
}
/* 0 means error here */
static uint16_t
_find_empty_slot(const Termpty *ty)
{
int pos;
int max_pos = HL_LINKS_MAX / 8;
for (pos = 0; pos < max_pos && ty->hl.bitmap[pos] == 0xff; pos++)
{
}
if (pos <= max_pos)
{
int bit;
for (bit = 0; bit < 8; bit++)
{
if (!(ty->hl.bitmap[pos] & (1<<bit)))
{
return pos * 8 + bit;
}
}
}
return 0;
}
static void
hl_bitmap_set_bit(Termpty *ty, uint16_t id)
{
uint8_t *pos = &ty->hl.bitmap[id / 8];
uint8_t bit = 1 << (id % 8);
*pos |= bit;
}
static void
hl_bitmap_clear_bit(Termpty *ty, uint16_t id)
{
uint8_t *pos = &ty->hl.bitmap[id / 8];
uint8_t bit = 1 << (id % 8);
*pos &= ~bit;
}
Term_Link *
term_link_new(Termpty *ty)
{
uint16_t id;
Term_Link *link;
/* 1st/ Find empty slot in bitmap */
id = _find_empty_slot(ty);
if (!id)
{
ERR("hyper links: can't find empty slot");
return NULL;
}
/* 2nd/ Do we need to realloc? */
if (id >= ty->hl.size)
{
Term_Link *links;
uint16_t old_size = ty->hl.size;
if (!ty->hl.size)
ty->hl.size = 256;
links = realloc(ty->hl.links,
ty->hl.size * 2 * sizeof(Term_Link));
if (!links)
return NULL;
ty->hl.size *= 2;
ty->hl.links = links;
memset(ty->hl.links + old_size,
0,
(ty->hl.size - old_size) * sizeof(Term_Link));
}
link = ty->hl.links + id;
link->key = NULL;
link->url = NULL;
link->refcount = 0;
/* Mark in bitmap */
hl_bitmap_set_bit(ty, id);
return link;
}
void
term_link_free(Term_Link *link, Termpty *ty)
{
if (!link)
return;
uint16_t id = (link - ty->hl.links);
/* Remove from bitmap */
hl_bitmap_clear_bit(ty, id);
}

View File

@ -1,6 +1,18 @@
#ifndef _TERMIO_LINK_H__
#define _TERMIO_LINK_H__ 1
#define HL_LINKS_MAX (1 << 16)
typedef struct _Termlink Term_Link;
struct _Termlink
{
char *key;
char *url;
unsigned int refcount;
};
char *termio_link_find(const Evas_Object *obj, int cx, int cy, int *x1r, int *y1r, int *x2r, int *y2r);
#endif

View File

@ -509,6 +509,16 @@ termpty_new(const char *cmd, Eina_Bool login_shell, const char *cd,
goto err;
}
ty->hl.bitmap = calloc(1, HL_LINKS_MAX / 8); /* bit map for 1 << 16 elements */
if (!ty->hl.bitmap)
{
ERR("Allocation of %d bytes failed: %s",
HL_LINKS_MAX / 8, strerror(errno));
goto err;
}
/* Mark id 0 as set */
ty->hl.bitmap[0] = 1;
termpty_resize_tabs(ty, 0, w);
termpty_reset_state(ty);
@ -739,6 +749,7 @@ termpty_new(const char *cmd, Eina_Bool login_shell, const char *cd,
err:
free(ty->screen);
free(ty->screen2);
free(ty->hl.bitmap);
if (ty->fd >= 0) close(ty->fd);
if (ty->slavefd >= 0) close(ty->slavefd);
free(ty);
@ -811,6 +822,20 @@ termpty_free(Termpty *ty)
}
free(ty->screen);
free(ty->screen2);
if (ty->hl.links)
{
uint16_t i;
for (i = 0; i < ty->hl.size; i++)
{
Term_Link *l = ty->hl.links + i;
free(l->key);
free(l->url);
}
free(ty->hl.links);
}
free(ty->hl.bitmap);
free(ty->buf);
free(ty->tabs);
free(ty);

View File

@ -3,6 +3,7 @@
#include "config.h"
#include "media.h"
#include "termiolink.h"
typedef struct _Termpty Termpty;
typedef struct _Termcell Termcell;
@ -74,6 +75,7 @@ struct _Termatt
#else
unsigned short bit_padding : 12;
#endif
uint16_t link_id;
};
struct _Termpty
@ -175,13 +177,17 @@ struct _Termpty
unsigned int mouse_mode : 3;
unsigned int mouse_ext : 2;
unsigned int bracketed_paste : 1;
struct {
Term_Link *links;
uint8_t *bitmap;
uint16_t size;
} hl;
};
struct _Termcell
{
Eina_Unicode codepoint;
Termatt att;
unsigned char padding[2];
};
struct _Termsave
@ -310,4 +316,10 @@ do { \
} while (0)
Term_Link *
term_link_new(Termpty *ty);
void
term_link_free(Term_Link *link, Termpty *ty);
#endif

View File

@ -1801,6 +1801,94 @@ err:
return -1;
}
static void
_handle_hyperlink(Termpty *ty,
char *s,
int len)
{
char *url = NULL;
char *key = NULL;
Term_Link *hl = NULL;
if (!s || len <= 0)
{
ERR("invalid hyperlink escape code (len:%d s:%p)",
len, s);
return;
}
if (len == 1 && *s == ';')
{
/* Closing escape code */
if (ty->termstate.att.link_id)
{
ty->termstate.att.link_id = 0;
}
else
{
ERR("invalid hyperlink escape code: no hyperlink to close"
" (len:%d s:%.*s)", len, len, s);
}
goto end;
}
if (*s != ';')
{
/* Parse parameters */
char *end;
/* /!\ we expect ';' and ':' to be escaped in params */
end = memchr(s+1, ';', len);
*end = '\0';
do
{
char *end_param;
end_param = strchrnul(s, ':');
if (len > 3 && strncmp(s, "id=", 3) == 0)
{
free(key);
s += 3;
len -= 3;
key = strndup(s, end_param - s);
}
len -= end_param - s;
s = end_param;
if (*end_param)
{
s++;
len--;
}
}
while (s < end);
*end = ';';
}
s++;
len--;
url = strndup(s, len);
if (!url)
goto end;
hl = term_link_new(ty);
if (!hl)
goto end;
hl->key = key;
hl->url = url;
key = NULL;
url = NULL;
ty->termstate.att.link_id = hl - ty->hl.links;
hl = NULL;
end:
term_link_free(hl, ty);
free(url);
free(key);
}
static void
_handle_xterm_50_command(Termpty *ty,
char *s,
@ -1994,6 +2082,11 @@ _handle_esc_xterm(Termpty *ty, const Eina_Unicode *c, const Eina_Unicode *ce)
WRN("set palette, not supported");
if ((cc - c) < 3) return 0;
break;
case 8:
DBG("hyperlink");
s = eina_unicode_unicode_to_utf8(p, &len);
_handle_hyperlink(ty, s, len);
break;
case 10:
if (!*p)
goto err;

View File

@ -421,6 +421,7 @@ termpty_reset_att(Termatt *att)
att->framed = 0;
att->encircled = 0;
att->overlined = 0;
att->link_id = 0;
}
void