M commands.c => commands.c +44 -21
@@ 19,6 19,7 @@
*/
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
@@ 29,7 30,8 @@
#define DELIM " "
static int
-add_send_code(SendCode snd_code, char *buf, char *rem, int *len)
+add_send_code(SendCode snd_code, char *buf, char *rem, int *len,
+ Message *pending)
{
if ( buf < rem ) {
/* Overwrite `buf` with remaining message content thereby
@@ 47,11 49,15 @@ add_send_code(SendCode snd_code, char *buf, char *rem, int *len)
buf[0] = (char) snd_code;
(*len)++;
+ memcpy(pending->content, buf + 1, *len - 1);
+ pending->content[*len - 1] = '\0';
+
return *len;
}
static int
-set_nick(char *nick, char *buf, char *rem, int *len, syntax_error_t *why)
+set_nick(const char *nick, char *buf, char *rem, int *len, syntax_error_t *why,
+ Message *pending)
{
char new_nick[NICKLEN];
@@ 63,24 69,19 @@ set_nick(char *nick, char *buf, char *rem, int *len, syntax_error_t *why)
return -1;
}
+ /* Set the new nick for the client */
+ strcpy(pending->new_nick, new_nick);
+
/*
* Depending on whether current nick exists, decide if it's a join
* or a nick change.
*/
- *buf = *nick ? SND_SETNICK : SND_JOIN;
+ *buf = pending->type = *nick ? RES_SETNICK : RES_JOIN;
/* Modify rest of `buf` for the announcement of join */
strcpy(buf + 1, new_nick);
-
*len = strlen(new_nick) + 1; /* + 1 for the send code */
- /*
- * Set the new nick for the client
- * TODO: Defer doing this until we hear a confirmation from the
- * server
- */
- strcpy(nick, new_nick);
-
return *len;
}
@@ 89,7 90,7 @@ desc_se(syntax_error_t err_code, char *desc)
{
switch ( err_code ) {
case REQ_ARG:
- strcpy(desc, "No argument specified!!");
+ strcpy(desc, "No arguments specified!!");
break;
case EXTRA_ARGS:
@@ 108,20 109,30 @@ desc_se(syntax_error_t err_code, char *desc)
}
int
-parse_command(char *nick, char *buf, int *len, syntax_error_t *why, char *help)
+parse_command(char *nick, char *buf, int *len, Message **pending,
+ syntax_error_t *why, char *help)
{
char *rem, *command;
/*
* Transforms a user typed message / command
- * into something the server can understand
+ * into something the server can understand.
*/
*help = '\0';
- if ( buf[0] != '/' )
+ if ( pending ) {
+ *pending = calloc(1, sizeof(Message));
+ strcpy((*pending)->nick, nick);
+ }
+
+ if ( buf[0] != '/' ) {
/* We've got a normal message */
- return add_send_code(SND_NORMAL, buf, buf, len);
+ if ( pending )
+ (*pending)->type = RES_NORMAL;
+
+ return add_send_code(SND_NORMAL, buf, buf, len, *pending);
+ }
if ( buf[1] == '/' ) {
/*
@@ 129,7 140,12 @@ parse_command(char *nick, char *buf, int *len, syntax_error_t *why, char *help)
* and treated as a normal message. We can achieve that by
* just replacing the starting '/' with the send code
*/
- buf[0] = (char) SND_NORMAL;
+ *buf = (char) SND_NORMAL;
+ if ( pending ) {
+ (*pending)->type = RES_NORMAL;
+ memcpy((*pending)->content, buf + 1, *len - 1);
+ (*pending)->content[*len - 1] = '\0';
+ }
return *len;
}
@@ 152,7 168,7 @@ parse_command(char *nick, char *buf, int *len, syntax_error_t *why, char *help)
return -1;
}
- return set_nick(nick, buf, rem, len, why);
+ return set_nick(nick, buf, rem, len, why, *pending);
}
if ( !strcmp(command, "/me") ) {
@@ 165,11 181,18 @@ parse_command(char *nick, char *buf, int *len, syntax_error_t *why, char *help)
return -1;
}
- return add_send_code(SND_ACTION, buf, rem, len);
+ if ( pending )
+ (*pending)->type = RES_ACTION;
+
+ return add_send_code(SND_ACTION, buf, rem, len, *pending);
}
- if ( !strcmp(command, "/quit") )
- return add_send_code(SND_QUIT, buf, rem, len);
+ if ( !strcmp(command, "/quit") ) {
+ if ( pending )
+ (*pending)->type = RES_QUIT;
+
+ return add_send_code(SND_QUIT, buf, rem, len, *pending);
+ }
if ( why ) *why = BAD_CMD;
return -1;
M commands.h => commands.h +4 -2
@@ 1,6 1,8 @@
#ifndef _COMMANDS_H
# define _COMMANDS_H
+# include "message.h"
+
/* TODO: We need command syntax errors */
typedef enum {
REQ_ARG=1,
@@ 10,8 12,8 @@ typedef enum {
char *desc_se(syntax_error_t, char *desc);
-int parse_command(char *nick, char *buf, int *len, syntax_error_t *,
- char *help);
+int parse_command(char *nick, char *buf, int *len, Message **pending,
+ syntax_error_t *, char *help);
#endif /* _COMMANDS_H */
M common.h => common.h +21 -12
@@ 25,21 25,30 @@
# define PORT "4200"
+/* TODO: Can we get rid of SND_JOIN and RES_JOIN ?? */
+
typedef enum {
- SND_NORMAL=0, /* A normal message */
- SND_JOIN, /* Join */
- SND_SETNICK, /* Change nick */
- SND_ACTION, /* Perform an action */
- SND_QUIT /* Quit */
+ SND_NORMAL=0, /* A normal message */
+ SND_JOIN, /* Join */
+ SND_SETNICK, /* Change nick */
+ SND_ACTION, /* Perform an action */
+ SND_QUIT, /* Quit */
+ SND_KEY /* Sending session key to server */
} SendCode;
-typedef enum _ResponseCode {
- RES_NORMAL=0, /* Normal group messages */
- RES_JOIN, /* User joins */
- RES_SETNICK, /* Nick changes */
- RES_ACTION, /* Actions */
- RES_QUIT, /* User quits or disconnects */
- RES_ERROR /* User did something wrong */
+/*
+ * TODO: We only need RES_ERROR and RES_CONFIRM in ResponseCode;
+ * everything else are redundant
+ */
+
+typedef enum {
+ RES_NORMAL=0, /* Normal group messages */
+ RES_JOIN, /* User joins */
+ RES_SETNICK, /* Nick changes */
+ RES_ACTION, /* Actions */
+ RES_QUIT, /* User quits or disconnects */
+ RES_ERROR, /* User did something wrong */
+ RES_CONFIRM /* Server announces to all */
} ResponseCode;
typedef enum {
M main.c => main.c +65 -55
@@ 20,6 20,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
@@ 361,7 362,7 @@ int main(int argc, char **argv)
char buf[BUFLEN], desc[256], help[256], nick[NICKLEN]="";
pref_t prefs;
syntax_error_t why;
- Message msg;
+ Message msg, *pending=NULL;
/*
* We'll have two `pfds`, one for `stdin` and another for the
@@ 409,34 410,33 @@ int main(int argc, char **argv)
break;
}
- if ( !num_events ) {
- /*
- * WARNING : COLD PATH !!
- * This can't ever happen since timeout is infinte.
- */
- fprintf(stderr, "`poll()` timed out.\n");
- continue; /* Go back to `poll()`ing */
- }
+ /*
+ * If num_events == 0, this means that `poll()` has timed out.
+ * This can't ever happen since timeout is infinte.
+ */
+ assert(num_events);
/* Now we want to know who is ready to read */
if ( pfds[0].revents & POLLIN ) {
/* `stdin` is ready to read. */
+ int items;
/* `scanf()` should now return immediately. */
- if ( scanf("%[^\n]", buf) == EOF ) {
+ if ( (items = scanf("%[^\n]", buf)) == EOF ) {
printf("EOF\n");
break;
}
getchar(); /* Eat trailing LF ('\n') character */
- numbytes = strlen(buf);
+ numbytes = items ? strlen(buf) : 0;
buf[numbytes] = '\0';
/*
* WARNING: This funtion mutates contents of `nick`, `buf`
* and `numbytes`.
*/
- if ( parse_command(nick, buf, &numbytes, &why, help) == -1 ) {
+ if ( parse_command(nick, buf, &numbytes, &pending,
+ &why, help) == -1 ) {
printf("\x1b[A\x1b[2K\r"); /* Move up and clear line */
fflush(stdout);
@@ 444,7 444,7 @@ int main(int argc, char **argv)
fprintf(stderr, "[x] Syntax Error: `%s`: %s\n",
buf, desc_se(why, desc));
- if ( *help ) fprintf(stderr, "[?] %s\n", help);
+ if ( *help ) printf("[?] %s\n", help);
continue;
}
@@ 481,55 481,65 @@ int main(int argc, char **argv)
break;
}
- parse_response(buf, numbytes, &msg);
+ parse_response(buf, numbytes, nick, &msg, &pending);
+
+ if ( pending ) continue;
if ( overwrite ) {
+ overwrite = 0;
+
/* Alter above line with server's echo */
printf("\x1b[A\x1b[2K\r"); /* Move up and clear line */
- overwrite = 0;
+
+ /*
+ * We need to explicitly flush stdout for the overwrite
+ * to take effect. Since it doesn't end with a line
+ * feed ('\n') character, it doesn't get flushed
+ * automatically.
+ */
+ fflush(stdout);
}
switch( msg.type ) {
- case RES_NORMAL:
- /* Decorate nick and print */
- printf("<%s> %s\n", msg.nick, msg.content);
- break;
-
- case RES_JOIN:
- /* Announce join */
- printf("[!] %s has joined\n", msg.nick);
- break;
-
- case RES_SETNICK:
- /* Announce nick change */
- printf("[!] %s is now known as %s\n",
- msg.nick, msg.new_nick);
- break;
-
- case RES_ACTION:
- printf(" * %s %s\n", msg.nick, msg.content);
- break;
-
- case RES_QUIT:
- printf("[!] %s has quit [%s]\n", msg.nick, msg.content);
- break;
-
- case RES_ERROR:
-
- /* Ignore these errors */
- if ( msg.err_code != EMPTY_NICK &&
- msg.err_code != NO_NICK_CHANGE ) {
-
- char desc[128];
- describe_error(msg.err_code, desc);
- printf("[x] %s\n", desc);
-
- } else {
- /* Yes, it wasn't flushing and the overwrite
- * wasn't taking effect with GNU libc 2.33 and
- * with musl libc 1.2.2 */
- fflush(stdout);
- }
+ case RES_NORMAL:
+ /* Decorate nick and print */
+ printf("<%s> %s\n", msg.nick, msg.content);
+ break;
+
+ case RES_JOIN:
+ /* Announce join */
+ printf("[!] %s has joined\n", msg.new_nick);
+ break;
+
+ case RES_SETNICK:
+ /* Announce nick change */
+ printf("[!] %s is now known as %s\n",
+ msg.nick, msg.new_nick);
+ break;
+
+ case RES_ACTION:
+ printf(" * %s %s\n", msg.nick, msg.content);
+ break;
+
+ case RES_QUIT:
+ printf("[!] %s has quit [%s]\n", msg.nick, msg.content);
+ break;
+
+ case RES_ERROR:
+ /* Ignore these errors */
+ if ( msg.err_code != EMPTY_NICK &&
+ msg.err_code != NO_NICK_CHANGE ) {
+
+ char desc[128];
+ describe_error(msg.err_code, desc);
+ printf("[x] %s\n", desc);
+ }
+ break;
+
+ case RES_CONFIRM:
+ fprintf(stderr, " ERR : %s:%i:"
+ " Unreachable code has been reached!",
+ __FILE__, __LINE__ - 2);
}
}
}
M message.c => message.c +66 -45
@@ 19,6 19,8 @@
*/
#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
#include <string.h>
#include <ctype.h>
@@ 62,61 64,80 @@ int describe_error(ResponseError err_code, char *description)
return -1;
}
-int parse_response(const char *buf, int len, Message *msg)
+int
+parse_response(const char *buf, int len, char *nick, Message *msg,
+ Message **pending)
{
/* The first byte of `buf` is the response code */
- msg->type = (ResponseCode) buf[0];
+ msg->type = *buf;
/* Skip the response code */
buf++;
len--;
- /* Initialize error code */
msg->err_code = 0;
/* The response code tells us what kind of message to expect */
- switch (msg->type) {
- case RES_NORMAL:
- case RES_ACTION:
- case RES_QUIT:
- /* buf = "<nick> <content>" */
-
- /* Extract the first word as nick */
- sscanf(buf, "%s", msg->nick);
- buf += strlen(msg->nick);
- len -= strlen(msg->nick);
-
- /* Get `buf` to the first byte of content */
- while ( len && buf[0] != '\0' && isspace(buf[0]) ) {
- buf++;
- len--;
- }
-
- strncpy(msg->content, buf, len);
- msg->content[len] = '\0';
- break;
-
- case RES_JOIN:
- /* buf = "<nick>" */
-
- /* Extract the first word as nick */
- sscanf(buf, "%s", msg->nick);
- msg->nick[len] = '\0';
- break;
-
- case RES_SETNICK:
- /* buf = "<nick> <new_nick>" */
- sscanf(buf, "%s %s", msg->nick, msg->new_nick);
- msg->new_nick[len - strlen(msg->nick) - 1] = '\0';
- break;
-
- case RES_ERROR:
- /* buf = <error_code> */
- msg->err_code = (ResponseError) buf[0];
- break;
-
- default:
- return -1;
+ switch ( msg->type ) {
+ case RES_NORMAL:
+ case RES_ACTION:
+ case RES_QUIT:
+ /* buf = "<nick> <content>" */
+
+ /* Extract the first word as nick */
+ sscanf(buf, "%s", msg->nick);
+ buf += strlen(msg->nick);
+ len -= strlen(msg->nick);
+
+ /* Get `buf` to the first byte of content */
+ while ( len && buf[0] != '\0' && isspace(buf[0]) ) {
+ buf++;
+ len--;
+ }
+
+ memcpy(msg->content, buf, len);
+ msg->content[len] = '\0';
+ break;
+
+ case RES_JOIN:
+ /* buf = "<nick>" */
+
+ /* Extract the first word as nick */
+ sscanf(buf, "%s", msg->nick);
+ msg->nick[len] = '\0';
+ break;
+
+ case RES_SETNICK:
+ /* buf = "<nick> <new_nick>" */
+ sscanf(buf, "%s %s", msg->nick, msg->new_nick);
+ msg->new_nick[len - strlen(msg->nick) - 1] = '\0';
+ break;
+
+ case RES_ERROR:
+ /* buf = <error_code> */
+ msg->err_code = (ResponseError) buf[0];
+ if ( pending && *pending ) {
+ free(*pending);
+ *pending = NULL;
+ }
+ break;
+
+ case RES_CONFIRM:
+ if ( pending ) {
+ assert(*pending);
+
+ memcpy(msg, *pending, sizeof(Message));
+
+ free(*pending);
+ *pending = NULL;
+
+ if ( msg->type == RES_JOIN || msg->type == RES_SETNICK )
+ strcpy(nick, msg->new_nick);
+ }
+ break;
+
+ default:
+ return -1;
}
return 0;
M message.h => message.h +3 -2
@@ 12,6 12,7 @@ typedef struct _Message {
} Message;
int describe_error(ResponseError, char *);
-int parse_response(const char *, int, Message *);
+int parse_response(const char *buf, int len, char *nick, Message *msg,
+ Message **pending);
-#endif
+#endif /* _MESSAGE_H */