---
 gatchat/gatserver.c |  182 +++++++++++++++++++++++++++++++++++---------------
 gatchat/gatserver.h |    7 ++-
 2 files changed, 133 insertions(+), 56 deletions(-)

diff --git a/gatchat/gatserver.c b/gatchat/gatserver.c
index c75fbf5..74c765d 100644
--- a/gatchat/gatserver.c
+++ b/gatchat/gatserver.c
@@ -112,9 +112,51 @@ struct _GAtServer {
        guint max_read_attempts;                /* Max reads per select */
        enum ParserState parser_state;
        gboolean destroyed;                     /* Re-entrancy guard */
+       char *read_line;                        /* Current read line */
+       unsigned int read_pos;                  /* Current read offset */
+       int notify_source;                      /* Source of notify callback */
+       GAtServerResult last_result;            /* Result of last command */
+};
+
+struct at_notify_node {
+       GAtServer *server;
+       char *command;
+       char *prefix;
+       GAtServerRequestType type;
 };
 
 static void g_at_server_wakeup_writer(GAtServer *server);
+static void server_parse_line(gpointer user_data);
+
+static struct at_notify_node *at_node_new(GAtServer *server,
+                                               char *buf, char *prefix,
+                                               GAtServerRequestType type)
+{
+       struct at_notify_node *ret;
+
+       ret = g_try_new0(struct at_notify_node, 1);
+
+       if (!ret)
+               return ret;
+
+       ret->server = server;
+       ret->command = g_strdup(buf);
+       ret->prefix = g_strdup(prefix);
+       ret->type = type;
+
+       return ret;
+}
+
+static void at_notify_destroy(gpointer user_data)
+{
+       struct at_notify_node *notify = user_data;
+
+       notify->server->notify_source = 0;
+
+       g_free(notify->command);
+       g_free(notify->prefix);
+       g_free(notify);
+}
 
 static struct ring_buffer *allocate_next(GAtServer *server)
 {
@@ -194,28 +236,32 @@ static inline gboolean is_extended_command_prefix(const 
char c)
        }
 }
 
-static void at_command_notify(GAtServer *server, char *command,
-                               char *prefix, GAtServerRequestType type)
+static gboolean at_command_notify(gpointer user_data)
 {
+       struct at_notify_node *notify = user_data;
+       GAtServer *server = notify->server;
        struct at_command *node;
        GAtResult result;
 
-       node = g_hash_table_lookup(server->command_list, prefix);
+       node = g_hash_table_lookup(server->command_list, notify->prefix);
 
        if (node == NULL) {
                g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
-               return;
+               return FALSE;
        }
 
-       result.lines = g_slist_prepend(NULL, command);
+       result.lines = g_slist_prepend(NULL, notify->command);
        result.final_or_pdu = 0;
 
-       node->notify(type, &result, node->user_data);
+       node->notify(notify->type, &result, node->user_data,
+                       server_parse_line, server);
 
        g_slist_free(result.lines);
+
+       return FALSE;
 }
 
-static unsigned int parse_extended_command(GAtServer *server, char *buf)
+static void parse_extended_command(GAtServer *server, char *buf)
 {
        const char *valid_extended_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                                "0123456789!%-./:_";
@@ -226,11 +272,12 @@ static unsigned int parse_extended_command(GAtServer 
*server, char *buf)
        gboolean seen_equals = FALSE;
        char prefix[18]; /* According to V250, 5.4.1 */
        GAtServerRequestType type;
+       struct at_notify_node *notify = NULL;
 
        prefix_len = strcspn(buf, separators);
 
        if (prefix_len > 17 || prefix_len < 2)
-               return 0;
+               goto error;
 
        /* Convert to upper case, we will always use upper case naming */
        for (i = 0; i < prefix_len; i++)
@@ -239,17 +286,17 @@ static unsigned int parse_extended_command(GAtServer 
*server, char *buf)
        prefix[prefix_len] = '\0';
 
        if (strspn(prefix + 1, valid_extended_chars) != (prefix_len - 1))
-               return 0;
+               goto error;
 
        /*
         * V.250 Section 5.4.1: "The first character following "+" shall be
         * an alphabetic character in the range "A" through "Z".
         */
        if (prefix[1] <= 'A' || prefix[1] >= 'Z')
-               return 0;
+               goto error;
 
        if (buf[i] != '\0' && buf[i] != ';' && buf[i] != '?' && buf[i] != '=')
-               return 0;
+               goto error;
 
        type = G_AT_SERVER_REQUEST_TYPE_COMMAND_ONLY;
 
@@ -265,16 +312,16 @@ static unsigned int parse_extended_command(GAtServer 
*server, char *buf)
 
                if (buf[i] == '?') {
                        if (seen_question || seen_equals)
-                               return 0;
+                               goto error;
 
                        if (buf[i + 1] != '\0' && buf[i + 1] != ';')
-                               return 0;
+                               goto error;
 
                        seen_question = TRUE;
                        type = G_AT_SERVER_REQUEST_TYPE_QUERY;
                } else if (buf[i] == '=') {
                        if (seen_equals || seen_question)
-                               return 0;
+                               goto error;
 
                        seen_equals = TRUE;
 
@@ -291,10 +338,22 @@ next:
        /* We can scratch in this buffer, so mark ';' as null */
        buf[i] = '\0';
 
-       at_command_notify(server, buf, prefix, type);
+       notify = at_node_new(server, buf, prefix, type);
+       if (!notify)
+               goto error;
 
        /* Also consume the terminating null */
-       return i + 1;
+       server->read_pos += i + 1;
+       server->notify_source = g_idle_add_full(G_PRIORITY_DEFAULT,
+                                               at_command_notify,
+                                               notify, g_free);
+
+       return;
+
+error:
+       g_free(notify);
+
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
 }
 
 static int get_basic_prefix_size(const char *buf)
@@ -336,16 +395,17 @@ static int get_basic_prefix_size(const char *buf)
        return 0;
 }
 
-static unsigned int parse_basic_command(GAtServer *server, char *buf)
+static void parse_basic_command(GAtServer *server, char *buf)
 {
        gboolean seen_equals = FALSE;
        char prefix[4], tmp;
        unsigned int i, prefix_size;
        GAtServerRequestType type;
+       struct at_notify_node *notify = NULL;
 
        prefix_size = get_basic_prefix_size(buf);
        if (prefix_size == 0)
-               return 0;
+               goto error;
 
        i = prefix_size;
        prefix[0] = g_ascii_toupper(buf[0]);
@@ -390,56 +450,59 @@ static unsigned int parse_basic_command(GAtServer 
*server, char *buf)
        }
 
 done:
-       if (prefix_size <= 3) {
-               memcpy(prefix + 1, buf + 1, prefix_size - 1);
-               prefix[prefix_size] = '\0';
-
-               tmp = buf[i];
-               buf[i] = '\0';
-               at_command_notify(server, buf, prefix, type);
-               buf[i] = tmp;
-       } else /* Handle S-parameter with 100+ */
-               g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
+       if (prefix_size > 3)
+               goto error;
 
        /* Commands like ATA, ATZ cause the remainder line
         * to be ignored.
         */
        if (prefix[0] == 'A' || prefix[0] == 'Z')
-               return strlen(buf);
+               i = strlen(buf);
 
        /* Consume the seperator ';' */
        if (buf[i] == ';')
                i += 1;
 
-       return i;
+       memcpy(prefix + 1, buf + 1, prefix_size - 1);
+       prefix[prefix_size] = '\0';
+
+       tmp = buf[i];
+       buf[i] = '\0';
+
+       notify = at_node_new(server, buf, prefix, type);
+       if (!notify)
+               goto error;
+
+       buf[i] = tmp;
+
+       server->read_pos += i;
+       server->notify_source = g_idle_add_full(G_PRIORITY_DEFAULT,
+                                               at_command_notify,
+                                               notify, at_notify_destroy);
+
+       return;
+
+error:
+       g_free(notify);
+
+       g_at_server_send_final(server, G_AT_SERVER_RESULT_ERROR);
 }
 
-static void server_parse_line(GAtServer *server, char *line)
+static void server_parse_line(gpointer user_data)
 {
-       unsigned int pos = 0;
+       GAtServer *server = user_data;
+       char *line = server->read_line;
+       unsigned int pos = server->read_pos;
        unsigned int len = strlen(line);
 
-       if (len == 0) {
-               g_at_server_send_final(server, G_AT_SERVER_RESULT_OK);
+       /* If we have a non-OK result or no further commands, then stop now */
+       if (server->last_result != G_AT_SERVER_RESULT_OK || pos >= len)
                return;
-       }
-
-       while (pos < len) {
-               unsigned int consumed;
 
-               if (is_extended_command_prefix(line[pos]))
-                       consumed = parse_extended_command(server, line + pos);
-               else
-                       consumed = parse_basic_command(server, line + pos);
-
-               if (consumed == 0) {
-                       g_at_server_send_final(server,
-                                               G_AT_SERVER_RESULT_ERROR);
-                       break;
-               }
-
-               pos += consumed;
-       }
+       if (is_extended_command_prefix(line[pos]))
+               parse_extended_command(server, line + pos);
+       else
+               parse_basic_command(server, line + pos);
 }
 
 static enum ParserResult server_feed(GAtServer *server,
@@ -621,11 +684,15 @@ static void new_bytes(GAtServer *p)
 
                case PARSER_RESULT_COMMAND:
                {
-                       char *line = extract_line(p);
+                       g_free(p->read_line);
 
-                       if (line) {
-                               server_parse_line(p, line);
-                               g_free(line);
+                       /* Save current line as last line */
+                       p->read_line = extract_line(p);
+                       if (p->read_line) {
+                               p->read_pos = 0;
+                               p->last_result = G_AT_SERVER_RESULT_OK;
+
+                               server_parse_line(p);
                        } else
                                g_at_server_send_final(p,
                                                G_AT_SERVER_RESULT_ERROR);
@@ -795,6 +862,11 @@ static void g_at_server_cleanup(GAtServer *server)
        g_hash_table_destroy(server->command_list);
        server->command_list = NULL;
 
+       g_free(server->read_line);
+
+       if (server->notify_source)
+               g_source_remove(server->notify_source);
+
        server->channel = NULL;
 }
 
diff --git a/gatchat/gatserver.h b/gatchat/gatserver.h
index 2ae19ca..4383a58 100644
--- a/gatchat/gatserver.h
+++ b/gatchat/gatserver.h
@@ -64,8 +64,13 @@ enum _GAtServerRequestType {
 
 typedef enum _GAtServerRequestType GAtServerRequestType;
 
+typedef void (*GAtServerNotifyCallback)(gpointer user_data);
+
 typedef void (*GAtServerNotifyFunc)(GAtServerRequestType type,
-                                       GAtResult *result, gpointer user_data);
+                                       GAtResult *result,
+                                       gpointer user_data,
+                                       GAtServerNotifyCallback cb,
+                                       gpointer cb_data);
 
 GAtServer *g_at_server_new(GIOChannel *io);
 
-- 
1.6.6.1

_______________________________________________
ofono mailing list
[email protected]
http://lists.ofono.org/listinfo/ofono

Reply via email to