Skip to content

Instantly share code, notes, and snippets.

@jgrar
Created October 18, 2013 05:10
Show Gist options
  • Save jgrar/7036760 to your computer and use it in GitHub Desktop.
Save jgrar/7036760 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <get_irc_message.h>
#define INIT_BUFFER_MAX 256
enum IRC_MESSAGE_STATE
{
INIT, NICK, USER, HOST, COMMAND, PARAM, TRAILING
};
static int fillbuffer(struct stream_buffer *stream);
int get_irc_message (struct stream_buffer *stream, struct irc_message *message)
{
int r; /* return value */
enum IRC_MESSAGE_STATE state;
char *base; /* pointer to current message start */
unsigned int p; /* read position indice into base */
unsigned int t; /* token start indice into base */
unsigned int n; /* maximum read indice value */
char c; /* read position character */
size_t i; /* iterator for param array */
state = INIT;
message->nick = message->user = message->host = message->command =
message->trailing = NULL;
message->nparam = 0;
base = stream->pos;
p = 0;
t = 0;
n = stream->end - stream->pos;
for (;; p++) /* state loop */
{
if (p >= n)
{
r = fillbuffer(stream);
if (r == -1)
{
goto END;
}
base = stream->pos;
n += r; /* iffy, but should be right */
}
c = base[p];
if (c == '\n' && base[p - 1] == '\r')
{
base[p - 1] = '\0';
switch (state)
{
case COMMAND:
message->command = t;
break;
case PARAM:
message->param[message->nparam++] = t;
break;
case TRAILING:
message->trailing = t;
break;
default:
/* TODO "parse" error */
break;
}
break; /* state loop */
}
else if (state != TRAILING)
{
switch (state)
{
case INIT:
if (c == ':')
{
state = NICK;
t++;
}
else
{
state = COMMAND;
}
break;
case NICK:
if (c == '!')
{
message->nick = t;
base[p] = '\0';
t = p + 1;
state = USER;
}
else if (c == '@')
{
message->nick = t; /* confirm */
base[p] = '\0';
t = p + 1;
state = HOST;
}
else if (c == ' ')
{
message->host = t; /* confirm */
base[p] = '\0';
t = p + 1;
state = COMMAND;
}
else if (c == '.')
{
state = HOST;
}
break;
case USER:
if (c == '@')
{
message->user = t;
base[p] = '\0';
t = p + 1;
state = HOST;
}
break;
case HOST:
if (c == ' ')
{
message->host = t;
base[p] = '\0';
t = p + 1;
state = COMMAND;
}
break;
case COMMAND:
if (c == ' ')
{
message->command = t;
base[p] = '\0';
t = p + 1;
state = PARAM;
}
break;
case PARAM:
if (c == ' ')
{
if (message->nparam < IRC_MESSAGE_PARAM_MAX)
{
message->param[message->nparam++] = t;
base[p] = '\0';
t = p + 1;
}
}
/* colon must occur after a token (marked by the nul) */
else if (c == ':' && base[p - 1] == '\0')
{
state = TRAILING;
t = p + 1;
}
break;
}
}
}
if (message->nick) message->nick = base + (unsigned long) message->nick;
if (message->user) message->user = base + (unsigned long) message->user;
if (message->host) message->host = base + (unsigned long) message->host;
message->command = base + (unsigned long) message->command;
if (message->trailing) message->trailing = base + (unsigned long) message->trailing;
for (i = 0; i < message->nparam; i++)
{
message->param[i] = base + (unsigned long) message->param[i];
}
message->param[message->nparam] = NULL;
stream->pos += p + 1;
r = 0;
END:
return r;
}
static int fillbuffer (struct stream_buffer *stream)
{
ssize_t r;
char *new_base;
size_t old_max;
size_t new_max;
size_t move_offset;
/* the following branch assumes that fillbuffer() has been called
* because stream->pos has reached stream->end */
if (stream->pos == stream->base)
{
if (stream->end == stream->max)
{
/* expand region */
old_max = stream->max - stream->base;
new_max = (old_max > 0) ? old_max * 2 : INIT_BUFFER_MAX;
stream->pos -= (unsigned long) stream->base;
stream->end -= (unsigned long) stream->base;
new_base = realloc(stream->base, new_max);
if (!new_base)
{
return -1;
}
stream->base = new_base;
stream->pos += (unsigned long) stream->base;
stream->end += (unsigned long) stream->base;
stream->max = stream->base + new_max;
}
}
else
{
/* move pos down to base to free up space at the end of the buffer */
move_offset = stream->pos - stream->base;
memmove(stream->base, stream->pos, move_offset);
stream->pos -= move_offset; /* assign straight to base? */
stream->end -= move_offset;
}
#ifdef SSL_ENABLED
if (stream->use_ssl)
{
r = gnutls_record_recv(stream->session, stream->end, stream->max - stream->end);
}
else
{
#endif
r = read(stream->fd, stream->end, stream->max - stream->end);
#ifdef SSL_ENABLED
}
#endif
if (r == -1)
{ /* read error */
}
else if (r == 0)
{ /* closed */
r = -1;
}
else
{
stream->end += r;
}
return r;
}
#ifndef IRC_MESSAGE_H
# define IRC_MESSAGE_H
# ifdef SSL_ENABLED
# include <gnutls/gnutls.h>
# endif
# include <stddef.h>
#define IRC_MESSAGE_PARAM_MAX 15
struct irc_message
{
char *nick;
char *user;
char *host;
char *command;
char *param[IRC_MESSAGE_PARAM_MAX]; /* fixed size parameter array */
size_t nparam; /* amount of parameters in the message */
char *trailing;
};
struct stream_buffer
{
char *base;
char *pos;
char *end;
char *max;
int fd;
int use_ssl; /* ssl enabled stream flag */
# ifdef SSL_ENABLED
gnutls_session_t session;
#endif
/* TODO Flagging error/closed state */
};
int get_irc_message(struct stream_buffer *stream, struct irc_message *message);
#endif /* IRC_MESSAGE_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment