diff options
| author | 2023-08-17 17:38:39 +0200 | |
|---|---|---|
| committer | 2023-08-17 17:38:39 +0200 | |
| commit | 6ede6497ad2a63e5ff2b57abba827d0da469f923 (patch) | |
| tree | a183cf6a66ff73b3a524fdc8955036f58ecea454 | |
| parent | Return the history in reverse order, which has more sense. (diff) | |
| download | snac2-6ede6497ad2a63e5ff2b57abba827d0da469f923.tar.gz snac2-6ede6497ad2a63e5ff2b57abba827d0da469f923.tar.xz snac2-6ede6497ad2a63e5ff2b57abba827d0da469f923.zip | |
Convert 'Link' attachments that have a media extension to something more useful.
| -rw-r--r-- | html.c | 10 | ||||
| -rw-r--r-- | xs_mime.h | 90 | ||||
| -rw-r--r-- | xs_regex.h | 10 | ||||
| -rw-r--r-- | xs_unicode.h | 51 | ||||
| -rw-r--r-- | xs_version.h | 2 |
5 files changed, 112 insertions, 51 deletions
| @@ -1313,6 +1313,16 @@ xs_str *html_entry(snac *user, xs_str *os, const xs_dict *msg, int local, | |||
| 1313 | if (xs_is_null(url)) | 1313 | if (xs_is_null(url)) |
| 1314 | continue; | 1314 | continue; |
| 1315 | 1315 | ||
| 1316 | /* if it's a plain Link, check if it can be "rewritten" */ | ||
| 1317 | if (strcmp(t, "Link") == 0) { | ||
| 1318 | const char *mt = xs_mime_by_ext(url); | ||
| 1319 | |||
| 1320 | if (xs_startswith(mt, "image/") || | ||
| 1321 | xs_startswith(mt, "audio/") || | ||
| 1322 | xs_startswith(mt, "video/")) | ||
| 1323 | t = mt; | ||
| 1324 | } | ||
| 1325 | |||
| 1316 | const char *name = xs_dict_get(v, "name"); | 1326 | const char *name = xs_dict_get(v, "name"); |
| 1317 | if (xs_is_null(name)) | 1327 | if (xs_is_null(name)) |
| 1318 | name = xs_dict_get(msg, "name"); | 1328 | name = xs_dict_get(msg, "name"); |
| @@ -6,57 +6,67 @@ | |||
| 6 | 6 | ||
| 7 | const char *xs_mime_by_ext(const char *file); | 7 | const char *xs_mime_by_ext(const char *file); |
| 8 | 8 | ||
| 9 | extern const char *xs_mime_types[]; | ||
| 10 | |||
| 9 | #ifdef XS_IMPLEMENTATION | 11 | #ifdef XS_IMPLEMENTATION |
| 10 | 12 | ||
| 11 | /* intentionally brain-dead simple */ | 13 | /* intentionally brain-dead simple */ |
| 12 | struct _mime_info { | 14 | /* CAUTION: sorted */ |
| 13 | const char *type; | 15 | |
| 14 | const char *ext; | 16 | const char *xs_mime_types[] = { |
| 15 | } mime_info[] = { | 17 | "3gp", "video/3gpp", |
| 16 | { "application/json", ".json" }, | 18 | "aac", "audio/aac", |
| 17 | { "image/gif", ".gif" }, | 19 | "css", "text/css", |
| 18 | { "image/jpeg", ".jpeg" }, | 20 | "flac", "audio/flac", |
| 19 | { "image/jpeg", ".jpg" }, | 21 | "flv", "video/flv", |
| 20 | { "image/png", ".png" }, | 22 | "gif", "image/gif", |
| 21 | { "image/webp", ".webp" }, | 23 | "gmi", "text/gemini", |
| 22 | { "video/mp4", ".mp4" }, | 24 | "html", "text/html", |
| 23 | { "video/mp4", ".mpg4" }, | 25 | "jpeg", "image/jpeg", |
| 24 | { "video/mp4", ".m4v" }, | 26 | "jpg", "image/jpeg", |
| 25 | { "video/webm", ".webm" }, | 27 | "json", "application/json", |
| 26 | { "video/quicktime", ".mov" }, | 28 | "m4a", "audio/aac", |
| 27 | { "video/3gpp", ".3gp" }, | 29 | "m4v", "video/mp4", |
| 28 | { "video/ogg", ".ogv" }, | 30 | "md", "text/markdown", |
| 29 | { "video/flv", ".flv" }, | 31 | "mov", "video/quicktime", |
| 30 | { "audio/mp3", ".mp3" }, | 32 | "mp3", "audio/mp3", |
| 31 | { "audio/ogg", ".ogg" }, | 33 | "mp4", "video/mp4", |
| 32 | { "audio/ogg", ".oga" }, | 34 | "mpg4", "video/mp4", |
| 33 | { "audio/ogg", ".opus" }, | 35 | "oga", "audio/ogg", |
| 34 | { "audio/flac", ".flac" }, | 36 | "ogg", "audio/ogg", |
| 35 | { "audio/wav", ".wav" }, | 37 | "ogv", "video/ogg", |
| 36 | { "audio/wma", ".wma" }, | 38 | "opus", "audio/ogg", |
| 37 | { "audio/aac", ".aac" }, | 39 | "png", "image/png", |
| 38 | { "audio/aac", ".m4a" }, | 40 | "txt", "text/plain", |
| 39 | { "text/css", ".css" }, | 41 | "wav", "audio/wav", |
| 40 | { "text/html", ".html" }, | 42 | "webm", "video/webm", |
| 41 | { "text/plain", ".txt" }, | 43 | "webp", "image/webp", |
| 42 | { "text/xml", ".xml" }, | 44 | "wma", "audio/wma", |
| 43 | { "text/markdown", ".md" }, | 45 | "xml", "text/xml", |
| 44 | { "text/gemini", ".gmi" }, | 46 | NULL, NULL, |
| 45 | { NULL, NULL } | ||
| 46 | }; | 47 | }; |
| 47 | 48 | ||
| 48 | 49 | ||
| 49 | const char *xs_mime_by_ext(const char *file) | 50 | const char *xs_mime_by_ext(const char *file) |
| 50 | /* returns the MIME type by file extension */ | 51 | /* returns the MIME type by file extension */ |
| 51 | { | 52 | { |
| 52 | struct _mime_info *mi = mime_info; | 53 | const char *ext = strrchr(file, '.'); |
| 53 | xs *lfile = xs_tolower_i(xs_dup(file)); | 54 | |
| 55 | if (ext) { | ||
| 56 | const char **p = xs_mime_types; | ||
| 57 | xs *uext = xs_tolower_i(xs_dup(ext + 1)); | ||
| 58 | |||
| 59 | while (**p) { | ||
| 60 | int c; | ||
| 54 | 61 | ||
| 55 | while (mi->type != NULL) { | 62 | if ((c = strcmp(*p, uext)) == 0) |
| 56 | if (xs_endswith(lfile, mi->ext)) | 63 | return p[1]; |
| 57 | return mi->type; | 64 | else |
| 65 | if (c > 0) | ||
| 66 | break; | ||
| 58 | 67 | ||
| 59 | mi++; | 68 | p += 2; |
| 69 | } | ||
| 60 | } | 70 | } |
| 61 | 71 | ||
| 62 | return "application/octet-stream"; | 72 | return "application/octet-stream"; |
| @@ -8,8 +8,10 @@ xs_list *xs_regex_split_n(const char *str, const char *rx, int count); | |||
| 8 | #define xs_regex_split(str, rx) xs_regex_split_n(str, rx, XS_ALL) | 8 | #define xs_regex_split(str, rx) xs_regex_split_n(str, rx, XS_ALL) |
| 9 | xs_list *xs_regex_match_n(const char *str, const char *rx, int count); | 9 | xs_list *xs_regex_match_n(const char *str, const char *rx, int count); |
| 10 | #define xs_regex_match(str, rx) xs_regex_match_n(str, rx, XS_ALL) | 10 | #define xs_regex_match(str, rx) xs_regex_match_n(str, rx, XS_ALL) |
| 11 | xs_list *xs_regex_replace_n(const char *str, const char *rx, const char *rep, int count); | 11 | xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int count); |
| 12 | #define xs_regex_replace(str, rx, rep) xs_regex_replace_n(str, rx, rep, XS_ALL) | 12 | #define xs_regex_replace_i(str, rx, rep) xs_regex_replace_in(str, rx, rep, XS_ALL) |
| 13 | #define xs_regex_replace_n(str, rx, rep, count) xs_regex_replace_in(xs_dup(str), rx, rep, count) | ||
| 14 | #define xs_regex_replace(str, rx, rep) xs_regex_replace_in(xs_dup(str), rx, rep, XS_ALL) | ||
| 13 | 15 | ||
| 14 | #ifdef XS_IMPLEMENTATION | 16 | #ifdef XS_IMPLEMENTATION |
| 15 | 17 | ||
| @@ -78,7 +80,7 @@ xs_list *xs_regex_match_n(const char *str, const char *rx, int count) | |||
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | 82 | ||
| 81 | xs_list *xs_regex_replace_n(const char *str, const char *rx, const char *rep, int count) | 83 | xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int count) |
| 82 | /* replaces all matches with the rep string. If it contains unescaped &, | 84 | /* replaces all matches with the rep string. If it contains unescaped &, |
| 83 | they are replaced with the match */ | 85 | they are replaced with the match */ |
| 84 | { | 86 | { |
| @@ -121,6 +123,8 @@ xs_list *xs_regex_replace_n(const char *str, const char *rx, const char *rep, in | |||
| 121 | n++; | 123 | n++; |
| 122 | } | 124 | } |
| 123 | 125 | ||
| 126 | xs_free(str); | ||
| 127 | |||
| 124 | return s; | 128 | return s; |
| 125 | } | 129 | } |
| 126 | 130 | ||
diff --git a/xs_unicode.h b/xs_unicode.h index 48cd660..35cd9f7 100644 --- a/xs_unicode.h +++ b/xs_unicode.h | |||
| @@ -4,8 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #define _XS_UNICODE_H | 5 | #define _XS_UNICODE_H |
| 6 | 6 | ||
| 7 | int _xs_utf8_enc(char buf[4], unsigned int cpoint); | ||
| 7 | xs_str *xs_utf8_enc(xs_str *str, unsigned int cpoint); | 8 | xs_str *xs_utf8_enc(xs_str *str, unsigned int cpoint); |
| 8 | unsigned int xs_utf8_dec(char **str); | 9 | unsigned int xs_utf8_dec(char **str); |
| 10 | int xs_unicode_width(unsigned int cpoint); | ||
| 9 | unsigned int *_xs_unicode_upper_search(unsigned int cpoint); | 11 | unsigned int *_xs_unicode_upper_search(unsigned int cpoint); |
| 10 | unsigned int *_xs_unicode_lower_search(unsigned int cpoint); | 12 | unsigned int *_xs_unicode_lower_search(unsigned int cpoint); |
| 11 | #define xs_unicode_is_upper(cpoint) (!!_xs_unicode_upper_search(cpoint)) | 13 | #define xs_unicode_is_upper(cpoint) (!!_xs_unicode_upper_search(cpoint)) |
| @@ -18,8 +20,8 @@ | |||
| 18 | #ifdef XS_IMPLEMENTATION | 20 | #ifdef XS_IMPLEMENTATION |
| 19 | 21 | ||
| 20 | 22 | ||
| 21 | char *_xs_utf8_enc(char buf[4], unsigned int cpoint) | 23 | int _xs_utf8_enc(char buf[4], unsigned int cpoint) |
| 22 | /* encodes an Unicode codepoint to utf-8 into buf and returns the new position */ | 24 | /* encodes an Unicode codepoint to utf-8 into buf and returns the size in bytes */ |
| 23 | { | 25 | { |
| 24 | unsigned char *p = (unsigned char *)buf; | 26 | unsigned char *p = (unsigned char *)buf; |
| 25 | 27 | ||
| @@ -42,18 +44,18 @@ char *_xs_utf8_enc(char buf[4], unsigned int cpoint) | |||
| 42 | *p++ = 0x80 | (cpoint & 0x3f); | 44 | *p++ = 0x80 | (cpoint & 0x3f); |
| 43 | } | 45 | } |
| 44 | 46 | ||
| 45 | return (char *)p; | 47 | return p - (unsigned char *)buf; |
| 46 | } | 48 | } |
| 47 | 49 | ||
| 48 | 50 | ||
| 49 | xs_str *xs_utf8_enc(xs_str *str, unsigned int cpoint) | 51 | xs_str *xs_utf8_enc(xs_str *str, unsigned int cpoint) |
| 50 | /* encodes an Unicode codepoint to utf-8 into str */ | 52 | /* encodes an Unicode codepoint to utf-8 into str */ |
| 51 | { | 53 | { |
| 52 | char tmp[4], *p; | 54 | char tmp[4]; |
| 53 | 55 | ||
| 54 | p = _xs_utf8_enc(tmp, cpoint); | 56 | int c = _xs_utf8_enc(tmp, cpoint); |
| 55 | 57 | ||
| 56 | return xs_append_m(str, tmp, p - tmp); | 58 | return xs_append_m(str, tmp, c); |
| 57 | } | 59 | } |
| 58 | 60 | ||
| 59 | 61 | ||
| @@ -99,9 +101,44 @@ unsigned int xs_utf8_dec(char **str) | |||
| 99 | } | 101 | } |
| 100 | 102 | ||
| 101 | 103 | ||
| 104 | /* intentionally dead simple */ | ||
| 105 | |||
| 106 | static unsigned int xs_unicode_width_table[] = { | ||
| 107 | 0x300, 0x36f, 0, /* diacritics */ | ||
| 108 | 0x1100, 0x11ff, 2, /* Hangul */ | ||
| 109 | 0x2e80, 0xa4cf, 2, /* CJK */ | ||
| 110 | 0xac00, 0xd7a3, 2, /* more Hangul */ | ||
| 111 | 0xe000, 0xf8ff, 0, /* private use */ | ||
| 112 | 0xf900, 0xfaff, 2, /* CJK compatibility */ | ||
| 113 | 0xff00, 0xff60, 2, /* full width things */ | ||
| 114 | 0xffdf, 0xffe6, 2, /* full width things */ | ||
| 115 | 0x1f200, 0x1ffff, 2, /* emojis */ | ||
| 116 | 0x20000, 0x2fffd, 2 /* more CJK */ | ||
| 117 | }; | ||
| 118 | |||
| 119 | int xs_unicode_width(unsigned int cpoint) | ||
| 120 | /* returns the width in columns of a Unicode codepoint (somewhat simplified) */ | ||
| 121 | { | ||
| 122 | unsigned int *p = xs_unicode_width_table; | ||
| 123 | unsigned int *e = p + sizeof(xs_unicode_width_table) / sizeof(unsigned int); | ||
| 124 | |||
| 125 | while (p < e) { | ||
| 126 | if (cpoint < p[0]) | ||
| 127 | return 1; | ||
| 128 | |||
| 129 | if (cpoint >= p[0] && cpoint <= p[1]) | ||
| 130 | return p[2]; | ||
| 131 | |||
| 132 | p += 3; | ||
| 133 | } | ||
| 134 | |||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | |||
| 102 | #ifdef _XS_UNICODE_TBL_H | 139 | #ifdef _XS_UNICODE_TBL_H |
| 103 | 140 | ||
| 104 | /* include xs_unicode_tbl.h before to use these functions */ | 141 | /* include xs_unicode_tbl.h before this one to use these functions */ |
| 105 | 142 | ||
| 106 | static int int_cmp(const void *p1, const void *p2) | 143 | static int int_cmp(const void *p1, const void *p2) |
| 107 | { | 144 | { |
diff --git a/xs_version.h b/xs_version.h index ae43ff4..8b2dea3 100644 --- a/xs_version.h +++ b/xs_version.h | |||
| @@ -1 +1 @@ | |||
| /* b7e9713d90382d8da0b58023f4c78416e6ca1bc5 */ | /* e85f257dd8fcb2980fd21aa37c1594c1461ddf48 */ | ||