send: add sendto_one_multiline_* API

Allows simplifying multiline wrapping for multiple usages, like CAP
LS/LIST, NAMES, and WHOIS channel listing
This commit is contained in:
Doug Freed 2020-11-06 19:03:59 +00:00
parent f0356d2a6f
commit 8efff56fdf
2 changed files with 180 additions and 0 deletions

View File

@ -93,6 +93,18 @@ extern void kill_client(struct Client *client_p, struct Client *diedie,
extern void kill_client_serv_butone(struct Client *one, struct Client *source_p,
const char *pattern, ...) AFP(3, 4);
enum multiline_item_result {
MULTILINE_FAILURE,
MULTILINE_SUCCESS,
MULTILINE_WRAPPED,
};
extern bool send_multiline_init(struct Client *target_p, const char *separator, const char *format, ...) AFP(3, 4);
extern bool send_multiline_remote_pad(struct Client *target_p, struct Client *client_p);
extern enum multiline_item_result send_multiline_item(struct Client *target_p, const char *format, ...) AFP(2, 3);
extern bool send_multiline_fini(struct Client *target_p, const char *format, ...) AFP(2, 3);
extern void send_multiline_reset(void);
#define L_ALL 0
#define L_OPER 1
#define L_ADMIN 2

View File

@ -1503,3 +1503,171 @@ kill_client_serv_butone(struct Client *one, struct Client *target_p, const char
rb_linebuf_donebuf(&rb_linebuf_id);
}
static struct Client *multiline_stashed_target_p;
static char multiline_prefix[DATALEN+1]; /* allow for null termination */
static int multiline_prefix_len;
static char multiline_separator[2];
static int multiline_separator_len;
static char *multiline_item_start;
static char *multiline_cur;
static int multiline_cur_len;
static int multiline_remote_pad;
bool
send_multiline_init(struct Client *target_p, const char *separator, const char *format, ...)
{
va_list args;
s_assert(multiline_stashed_target_p == NULL && "Multiline: didn't cleanup after last usage!");
va_start(args, format);
multiline_prefix_len = vsnprintf(multiline_prefix, sizeof multiline_prefix, format, args);
va_end(args);
if (multiline_prefix_len <= 0 || multiline_prefix_len >= DATALEN)
{
s_assert(false && "Multiline: failure preparing prefix!");
return false;
}
multiline_separator_len = rb_strlcpy(multiline_separator, separator, sizeof multiline_separator);
if (multiline_separator_len >= sizeof multiline_separator)
{
s_assert(false && "Multiline: separator too long");
return false;
}
multiline_stashed_target_p = target_p;
multiline_item_start = multiline_prefix + multiline_prefix_len;
multiline_cur = multiline_item_start;
multiline_cur_len = multiline_prefix_len;
multiline_remote_pad = 0;
return true;
}
bool
send_multiline_remote_pad(struct Client *target_p, struct Client *client_p)
{
ssize_t remote_pad;
if (target_p != multiline_stashed_target_p)
{
s_assert(false && "Multiline: missed init call!");
multiline_stashed_target_p = NULL;
return false;
}
if (MyConnect(target_p))
return true;
remote_pad = strlen(client_p->name) - strlen(client_p->id);
if (remote_pad > 0)
{
multiline_remote_pad += remote_pad;
}
return true;
}
enum multiline_item_result
send_multiline_item(struct Client *target_p, const char *format, ...)
{
va_list args;
char item[DATALEN];
int item_len, res;
enum multiline_item_result ret = MULTILINE_SUCCESS;
if (target_p != multiline_stashed_target_p)
{
s_assert(false && "Multiline: missed init call!");
multiline_stashed_target_p = NULL;
return MULTILINE_FAILURE;
}
va_start(args, format);
item_len = vsnprintf(item, sizeof item, format, args);
va_end(args);
if (item_len < 0 || multiline_prefix_len + multiline_remote_pad + item_len > DATALEN)
{
s_assert(false && "Multiline: failure preparing item!");
multiline_stashed_target_p = NULL;
return MULTILINE_FAILURE;
}
if (multiline_cur_len + ((*multiline_item_start != '\0') ? multiline_separator_len : 0) + item_len > DATALEN - multiline_remote_pad)
{
sendto_one(target_p, "%s", multiline_prefix);
*multiline_item_start = '\0';
multiline_cur_len = multiline_prefix_len;
multiline_cur = multiline_item_start;
ret = MULTILINE_WRAPPED;
}
res = snprintf(multiline_cur, sizeof multiline_prefix - multiline_cur_len, "%s%s",
(*multiline_item_start != '\0') ? multiline_separator : "",
item);
if (res < 0)
{
s_assert(false && "Multiline: failure appending item!");
multiline_stashed_target_p = NULL;
return MULTILINE_FAILURE;
}
multiline_cur_len += res;
multiline_cur += res;
return ret;
}
bool
send_multiline_fini(struct Client *target_p, const char *format, ...)
{
va_list args;
char final[DATALEN];
int final_len;
if (target_p != multiline_stashed_target_p)
{
s_assert(false && "Multiline: missed init call!");
multiline_stashed_target_p = NULL;
return false;
}
if (multiline_cur_len == multiline_prefix_len)
{
multiline_stashed_target_p = NULL;
return true;
}
if (format)
{
va_start(args, format);
final_len = vsnprintf(final, sizeof final, format, args);
va_end(args);
if (final_len <= 0 || final_len > multiline_prefix_len)
{
s_assert(false && "Multiline: failure preparing final prefix!");
multiline_stashed_target_p = NULL;
return false;
}
}
else
{
rb_strlcpy(final, multiline_prefix, multiline_prefix_len + 1);
}
sendto_one(target_p, "%s%s", final, multiline_item_start);
multiline_stashed_target_p = NULL;
return true;
}
void
send_multiline_reset(void)
{
multiline_stashed_target_p = NULL;
}