void
mm_port_serial_at_add_unsolicited_msg_handler (MMPortSerialAt *self,
                                               GRegex *regex,
                                               MMPortSerialAtUnsolicitedMsgFn callback,
                                               gpointer user_data,
                                               GDestroyNotify notify)
{
    GSList *existing;
    MMAtUnsolicitedMsgHandler *handler;

    g_return_if_fail (MM_IS_PORT_SERIAL_AT (self));
    g_return_if_fail (regex != NULL);

    existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers,
                                    regex,
                                    (GCompareFunc)unsolicited_msg_handler_cmp);
    if (existing) {
        handler = existing->data;
        /* We OVERWRITE any existing one, so if any context data existing, free it */
        if (handler->notify)
            handler->notify (handler->user_data);
    } else {
        handler = g_slice_new (MMAtUnsolicitedMsgHandler);
        self->priv->unsolicited_msg_handlers = g_slist_append (self->priv->unsolicited_msg_handlers, handler);
        handler->regex = g_regex_ref (regex);
    }

    handler->callback = callback;
    handler->enable = TRUE;
    handler->user_data = user_data;
    handler->notify = notify;
}
static void
parse_unsolicited (MMPortSerial *port, GByteArray *response)
{
    MMPortSerialAt *self = MM_PORT_SERIAL_AT (port);
    GSList *iter;

    /* Remove echo */
    if (self->priv->remove_echo)
        mm_port_serial_at_remove_echo (response);

    for (iter = self->priv->unsolicited_msg_handlers; iter; iter = iter->next) {
        MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) iter->data;
        GMatchInfo *match_info;
        gboolean matches;

        if (!handler->enable)
            continue;

        matches = g_regex_match_full (handler->regex,
                                      (const char *) response->data,
                                      response->len,
                                      0, 0, &match_info, NULL);
        if (handler->callback) {
            while (g_match_info_matches (match_info)) {
                handler->callback (self, match_info, handler->user_data);
                g_match_info_next (match_info, NULL);
            }
        }

        g_match_info_free (match_info);

        if (matches) {
            /* Remove matches */
            char *str;
            int result_len = response->len;

            str = g_regex_replace_eval (handler->regex,
                                        (const char *) response->data,
                                        response->len,
                                        0, 0,
                                        remove_eval_cb, &result_len, NULL);

            g_byte_array_remove_range (response, 0, response->len);
            g_byte_array_append (response, (const guint8 *) str, result_len);
            g_free (str);
        }
    }
}
static void
parse_unsolicited (MMSerialPort *port, GByteArray *response)
{
    MMAtSerialPort *self = MM_AT_SERIAL_PORT (port);
    MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);
    GSList *iter;

    for (iter = priv->unsolicited_msg_handlers; iter; iter = iter->next) {
        MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) iter->data;
        GMatchInfo *match_info;
        gboolean matches;

        matches = g_regex_match_full (handler->regex,
                                      (const char *) response->data,
                                      response->len,
                                      0, 0, &match_info, NULL);
        if (handler->callback) {
            while (g_match_info_matches (match_info)) {
                handler->callback (self, match_info, handler->user_data);
                g_match_info_next (match_info, NULL);
            }
        }

        g_match_info_free (match_info);

        if (matches) {
            /* Remove matches */
            char *str;
            int result_len = response->len;

            str = g_regex_replace_eval (handler->regex,
                                        (const char *) response->data,
                                        response->len,
                                        0, 0,
                                        remove_eval_cb, &result_len, NULL);

            g_byte_array_remove_range (response, 0, response->len);
            g_byte_array_append (response, (const guint8 *) str, result_len);
            g_free (str);
        }
    }
}
static void
finalize (GObject *object)
{
    MMAtSerialPort *self = MM_AT_SERIAL_PORT (object);
    MMAtSerialPortPrivate *priv = MM_AT_SERIAL_PORT_GET_PRIVATE (self);

    while (priv->unsolicited_msg_handlers) {
        MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) priv->unsolicited_msg_handlers->data;

        if (handler->notify)
            handler->notify (handler->user_data);

        g_regex_unref (handler->regex);
        g_slice_free (MMAtUnsolicitedMsgHandler, handler);
        priv->unsolicited_msg_handlers = g_slist_delete_link (priv->unsolicited_msg_handlers,
                                                              priv->unsolicited_msg_handlers);
    }

    if (priv->response_parser_notify)
        priv->response_parser_notify (priv->response_parser_user_data);

    G_OBJECT_CLASS (mm_at_serial_port_parent_class)->finalize (object);
}
static void
finalize (GObject *object)
{
    MMPortSerialAt *self = MM_PORT_SERIAL_AT (object);

    while (self->priv->unsolicited_msg_handlers) {
        MMAtUnsolicitedMsgHandler *handler = (MMAtUnsolicitedMsgHandler *) self->priv->unsolicited_msg_handlers->data;

        if (handler->notify)
            handler->notify (handler->user_data);

        g_regex_unref (handler->regex);
        g_slice_free (MMAtUnsolicitedMsgHandler, handler);
        self->priv->unsolicited_msg_handlers = g_slist_delete_link (self->priv->unsolicited_msg_handlers,
                                                                    self->priv->unsolicited_msg_handlers);
    }

    if (self->priv->response_parser_notify)
        self->priv->response_parser_notify (self->priv->response_parser_user_data);

    g_strfreev (self->priv->init_sequence);

    G_OBJECT_CLASS (mm_port_serial_at_parent_class)->finalize (object);
}