src/irc_line.c
author Tero Marttila <terom@fixme.fi>
Thu, 28 May 2009 01:17:36 +0300
branchnew-lib-errors
changeset 219 cefec18b8268
parent 190 69fd25c8484c
permissions -rw-r--r--
some of the lib/transport stuff compiles
#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;
}