Created
October 18, 2013 05:10
-
-
Save jgrar/7036760 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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