#include "irc_line.h"
#include <string.h>
#include <assert.h>
err_t irc_line_parse (struct irc_line *line, char *data)
{
int i;
// prefix?
if (data && *data == ':') {
// consume as token
line->prefix = strsep(&data, " ") + 1;
}
// 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;
}
/*
* Tokens for output_token
*/
enum {
/* 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->prefix == 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;
}