xC: refactor parsing of IRC formatting

This commit is contained in:
Přemysl Eric Janouch 2021-08-29 12:06:53 +02:00
parent b082e82b62
commit de7df1f60d
Signed by: p
GPG Key ID: A0420B94F92B9493

166
xC.c
View File

@ -2961,6 +2961,7 @@ attr_printer_apply (struct attr_printer *self,
attr_printer_reset (self); attr_printer_reset (self);
// TEXT_MONOSPACE is unimplemented, for obvious reasons
if (text_attrs) if (text_attrs)
attr_printer_tputs (self, tparm (set_attributes, attr_printer_tputs (self, tparm (set_attributes,
0, // standout 0, // standout
@ -3153,8 +3154,6 @@ formatter_add_item (struct formatter *self, struct formatter_item template_)
FORMATTER_ADD_ITEM ((self), ATTR, .attribute = ATTR_RESET) FORMATTER_ADD_ITEM ((self), ATTR, .attribute = ATTR_RESET)
#define FORMATTER_ADD_TEXT(self, text_) \ #define FORMATTER_ADD_TEXT(self, text_) \
FORMATTER_ADD_ITEM ((self), TEXT, .text = (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; 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 * static const char *
formatter_parse_mirc_color (struct formatter *self, const char *s) 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); str_reset (&buf);
} }
switch (c) int attribute = irc_parse_attribute (c);
{ if (c == '\x03')
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':
s = formatter_parse_mirc_color (self, s); s = formatter_parse_mirc_color (self, s);
break; else if (attribute > 0)
case '\x0f': FORMATTER_ADD_ITEM (self, SIMPLE, .attribute = attribute);
else if (attribute < 0)
FORMATTER_ADD_RESET (self); FORMATTER_ADD_RESET (self);
break; else
default:
str_append_c (&buf, c); str_append_c (&buf, c);
}
} }
if (buf.len) 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 // This is a rather basic algorithm; something like ICU with proper
// locale specification would be needed to make it work better. // 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 static size_t
wrap_text_for_single_line (const char *text, struct irc_char_attrs *attrs, wrap_text_for_single_line (const char *text, struct irc_char_attrs *attrs,
size_t text_len, size_t target_len, struct str *output) 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; return eaten;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// In practice, this should never fail at all, although it's not guaranteed // In practice, this should never fail at all, although it's not guaranteed
static bool static bool
wrap_message (const char *message, wrap_message (const char *message,