diff --git a/xC.c b/xC.c index e714c65..96d54b5 100644 --- a/xC.c +++ b/xC.c @@ -2961,6 +2961,7 @@ attr_printer_apply (struct attr_printer *self, attr_printer_reset (self); + // TEXT_MONOSPACE is unimplemented, for obvious reasons if (text_attrs) attr_printer_tputs (self, tparm (set_attributes, 0, // standout @@ -3153,8 +3154,6 @@ formatter_add_item (struct formatter *self, struct formatter_item template_) FORMATTER_ADD_ITEM ((self), ATTR, .attribute = ATTR_RESET) #define FORMATTER_ADD_TEXT(self, text_) \ FORMATTER_ADD_ITEM ((self), TEXT, .text = (text_)) -#define FORMATTER_ADD_SIMPLE(self, attribute_) \ - FORMATTER_ADD_ITEM ((self), SIMPLE, .attribute = TEXT_ ## attribute_) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -3225,6 +3224,82 @@ irc_parse_mirc_color (const char *s, uint8_t *fg, uint8_t *bg) return s; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct irc_char_attrs +{ + uint8_t fg, bg; ///< {Fore,back}ground colour or 99 + uint8_t attributes; ///< TEXT_* flags, except TEXT_BLINK + uint8_t starts_at_boundary; ///< Possible to split here? +}; + +static void +irc_serialize_char_attrs (const struct irc_char_attrs *attrs, struct str *out) +{ + soft_assert (attrs->fg < 100 && attrs->bg < 100); + + if (attrs->fg != 99 || attrs->bg != 99) + { + str_append_printf (out, "\x03%u", attrs->fg); + if (attrs->bg != 99) + str_append_printf (out, ",%02u", attrs->bg); + } + if (attrs->attributes & TEXT_BOLD) str_append_c (out, '\x02'); + if (attrs->attributes & TEXT_ITALIC) str_append_c (out, '\x1d'); + if (attrs->attributes & TEXT_UNDERLINE) str_append_c (out, '\x1f'); + if (attrs->attributes & TEXT_INVERSE) str_append_c (out, '\x16'); + if (attrs->attributes & TEXT_CROSSED_OUT) str_append_c (out, '\x1e'); + if (attrs->attributes & TEXT_MONOSPACE) str_append_c (out, '\x11'); +} + +static int +irc_parse_attribute (char c) +{ + switch (c) + { + case '\x02' /* ^B */: return TEXT_BOLD; + case '\x11' /* ^Q */: return TEXT_MONOSPACE; + case '\x16' /* ^V */: return TEXT_INVERSE; + case '\x1d' /* ^] */: return TEXT_ITALIC; + case '\x1e' /* ^^ */: return TEXT_CROSSED_OUT; + case '\x1f' /* ^_ */: return TEXT_UNDERLINE; + case '\x0f' /* ^O */: return -1; + } + return 0; +} + +// The text needs to be NUL-terminated, and a valid UTF-8 string +static struct irc_char_attrs * +irc_analyze_text (const char *text, size_t len) +{ + struct irc_char_attrs *attrs = xcalloc (len, sizeof *attrs), + blank = { .fg = 99, .bg = 99, .starts_at_boundary = true }, + next = blank, cur = next; + + for (size_t i = 0; i != len; cur = next) + { + const char *start = text; + hard_assert (utf8_decode (&text, len - i) >= 0); + + int attribute = irc_parse_attribute (*start); + if (*start == '\x03') + text = irc_parse_mirc_color (text, &next.fg, &next.bg); + else if (attribute > 0) + next.attributes ^= attribute; + else if (attribute < 0) + next = blank; + + while (start++ != text) + { + attrs[i++] = cur; + cur.starts_at_boundary = false; + } + } + return attrs; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + static const char * formatter_parse_mirc_color (struct formatter *self, const char *s) { @@ -3261,24 +3336,15 @@ formatter_parse_mirc (struct formatter *self, const char *s) str_reset (&buf); } - switch (c) - { - case '\x02': FORMATTER_ADD_SIMPLE (self, BOLD); break; - case '\x11': /* monospace, N/A */ break; - case '\x1d': FORMATTER_ADD_SIMPLE (self, ITALIC); break; - case '\x1e': FORMATTER_ADD_SIMPLE (self, CROSSED_OUT); break; - case '\x1f': FORMATTER_ADD_SIMPLE (self, UNDERLINE); break; - case '\x16': FORMATTER_ADD_SIMPLE (self, INVERSE); break; - - case '\x03': + int attribute = irc_parse_attribute (c); + if (c == '\x03') s = formatter_parse_mirc_color (self, s); - break; - case '\x0f': + else if (attribute > 0) + FORMATTER_ADD_ITEM (self, SIMPLE, .attribute = attribute); + else if (attribute < 0) FORMATTER_ADD_RESET (self); - break; - default: + else str_append_c (&buf, c); - } } if (buf.len) @@ -8240,70 +8306,6 @@ irc_process_message (const struct irc_message *msg, struct server *s) // This is a rather basic algorithm; something like ICU with proper // locale specification would be needed to make it work better. -struct irc_char_attrs -{ - uint8_t fg, bg; ///< {Fore,back}ground colour or 99 - uint8_t attributes; ///< TEXT_* flags, except TEXT_BLINK - uint8_t starts_at_boundary; ///< Possible to split here? -}; - -static void -irc_serialize_char_attrs (const struct irc_char_attrs *attrs, struct str *out) -{ - soft_assert (attrs->fg < 100 && attrs->bg < 100); - - if (attrs->fg != 99 || attrs->bg != 99) - { - str_append_printf (out, "\x03%u", attrs->fg); - if (attrs->bg != 99) - str_append_printf (out, ",%02u", attrs->bg); - } - if (attrs->attributes & TEXT_BOLD) str_append_c (out, '\x02'); - if (attrs->attributes & TEXT_ITALIC) str_append_c (out, '\x1d'); - if (attrs->attributes & TEXT_UNDERLINE) str_append_c (out, '\x1f'); - if (attrs->attributes & TEXT_INVERSE) str_append_c (out, '\x16'); - if (attrs->attributes & TEXT_CROSSED_OUT) str_append_c (out, '\x1e'); - if (attrs->attributes & TEXT_MONOSPACE) str_append_c (out, '\x11'); -} - -// The text needs to be NUL-terminated -// TODO: try to deduplicate analogous code in formatter_parse_mirc() -static struct irc_char_attrs * -irc_analyze_text (const char *text, size_t len) -{ - struct irc_char_attrs *attrs = xcalloc (len, sizeof *attrs), - blank = { .fg = 99, .bg = 99, .starts_at_boundary = true }, - next = blank, cur = next; - - for (size_t i = 0; i != len; cur = next) - { - const char *start = text; - hard_assert (utf8_decode (&text, len - i) >= 0); - switch (*start) - { - case '\x02': next.attributes ^= TEXT_BOLD; break; - case '\x11': next.attributes ^= TEXT_MONOSPACE; break; - case '\x1d': next.attributes ^= TEXT_ITALIC; break; - case '\x1e': next.attributes ^= TEXT_CROSSED_OUT; break; - case '\x1f': next.attributes ^= TEXT_UNDERLINE; break; - case '\x16': next.attributes ^= TEXT_INVERSE; break; - - case '\x03': - text = irc_parse_mirc_color (text, &next.fg, &next.bg); - break; - case '\x0f': - next = blank; - } - - while (start++ != text) - { - attrs[i++] = cur; - cur.starts_at_boundary = false; - } - } - return attrs; -} - static size_t wrap_text_for_single_line (const char *text, struct irc_char_attrs *attrs, size_t text_len, size_t target_len, struct str *output) @@ -8344,8 +8346,6 @@ wrap_text_for_single_line (const char *text, struct irc_char_attrs *attrs, return eaten; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // In practice, this should never fail at all, although it's not guaranteed static bool wrap_message (const char *message,