degesch: add output text formatting
This commit is contained in:
parent
4393e48145
commit
864be7cfc5
466
degesch.c
466
degesch.c
|
@ -27,6 +27,11 @@
|
||||||
#define ATTR_WARNING "attr_warning"
|
#define ATTR_WARNING "attr_warning"
|
||||||
#define ATTR_ERROR "attr_error"
|
#define ATTR_ERROR "attr_error"
|
||||||
|
|
||||||
|
#define ATTR_TIMESTAMP "attr_timestamp"
|
||||||
|
#define ATTR_ACTION "attr_action"
|
||||||
|
#define ATTR_JOIN "attr_join"
|
||||||
|
#define ATTR_PART "attr_part"
|
||||||
|
|
||||||
// User data for logger functions to enable formatted logging
|
// User data for logger functions to enable formatted logging
|
||||||
#define print_fatal_data ATTR_ERROR
|
#define print_fatal_data ATTR_ERROR
|
||||||
#define print_error_data ATTR_ERROR
|
#define print_error_data ATTR_ERROR
|
||||||
|
@ -86,6 +91,11 @@ static struct config_item g_config_table[] =
|
||||||
{ ATTR_WARNING, NULL, "Terminal attributes for warnings" },
|
{ ATTR_WARNING, NULL, "Terminal attributes for warnings" },
|
||||||
{ ATTR_ERROR, NULL, "Terminal attributes for errors" },
|
{ ATTR_ERROR, NULL, "Terminal attributes for errors" },
|
||||||
|
|
||||||
|
{ ATTR_TIMESTAMP, NULL, "Terminal attributes for timestamps" },
|
||||||
|
{ ATTR_ACTION, NULL, "Terminal attributes for user actions" },
|
||||||
|
{ ATTR_JOIN, NULL, "Terminal attributes for joins" },
|
||||||
|
{ ATTR_PART, NULL, "Terminal attributes for parts" },
|
||||||
|
|
||||||
{ NULL, NULL, NULL }
|
{ NULL, NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -94,6 +104,12 @@ static struct config_item g_config_table[] =
|
||||||
// All text stored in our data structures is encoded in UTF-8.
|
// All text stored in our data structures is encoded in UTF-8.
|
||||||
// Or at least should be. The exception is IRC identifiers.
|
// Or at least should be. The exception is IRC identifiers.
|
||||||
|
|
||||||
|
static bool
|
||||||
|
isdigit_ascii (int c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
/// Shorthand to set an error and return failure from the function
|
/// Shorthand to set an error and return failure from the function
|
||||||
#define FAIL(...) \
|
#define FAIL(...) \
|
||||||
BLOCK_START \
|
BLOCK_START \
|
||||||
|
@ -575,7 +591,8 @@ static struct
|
||||||
bool stdout_is_tty; ///< `stdout' is a terminal
|
bool stdout_is_tty; ///< `stdout' is a terminal
|
||||||
bool stderr_is_tty; ///< `stderr' is a terminal
|
bool stderr_is_tty; ///< `stderr' is a terminal
|
||||||
|
|
||||||
char *color_set[8]; ///< Codes to set the foreground colour
|
char *color_set_fg[8]; ///< Codes to set the foreground colour
|
||||||
|
char *color_set_bg[8]; ///< Codes to set the background colour
|
||||||
}
|
}
|
||||||
g_terminal;
|
g_terminal;
|
||||||
|
|
||||||
|
@ -593,15 +610,20 @@ init_terminal (void)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Make sure all terminal features used by us are supported
|
// Make sure all terminal features used by us are supported
|
||||||
if (!set_a_foreground || !enter_bold_mode || !exit_attribute_mode)
|
if (!set_a_foreground || !set_a_background
|
||||||
|
|| !enter_bold_mode || !exit_attribute_mode)
|
||||||
{
|
{
|
||||||
del_curterm (cur_term);
|
del_curterm (cur_term);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < N_ELEMENTS (g_terminal.color_set); i++)
|
for (size_t i = 0; i < N_ELEMENTS (g_terminal.color_set_fg); i++)
|
||||||
g_terminal.color_set[i] = xstrdup (tparm (set_a_foreground,
|
{
|
||||||
|
g_terminal.color_set_fg[i] = xstrdup (tparm (set_a_foreground,
|
||||||
i, 0, 0, 0, 0, 0, 0, 0, 0));
|
i, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||||
|
g_terminal.color_set_bg[i] = xstrdup (tparm (set_a_background,
|
||||||
|
i, 0, 0, 0, 0, 0, 0, 0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
return g_terminal.initialized = true;
|
return g_terminal.initialized = true;
|
||||||
}
|
}
|
||||||
|
@ -612,8 +634,11 @@ free_terminal (void)
|
||||||
if (!g_terminal.initialized)
|
if (!g_terminal.initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (size_t i = 0; i < N_ELEMENTS (g_terminal.color_set); i++)
|
for (size_t i = 0; i < N_ELEMENTS (g_terminal.color_set_fg); i++)
|
||||||
free (g_terminal.color_set[i]);
|
{
|
||||||
|
free (g_terminal.color_set_fg[i]);
|
||||||
|
free (g_terminal.color_set_bg[i]);
|
||||||
|
}
|
||||||
del_curterm (cur_term);
|
del_curterm (cur_term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,8 +765,13 @@ init_colors (struct app_context *ctx)
|
||||||
|
|
||||||
INIT_ATTR (ATTR_PROMPT, enter_bold_mode, "\x1b[1m");
|
INIT_ATTR (ATTR_PROMPT, enter_bold_mode, "\x1b[1m");
|
||||||
INIT_ATTR (ATTR_RESET, exit_attribute_mode, "\x1b[0m");
|
INIT_ATTR (ATTR_RESET, exit_attribute_mode, "\x1b[0m");
|
||||||
INIT_ATTR (ATTR_WARNING, g_terminal.color_set[3], "\x1b[33m");
|
INIT_ATTR (ATTR_WARNING, g_terminal.color_set_fg[3], "\x1b[33m");
|
||||||
INIT_ATTR (ATTR_ERROR, g_terminal.color_set[1], "\x1b[31m");
|
INIT_ATTR (ATTR_ERROR, g_terminal.color_set_fg[1], "\x1b[31m");
|
||||||
|
|
||||||
|
INIT_ATTR (ATTR_TIMESTAMP, g_terminal.color_set_fg[7], "\x1b[37m");
|
||||||
|
INIT_ATTR (ATTR_ACTION, g_terminal.color_set_fg[1], "\x1b[31m");
|
||||||
|
INIT_ATTR (ATTR_JOIN, g_terminal.color_set_fg[2], "\x1b[32m");
|
||||||
|
INIT_ATTR (ATTR_PART, g_terminal.color_set_fg[1], "\x1b[31m");
|
||||||
|
|
||||||
#undef INIT_ATTR
|
#undef INIT_ATTR
|
||||||
|
|
||||||
|
@ -829,6 +859,274 @@ setup_signal_handlers (void)
|
||||||
exit_fatal ("sigaction: %s", strerror (errno));
|
exit_fatal ("sigaction: %s", strerror (errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Output formatter --------------------------------------------------------
|
||||||
|
|
||||||
|
// This complicated piece of code makes attributed text formatting simple.
|
||||||
|
// We use a printf-inspired syntax to push attributes and text to the object,
|
||||||
|
// then flush it either to a terminal, or a log file with formatting stripped.
|
||||||
|
//
|
||||||
|
// Format strings use a #-quoted notation, to differentiate from printf:
|
||||||
|
// #s inserts a string
|
||||||
|
// #d inserts a signed integer; also supports the #<N> and #0<N> notation
|
||||||
|
//
|
||||||
|
// #a inserts named attributes (auto-resets)
|
||||||
|
// #r resets terminal attributes
|
||||||
|
// #c sets foreground color
|
||||||
|
// #C sets background color
|
||||||
|
|
||||||
|
enum formatter_item_type
|
||||||
|
{
|
||||||
|
FORMATTER_ITEM_TEXT, ///< Text
|
||||||
|
FORMATTER_ITEM_ATTR, ///< Named formatting attributes
|
||||||
|
FORMATTER_ITEM_FG_COLOR, ///< Foreground color
|
||||||
|
FORMATTER_ITEM_BG_COLOR ///< Background color
|
||||||
|
};
|
||||||
|
|
||||||
|
struct formatter_item
|
||||||
|
{
|
||||||
|
LIST_HEADER (struct formatter_item)
|
||||||
|
|
||||||
|
enum formatter_item_type type; ///< Type of this item
|
||||||
|
int color; ///< Color
|
||||||
|
char *data; ///< Either text or an attribute string
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct formatter_item *
|
||||||
|
formatter_item_new (void)
|
||||||
|
{
|
||||||
|
struct formatter_item *self = xcalloc (1, sizeof *self);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_item_destroy (struct formatter_item *self)
|
||||||
|
{
|
||||||
|
free (self->data);
|
||||||
|
free (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
|
struct formatter
|
||||||
|
{
|
||||||
|
struct app_context *ctx; ///< Application context
|
||||||
|
|
||||||
|
struct formatter_item *items; ///< Items
|
||||||
|
struct formatter_item *items_tail; ///< Tail of items
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_init (struct formatter *self, struct app_context *ctx)
|
||||||
|
{
|
||||||
|
memset (self, 0, sizeof *self);
|
||||||
|
self->ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_free (struct formatter *self)
|
||||||
|
{
|
||||||
|
LIST_FOR_EACH (struct formatter_item, iter, self->items)
|
||||||
|
formatter_item_destroy (iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct formatter_item *
|
||||||
|
formatter_add_blank (struct formatter *self)
|
||||||
|
{
|
||||||
|
struct formatter_item *item = formatter_item_new ();
|
||||||
|
LIST_APPEND_WITH_TAIL (self->items, self->items_tail, item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_add_attr (struct formatter *self, const char *attr_name)
|
||||||
|
{
|
||||||
|
struct formatter_item *item = formatter_add_blank (self);
|
||||||
|
item->type = FORMATTER_ITEM_ATTR;
|
||||||
|
item->data = xstrdup (str_map_find (&self->ctx->config, attr_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_add_reset (struct formatter *self)
|
||||||
|
{
|
||||||
|
struct formatter_item *item = formatter_add_blank (self);
|
||||||
|
item->type = FORMATTER_ITEM_ATTR;
|
||||||
|
item->data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_add_text (struct formatter *self, const char *text)
|
||||||
|
{
|
||||||
|
struct formatter_item *item = formatter_add_blank (self);
|
||||||
|
item->type = FORMATTER_ITEM_TEXT;
|
||||||
|
item->data = xstrdup (text);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_add_fg_color (struct formatter *self, int color)
|
||||||
|
{
|
||||||
|
struct formatter_item *item = formatter_add_blank (self);
|
||||||
|
item->type = FORMATTER_ITEM_FG_COLOR;
|
||||||
|
item->color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_add_bg_color (struct formatter *self, int color)
|
||||||
|
{
|
||||||
|
struct formatter_item *item = formatter_add_blank (self);
|
||||||
|
item->type = FORMATTER_ITEM_BG_COLOR;
|
||||||
|
item->color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
formatter_parse_field (struct formatter *self,
|
||||||
|
const char *field, struct str *buf, va_list *ap)
|
||||||
|
{
|
||||||
|
size_t width = 0;
|
||||||
|
bool zero_padded = false;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
restart:
|
||||||
|
switch ((c = *field++))
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
// We can push boring text content to the caller's buffer
|
||||||
|
// and let it flush the buffer only when it's actually needed
|
||||||
|
case 's':
|
||||||
|
s = va_arg (*ap, char *);
|
||||||
|
for (size_t len = strlen (s); len < width; len++)
|
||||||
|
str_append_c (buf, ' ');
|
||||||
|
str_append (buf, s);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
s = xstrdup_printf ("%d", va_arg (*ap, int));
|
||||||
|
for (size_t len = strlen (s); len < width; len++)
|
||||||
|
str_append_c (buf, " 0"[zero_padded]);
|
||||||
|
str_append (buf, s);
|
||||||
|
free (s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
formatter_add_attr (self, va_arg (*ap, const char *));
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
formatter_add_fg_color (self, va_arg (*ap, int));
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
formatter_add_bg_color (self, va_arg (*ap, int));
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
formatter_add_reset (self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (c == '0' && !zero_padded)
|
||||||
|
zero_padded = true;
|
||||||
|
else if (isdigit_ascii (c))
|
||||||
|
width = width * 10 + (c - '0');
|
||||||
|
else if (c)
|
||||||
|
hard_assert (!"unexpected format specifier");
|
||||||
|
else
|
||||||
|
hard_assert (!"unexpected end of format string");
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_add (struct formatter *self, const char *format, ...)
|
||||||
|
{
|
||||||
|
struct str buf;
|
||||||
|
str_init (&buf);
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start (ap, format);
|
||||||
|
|
||||||
|
while (*format)
|
||||||
|
{
|
||||||
|
if (*format != '#' || *++format == '#')
|
||||||
|
{
|
||||||
|
str_append_c (&buf, *format++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (buf.len)
|
||||||
|
{
|
||||||
|
formatter_add_text (self, buf.str);
|
||||||
|
str_reset (&buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
format = formatter_parse_field (self, format, &buf, &ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf.len)
|
||||||
|
formatter_add_text (self, buf.str);
|
||||||
|
|
||||||
|
str_free (&buf);
|
||||||
|
va_end (ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
formatter_flush (struct formatter *self, FILE *stream)
|
||||||
|
{
|
||||||
|
terminal_printer_fn printer = get_attribute_printer (stream);
|
||||||
|
|
||||||
|
const char *attr_reset = str_map_find (&self->ctx->config, ATTR_RESET);
|
||||||
|
if (printer)
|
||||||
|
tputs (attr_reset, 1, printer);
|
||||||
|
|
||||||
|
bool is_attributed = false;
|
||||||
|
bool is_tty = isatty (fileno (stream));
|
||||||
|
LIST_FOR_EACH (struct formatter_item, iter, self->items)
|
||||||
|
{
|
||||||
|
switch (iter->type)
|
||||||
|
{
|
||||||
|
case FORMATTER_ITEM_TEXT:
|
||||||
|
if (is_tty)
|
||||||
|
{
|
||||||
|
char *term = iconv_xstrdup
|
||||||
|
(self->ctx->term_from_utf8, iter->data, -1, NULL);
|
||||||
|
fputs (term, stream);
|
||||||
|
free (term);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fputs (iter->data, stream);
|
||||||
|
break;
|
||||||
|
case FORMATTER_ITEM_ATTR:
|
||||||
|
if (!printer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (is_attributed)
|
||||||
|
{
|
||||||
|
tputs (attr_reset, 1, printer);
|
||||||
|
is_attributed = false;
|
||||||
|
}
|
||||||
|
if (iter->data)
|
||||||
|
{
|
||||||
|
tputs (iter->data, 1, printer);
|
||||||
|
is_attributed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FORMATTER_ITEM_FG_COLOR:
|
||||||
|
if (!printer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tputs (g_terminal.color_set_fg[iter->color], 1, printer);
|
||||||
|
is_attributed = true;
|
||||||
|
break;
|
||||||
|
case FORMATTER_ITEM_BG_COLOR:
|
||||||
|
if (!printer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tputs (g_terminal.color_set_bg[iter->color], 1, printer);
|
||||||
|
is_attributed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_attributed)
|
||||||
|
tputs (attr_reset, 1, printer);
|
||||||
|
}
|
||||||
|
|
||||||
// --- Buffers -----------------------------------------------------------------
|
// --- Buffers -----------------------------------------------------------------
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -856,117 +1154,117 @@ buffer_update_time (struct app_context *ctx, time_t now)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
buffer_line_display (struct app_context *ctx, struct buffer_line *line)
|
buffer_line_display (struct app_context *ctx,
|
||||||
|
struct buffer_line *line, bool is_external)
|
||||||
{
|
{
|
||||||
// Normal timestamps don't include the date, this way the user won't be
|
// Normal timestamps don't include the date, this way the user won't be
|
||||||
// confused as to when an event has happened
|
// confused as to when an event has happened
|
||||||
buffer_update_time (ctx, line->when);
|
buffer_update_time (ctx, line->when);
|
||||||
|
|
||||||
struct str output;
|
struct buffer_line_args *a = &line->args;
|
||||||
str_init (&output);
|
|
||||||
|
char *nick = NULL;
|
||||||
|
const char *userhost = NULL;
|
||||||
|
int nick_color = -1;
|
||||||
|
int object_color = -1;
|
||||||
|
|
||||||
|
if (a->who)
|
||||||
|
{
|
||||||
|
nick = irc_cut_nickname (a->who);
|
||||||
|
userhost = irc_find_userhost (a->who);
|
||||||
|
nick_color = str_map_hash (nick, strlen (nick)) % 8;
|
||||||
|
}
|
||||||
|
if (a->object)
|
||||||
|
object_color = str_map_hash (a->object, strlen (a->object)) % 8;
|
||||||
|
|
||||||
|
struct formatter f;
|
||||||
|
formatter_init (&f, ctx);
|
||||||
|
|
||||||
struct tm current;
|
struct tm current;
|
||||||
if (!localtime_r (&line->when, ¤t))
|
if (!localtime_r (&line->when, ¤t))
|
||||||
print_error ("%s: %s", "localtime_r", strerror (errno));
|
print_error ("%s: %s", "localtime_r", strerror (errno));
|
||||||
else
|
else
|
||||||
str_append_printf (&output, "%02d:%02d:%02d ",
|
formatter_add (&f, "#a#02d:#02d:#02d#r ",
|
||||||
current.tm_hour, current.tm_min, current.tm_sec);
|
ATTR_TIMESTAMP, current.tm_hour, current.tm_min, current.tm_sec);
|
||||||
|
|
||||||
#define GET_FIELD(name) char *name = line->args.name \
|
// TODO: when this comes from a different buffer (is_external),
|
||||||
? iconv_xstrdup (ctx->term_from_utf8, line->args.name, -1, NULL) : NULL
|
// ignore all attributes and instead print it with ATTR_OTHER
|
||||||
|
|
||||||
GET_FIELD (who);
|
// TODO: try to decode as much as possible using mIRC formatting;
|
||||||
GET_FIELD (object);
|
// could either add a #m format specifier, or write a separate function
|
||||||
GET_FIELD (text);
|
// to translate the formatting into formatter API calls
|
||||||
GET_FIELD (reason);
|
|
||||||
|
|
||||||
#undef GET_FIELD
|
|
||||||
|
|
||||||
// TODO: colorize the output, note that we shouldn't put everything through
|
|
||||||
// tputs but only the attribute strings. That might prove a bit
|
|
||||||
// challenging. Maybe we could create a helper object to pust text
|
|
||||||
// and formatting into. We could have a varargs function to make it a bit
|
|
||||||
// more friendly, e.g. push(&x, ATTR_JOIN, "--> ", ATTR_RESET, who, NULL)
|
|
||||||
|
|
||||||
char *nick = NULL;
|
|
||||||
const char *userhost = NULL;
|
|
||||||
|
|
||||||
if (who)
|
|
||||||
{
|
|
||||||
nick = irc_cut_nickname (who);
|
|
||||||
userhost = irc_find_userhost (who);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (line->type)
|
switch (line->type)
|
||||||
{
|
{
|
||||||
case BUFFER_LINE_PRIVMSG:
|
case BUFFER_LINE_PRIVMSG:
|
||||||
str_append_printf (&output, "<%s> %s", nick, text);
|
formatter_add (&f, "<#c#s#r> #s", nick_color, nick, a->text);
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_ACTION:
|
case BUFFER_LINE_ACTION:
|
||||||
str_append_printf (&output, " * %s %s", nick, text);
|
formatter_add (&f, " #a*#r ", ATTR_ACTION);
|
||||||
|
formatter_add (&f, "#c#s#r #s", nick_color, nick, a->text);
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_NOTICE:
|
case BUFFER_LINE_NOTICE:
|
||||||
str_append_printf (&output, " - Notice(%s): %s", nick, text);
|
formatter_add (&f, " - ");
|
||||||
|
formatter_add (&f, "#s(#c#s#r): #s",
|
||||||
|
"Notice", nick_color, nick, a->text);
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_JOIN:
|
case BUFFER_LINE_JOIN:
|
||||||
if (who)
|
formatter_add (&f, "#a-->#r ", ATTR_JOIN);
|
||||||
str_append_printf (&output, "--> %s (%s) has joined %s",
|
formatter_add (&f, "#c#s#r (#s) #a#s#r #s",
|
||||||
nick, userhost, object);
|
nick_color, nick, userhost,
|
||||||
else
|
ATTR_JOIN, "has joined", a->object);
|
||||||
str_append_printf (&output, "--> You have joined %s", object);
|
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_PART:
|
case BUFFER_LINE_PART:
|
||||||
if (who)
|
formatter_add (&f, "#a<--#r ", ATTR_PART);
|
||||||
str_append_printf (&output, "<-- %s (%s) has left %s (%s)",
|
formatter_add (&f, "#c#s#r (#s) #a#s#r #s",
|
||||||
nick, userhost, object, reason);
|
nick_color, nick, userhost,
|
||||||
else
|
ATTR_PART, "has left", a->object);
|
||||||
str_append_printf (&output, "<-- You have left %s (%s)",
|
if (a->reason)
|
||||||
object, reason);
|
formatter_add (&f, " (#s)", a->reason);
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_KICK:
|
case BUFFER_LINE_KICK:
|
||||||
if (who)
|
formatter_add (&f, "#a<--#r ", ATTR_PART);
|
||||||
str_append_printf (&output, "<-- %s has kicked %s (%s)",
|
formatter_add (&f, "#c#s#r (#s) #a#s#r #c#s#r",
|
||||||
nick, object, reason);
|
nick_color, nick, userhost,
|
||||||
else
|
ATTR_PART, "has kicked", object_color, a->object);
|
||||||
str_append_printf (&output, "<-- You have kicked %s (%s)",
|
if (a->reason)
|
||||||
object, reason);
|
formatter_add (&f, " (#s)", a->reason);
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_NICK:
|
case BUFFER_LINE_NICK:
|
||||||
if (who)
|
formatter_add (&f, " - ");
|
||||||
str_append_printf (&output, " - %s is now known as %s",
|
if (a->who)
|
||||||
nick, object);
|
formatter_add (&f, "#c#s#r #s #c#s#r",
|
||||||
|
nick_color, nick,
|
||||||
|
"is now known as", object_color, a->object);
|
||||||
else
|
else
|
||||||
str_append_printf (&output, " - You are now known as %s", object);
|
formatter_add (&f, "#s #s",
|
||||||
|
"You are now known as", a->object);
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_TOPIC:
|
case BUFFER_LINE_TOPIC:
|
||||||
if (who)
|
formatter_add (&f, " - ");
|
||||||
str_append_printf (&output,
|
formatter_add (&f, "#c#s#r #s \"#s\"",
|
||||||
" - %s has changed the topic to: %s", nick, text);
|
nick_color, nick,
|
||||||
else
|
"has changed the topic to", a->text);
|
||||||
str_append_printf (&output,
|
|
||||||
" - You have changed the topic to: %s", text);
|
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_QUIT:
|
case BUFFER_LINE_QUIT:
|
||||||
if (who)
|
formatter_add (&f, "#a<--#r ", ATTR_PART);
|
||||||
str_append_printf (&output, "<-- %s (%s) has quit (%s)",
|
formatter_add (&f, "#c#s#r (%s) #a#s#r",
|
||||||
nick, userhost, reason);
|
nick_color, nick, userhost,
|
||||||
else
|
ATTR_PART, "has quit");
|
||||||
str_append_printf (&output, "<-- You have quit (%s)", reason);
|
if (a->reason)
|
||||||
|
formatter_add (&f, " (#s)", a->reason);
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_STATUS:
|
case BUFFER_LINE_STATUS:
|
||||||
str_append_printf (&output, " - %s", text);
|
formatter_add (&f, " - ");
|
||||||
|
formatter_add (&f, "#s", a->text);
|
||||||
break;
|
break;
|
||||||
case BUFFER_LINE_ERROR:
|
case BUFFER_LINE_ERROR:
|
||||||
str_append_printf (&output, "=!= %s", text);
|
formatter_add (&f, "#a=!=#r ", ATTR_ERROR);
|
||||||
|
formatter_add (&f, "#s", a->text);
|
||||||
}
|
}
|
||||||
|
|
||||||
free (nick);
|
free (nick);
|
||||||
|
|
||||||
free (who);
|
|
||||||
free (object);
|
|
||||||
free (text);
|
|
||||||
free (reason);
|
|
||||||
|
|
||||||
struct app_readline_state state;
|
struct app_readline_state state;
|
||||||
if (ctx->readline_prompt_shown)
|
if (ctx->readline_prompt_shown)
|
||||||
app_readline_hide (&state);
|
app_readline_hide (&state);
|
||||||
|
@ -974,8 +1272,9 @@ buffer_line_display (struct app_context *ctx, struct buffer_line *line)
|
||||||
// TODO: write the line to a log file; note that the global and server
|
// TODO: write the line to a log file; note that the global and server
|
||||||
// buffers musn't collide with filenames
|
// buffers musn't collide with filenames
|
||||||
|
|
||||||
printf ("%s\n", output.str);
|
formatter_add (&f, "\n");
|
||||||
str_free (&output);
|
formatter_flush (&f, stdout);
|
||||||
|
formatter_free (&f);
|
||||||
|
|
||||||
if (ctx->readline_prompt_shown)
|
if (ctx->readline_prompt_shown)
|
||||||
app_readline_restore (&state, ctx->readline_prompt);
|
app_readline_restore (&state, ctx->readline_prompt);
|
||||||
|
@ -996,11 +1295,10 @@ buffer_send_internal (struct app_context *ctx, struct buffer *buffer,
|
||||||
buffer->lines_count++;
|
buffer->lines_count++;
|
||||||
|
|
||||||
if (buffer == ctx->current_buffer)
|
if (buffer == ctx->current_buffer)
|
||||||
buffer_line_display (ctx, line);
|
buffer_line_display (ctx, line, false);
|
||||||
else if (!ctx->isolate_buffers &&
|
else if (!ctx->isolate_buffers &&
|
||||||
(buffer == ctx->global_buffer || buffer == ctx->server_buffer))
|
(buffer == ctx->global_buffer || buffer == ctx->server_buffer))
|
||||||
// TODO: show this in another color or something
|
buffer_line_display (ctx, line, true);
|
||||||
buffer_line_display (ctx, line);
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffer->unseen_messages_count++;
|
buffer->unseen_messages_count++;
|
||||||
|
@ -1100,7 +1398,7 @@ buffer_activate (struct app_context *ctx, struct buffer *buffer)
|
||||||
|
|
||||||
// Once we've found where we want to start with the backlog, print it
|
// Once we've found where we want to start with the backlog, print it
|
||||||
for (; line; line = line->next)
|
for (; line; line = line->next)
|
||||||
buffer_line_display (ctx, line);
|
buffer_line_display (ctx, line, false);
|
||||||
buffer->unseen_messages_count = 0;
|
buffer->unseen_messages_count = 0;
|
||||||
|
|
||||||
// The following part shows you why it's not a good idea to use
|
// The following part shows you why it's not a good idea to use
|
||||||
|
|
Loading…
Reference in New Issue