#include "irc_line.h"
#include "log.h"
#include <string.h>
#include <assert.h>
err_t irc_line_parse (struct irc_line *line, struct irc_nm *nm, char *data)
{
int i;
err_t err;
// line would start with a prefix
if (data && *data == ':') {
// consume the prefix as token
char *prefix = strsep(&data, " ") + 1;
// parse as a nickmask
if ((err = irc_nm_parse_buf(nm, prefix))) {
line->source = NULL;
log_warn("invalid prefix: '%s': %s", prefix, error_name(err));
} else {
// valid, store
line->source = nm;
}
} else {
// no prefix
line->source = NULL;
}
// command
line->command = strsep(&data, " ");
// args
for (i = 0; i < IRC_ARG_MAX; i++) {
// trailing?
if (data && *data == ':') {
// consume the rest of the line
line->args[i] = data + 1;
data = NULL;
} else {
// either NULL or a normal token
line->args[i] = strsep(&data, " ");
}
}
// parse errors? What are those?
return SUCCESS;
}
/*
* Flags for output_token
*/
enum output_token_flag {
/* No space before token */
TOK_NOSPACE = 0x01,
/* Prefix with :, may contain spaces, last token */
TOK_TRAILING = 0x02,
};
static err_t output_token (char *buf, size_t *offset, const char *token, int flags)
{
size_t token_len = strlen(token), len;
char *dest = buf + *offset;
// overall length
len = token_len + (flags & TOK_NOSPACE ? 0 : 1) + (flags & TOK_TRAILING ? 1 : 0);
// check overflow
if (*offset + len + 1 > IRC_LINE_MAX)
return ERR_LINE_TOO_LONG;
// check invalid tokens
if (strpbrk(token, (flags & TOK_TRAILING) ? IRC_TOKEN_TRAILING_INVALID : IRC_TOKEN_INVALID))
return ERR_LINE_INVALID_TOKEN;
// delimit with space?
if (!(flags & TOK_NOSPACE))
*dest++ = ' ';
// prefix trailing?
if (flags & TOK_TRAILING)
*dest++= ':';
// copy to buffer
memcpy(dest, token, token_len);
// update offset
*offset += len;
// ok
return 0;
}
err_t irc_line_build (const struct irc_line *line, char *buf)
{
size_t off = 0;
int i;
err_t err;
// XXX: no need for prefix on client
assert(line->source == NULL);
// command
if ((err = output_token(buf, &off, line->command, TOK_NOSPACE)))
return err;
// args
for (i = 0; i < IRC_ARG_MAX; i++) {
// skip unused args at end
if (line->args[i] == NULL)
break;
// if last and contains a space, format as a trailing param
if ((i < IRC_ARG_MAX - 1) && line->args[i + 1] == NULL && strchr(line->args[i], ' ')) {
// output using TOK_TRAILING
if ((err = output_token(buf, &off, line->args[i], TOK_TRAILING)))
return err;
} else {
// output as normal token
if ((err = output_token(buf, &off, line->args[i], 0)))
return err;
}
}
// terminate, output_token reserved the space for this
buf[off] = '\0';
// done
return SUCCESS;
}