diff --git a/sundown/autolink.c b/sundown/autolink.c index 697de42..a67e6ca 100644 --- a/sundown/autolink.c +++ b/sundown/autolink.c @@ -26,12 +26,26 @@ #define strncasecmp _strnicmp #endif +static int +ismentionchar(char c) +{ + return isalnum(c) || c == '_' || c == '-' || c == '+' || c == '/' || c == '='; +} + +static int +ishashtagend(char c) +{ + return isspace(c) || c == '(' || c == ')' || c == '[' || c == ']' || + c == ',' || c == '.' || c == '"'; +} + int sd_autolink_issafe(const uint8_t *link, size_t link_len) { - static const size_t valid_uris_count = 8; + static const size_t valid_uris_count = 12; static const char *valid_uris[] = { "§", + "@", "&", "%", "#", "gemini://", "gopher://", "/", "http://", "https://", "ftp://", "mailto:" }; @@ -329,3 +343,46 @@ sd_autolink__zet( return link_end; } + +size_t +sd_autolink__ssb( + size_t *rewind_p, + struct buf *link, + uint8_t *data, + size_t max_rewind, + size_t size, + unsigned int flags) +{ + size_t link_end = 1, rewind = 0, domain_len; + char sigil = data[0]; + + if (sigil == '#') { + while (link_end < size && isdigit(data[link_end])) + link_end++; + + if (link_end < size && (isspace(data[link_end]) || + ispunct(data[link_end]))) + return 0; + + while (link_end < size && !ishashtagend(data[link_end])) + link_end++; + + } else { + if (sigil != '%' && sigil != '@' && sigil != '&') + return 0; + + while (link_end < size && (ismentionchar(data[link_end]) || data[link_end] == '.')) + link_end++; + + if (ispunct(data[link_end-1])) + link_end--; + + if (link_end < 2) + return 0; + } + + bufput(link, data - rewind, link_end + rewind); + *rewind_p = rewind; + + return link_end; +} diff --git a/sundown/autolink.h b/sundown/autolink.h index 8593659..94a254b 100644 --- a/sundown/autolink.h +++ b/sundown/autolink.h @@ -46,6 +46,10 @@ size_t sd_autolink__zet(size_t *rewind_p, struct buf *link, uint8_t *data, size_t offset, size_t size, unsigned int flags); +size_t +sd_autolink__ssb(size_t *rewind_p, struct buf *link, + uint8_t *data, size_t offset, size_t size, unsigned int flags); + #ifdef __cplusplus } #endif diff --git a/sundown/markdown.c b/sundown/markdown.c index f40aaee..81aab21 100644 --- a/sundown/markdown.c +++ b/sundown/markdown.c @@ -71,7 +71,9 @@ static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *dat static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_at(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_ssb(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); static size_t char_autolink_zet(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); @@ -86,8 +88,9 @@ enum markdown_char_t { MD_CHAR_ESCAPE, MD_CHAR_ENTITITY, MD_CHAR_AUTOLINK_URL, - MD_CHAR_AUTOLINK_EMAIL, + MD_CHAR_AUTOLINK_AT, MD_CHAR_AUTOLINK_WWW, + MD_CHAR_AUTOLINK_SSB, MD_CHAR_AUTOLINK_ZET, MD_CHAR_SUPERSCRIPT, }; @@ -102,8 +105,9 @@ static char_trigger markdown_char_ptrs[] = { &char_escape, &char_entity, &char_autolink_url, - &char_autolink_email, + &char_autolink_at, &char_autolink_www, + &char_autolink_ssb, &char_autolink_zet, &char_superscript, }; @@ -817,6 +821,17 @@ char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, siz return link_len; } +static size_t +char_autolink_at(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + size_t link_len; + + link_len = char_autolink_ssb(ob, rndr, data, offset, size); + if (link_len) return link_len; + + return char_autolink_email(ob, rndr, data, offset, size); +} + static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) { @@ -837,6 +852,26 @@ char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_ return link_len; } +static size_t +char_autolink_ssb(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + struct buf *link; + size_t link_len, rewind; + + if (!rndr->cb.autolink || rndr->in_link_body) + return 0; + + link = rndr_newbuf(rndr, BUFFER_SPAN); + + if ((link_len = sd_autolink__ssb(&rewind, link, data, offset, size, 0)) > 0) { + ob->size -= rewind; + rndr->cb.autolink(ob, link, MKDA_SSB, rndr->opaque); + } + + rndr_popbuf(rndr, BUFFER_SPAN); + return link_len; +} + static size_t char_autolink_zet(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) { @@ -2457,8 +2492,11 @@ sd_markdown_new( if (extensions & MKDEXT_AUTOLINK) { md->active_char[':'] = MD_CHAR_AUTOLINK_URL; - md->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL; + md->active_char['@'] = MD_CHAR_AUTOLINK_AT; md->active_char['w'] = MD_CHAR_AUTOLINK_WWW; + md->active_char['%'] = MD_CHAR_AUTOLINK_SSB; + md->active_char['&'] = MD_CHAR_AUTOLINK_SSB; + md->active_char['#'] = MD_CHAR_AUTOLINK_SSB; md->active_char[194] = MD_CHAR_AUTOLINK_ZET; } diff --git a/sundown/markdown.h b/sundown/markdown.h index 6e280d9..e7c8e6d 100644 --- a/sundown/markdown.h +++ b/sundown/markdown.h @@ -40,7 +40,8 @@ enum mkd_autolink { MKDA_NOT_AUTOLINK, /* used internally when it is not an autolink*/ MKDA_NORMAL, /* normal http/http/ftp/mailto/etc link */ MKDA_EMAIL, /* e-mail link without explit mailto: */ - MKDA_ZET, /* zet link */ + MKDA_SSB, /* ssb link */ + MKDA_ZET, /* zet link */ }; enum mkd_tableflags { diff --git a/zet.dpi.c b/zet.dpi.c index 8d206d4..c548941 100644 --- a/zet.dpi.c +++ b/zet.dpi.c @@ -27,6 +27,9 @@ static const size_t sigil_size = sizeof("§")-1; static const int id_length = 16; +static const char ssb_ref_base[] = "http://localhost:8027/"; +static const char ssb_channel_base[] = "http://localhost:8027/channel/"; + static const int max_cols = 40; static char server_hostname[256]; @@ -473,7 +476,14 @@ md_rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, co if (!strncmp((char *)link->data, "§", sigil_size)) houdini_escape_href(ob, link->data + sigil_size, link->size - sigil_size); - else + if (link->data[0] == '#') { + BUFPUTSL(ob, ssb_channel_base); + houdini_escape_href(ob, link->data + 1, link->size - 1); + } else if (link->data[0] == '#' || link->data[0] == '@' + || link->data[0] == '&' || link->data[0] == '%') { + BUFPUTSL(ob, ssb_ref_base); + houdini_escape_href(ob, link->data, link->size); + } else houdini_escape_href(ob, link->data, link->size); } @@ -495,12 +505,13 @@ md_rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, co return 1; } -static void put_link_truncated(struct buf *ob, const struct buf *link) { - if (link->size < max_cols) { - return houdini_escape_html0(ob, link->data, link->size, 0); +static void put_link_truncated(struct buf *ob, const struct buf *link, int max_length) { + if (link->size > max_length) { + houdini_escape_html0(ob, link->data, max_length, 0); + BUFPUTSL(ob, "…"); + } else { + houdini_escape_html0(ob, link->data, link->size, 0); } - houdini_escape_html0(ob, link->data, max_cols, 0); - BUFPUTSL(ob, "…"); } static int @@ -521,7 +532,15 @@ md_rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, BUFPUTSL(ob, "mailto:"); if (type == MKDA_ZET) houdini_escape_href(ob, link->data + sigil_size, link->size - sigil_size); - else + else if (type == MKDA_SSB) { + if (link->data[0] == '#') { + BUFPUTSL(ob, ssb_channel_base); + houdini_escape_href(ob, link->data + 1, link->size - 1); + } else { + BUFPUTSL(ob, ssb_ref_base); + houdini_escape_href(ob, link->data, link->size); + } + } else houdini_escape_href(ob, link->data, link->size); if (type == MKDA_ZET) { @@ -548,8 +567,10 @@ md_rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, */ if (bufprefix(link, "mailto:") == 0) { houdini_escape_html0(ob, link->data + 7, link->size - 7, 0); + } else if (link->data[0] == '&' || link->data[0] == '@' || link->data[0] == '%') { + put_link_truncated(ob, link, 8); } else { - put_link_truncated(ob, link); + put_link_truncated(ob, link, max_cols); } BUFPUTSL(ob, "");