src/irc_line.c
author Tero Marttila <terom@fixme.fi>
Sun, 08 Mar 2009 17:17:37 +0200
changeset 23 542c73d07d3c
parent 17 5001564ac5fc
child 75 ff6272398d2e
permissions -rw-r--r--
add a simple irc_log module (with evsql code) that joins a channel and log_info's PRIVMSGs

#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;
}