Refactor GAtChat to accept user-provided parsers

Intended for really broken modems.  A default 27.007 compliant
parser is provided.
This commit is contained in:
Denis Kenzior 2009-08-04 18:53:25 -05:00
parent 239ab461f7
commit 83820c88b2
5 changed files with 389 additions and 232 deletions

View File

@ -2,7 +2,7 @@
noinst_LTLIBRARIES = libgatchat.la
libgatchat_la_SOURCES = gatchat.h gatchat.c gatresult.h gatresult.c \
ringbuffer.h ringbuffer.c
ringbuffer.h ringbuffer.c gatsyntax.h gatsyntax.c
AM_CFLAGS = @GLIB_CFLAGS@

View File

@ -33,30 +33,12 @@
#include <glib.h>
#include "ringbuffer.h"
#include "gatresult.h"
#include "gatchat.h"
/* #define WRITE_SCHEDULER_DEBUG 1 */
static void g_at_chat_wakeup_writer(GAtChat *chat);
enum chat_state {
PARSER_STATE_IDLE = 0,
PARSER_STATE_INITIAL_CR,
PARSER_STATE_INITIAL_LF,
PARSER_STATE_RESPONSE,
PARSER_STATE_TERMINATOR_CR,
PARSER_STATE_RESPONSE_COMPLETE,
PARSER_STATE_GUESS_MULTILINE_RESPONSE,
PARSER_STATE_MULTILINE_RESPONSE,
PARSER_STATE_MULTILINE_TERMINATOR_CR,
PARSER_STATE_MULTILINE_COMPLETE,
PARSER_STATE_PDU,
PARSER_STATE_PDU_CR,
PARSER_STATE_PDU_COMPLETE,
PARSER_STATE_PROMPT,
PARSER_STATE_PROMPT_COMPLETE
};
static void debug_chat(GAtChat *chat, gboolean in, const char *str, gsize len);
struct at_command {
char *cmd;
@ -95,14 +77,13 @@ struct _GAtChat {
struct ring_buffer *buf; /* Current read buffer */
guint read_so_far; /* Number of bytes processed */
gboolean disconnecting; /* Whether we're disconnecting */
enum chat_state state; /* Current chat state */
int flags;
char *pdu_notify; /* Unsolicited Resp w/ PDU */
GSList *response_lines; /* char * lines of the response */
char *wakeup; /* command sent to wakeup modem */
gdouble inactivity_time; /* Period of inactivity */
guint wakeup_timeout; /* How long to wait for resp */
GTimer *wakeup_timer; /* Keep track of elapsed time */
GAtSyntax *syntax;
};
static gint at_notify_node_compare_by_id(gconstpointer a, gconstpointer b)
@ -254,6 +235,9 @@ static void g_at_chat_cleanup(GAtChat *chat)
g_timer_destroy(chat->wakeup_timer);
chat->wakeup_timer = 0;
}
g_at_syntax_unref(chat->syntax);
chat->syntax = NULL;
}
static void read_watcher_destroy_notify(GAtChat *chat)
@ -306,7 +290,10 @@ static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
if (notify->pdu) {
chat->pdu_notify = line;
chat->state = PARSER_STATE_PDU;
if (chat->syntax->set_hint)
chat->syntax->set_hint(chat->syntax,
G_AT_SYNTAX_EXPECT_PDU);
return TRUE;
}
@ -321,7 +308,6 @@ static gboolean g_at_chat_match_notify(GAtChat *chat, char *line)
if (ret) {
g_slist_free(result.lines);
g_free(line);
chat->state = PARSER_STATE_IDLE;
}
return ret;
@ -387,8 +373,6 @@ static gboolean g_at_chat_handle_command_response(GAtChat *p,
int i;
int size = sizeof(terminator_table) / sizeof(struct terminator_info);
p->state = PARSER_STATE_IDLE;
for (i = 0; i < size; i++) {
struct terminator_info *info = &terminator_table[i];
@ -415,8 +399,8 @@ static gboolean g_at_chat_handle_command_response(GAtChat *p,
}
out:
if (!(p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF))
p->state = PARSER_STATE_GUESS_MULTILINE_RESPONSE;
if (p->syntax->set_hint)
p->syntax->set_hint(p->syntax, G_AT_SYNTAX_EXPECT_MULTILINE);
if (cmd->listing) {
GAtResult result;
@ -434,31 +418,13 @@ out:
return TRUE;
}
static void have_line(GAtChat *p, gboolean strip_preceding)
static void have_line(GAtChat *p, char *str)
{
/* We're not going to copy terminal <CR><LF> */
unsigned int len = p->read_so_far - 2;
char *str;
struct at_command *cmd;
/* If we have preceding <CR><LF> modify the len */
if (strip_preceding)
len -= 2;
/* Make sure we have terminal null */
str = g_try_new(char, len + 1);
if (!str) {
ring_buffer_drain(p->buf, p->read_so_far);
if (!str)
return;
}
if (strip_preceding)
ring_buffer_drain(p->buf, 2);
ring_buffer_read(p->buf, str, len);
ring_buffer_drain(p->buf, 2);
str[len] = '\0';
/* Check for echo, this should not happen, but lets be paranoid */
if (!strncmp(str, "AT", 2) == TRUE)
@ -488,30 +454,18 @@ static void have_line(GAtChat *p, gboolean strip_preceding)
done:
/* No matches & no commands active, ignore line */
g_free(str);
p->state = PARSER_STATE_IDLE;
}
static void have_pdu(GAtChat *p)
static void have_pdu(GAtChat *p, char *pdu)
{
unsigned int len = p->read_so_far - 2;
char *pdu;
GHashTableIter iter;
struct at_notify *notify;
char *prefix;
gpointer key, value;
GAtResult result;
pdu = g_try_new(char, len + 1);
if (!pdu) {
ring_buffer_drain(p->buf, p->read_so_far);
if (!pdu)
goto out;
}
ring_buffer_read(p->buf, pdu, len);
ring_buffer_drain(p->buf, 2);
pdu[len] = '\0';
result.lines = g_slist_prepend(NULL, p->pdu_notify);
result.final_or_pdu = pdu;
@ -540,97 +494,47 @@ out:
if (pdu)
g_free(pdu);
p->state = PARSER_STATE_IDLE;
}
static inline void parse_char(GAtChat *chat, char byte)
static char *extract_line(GAtChat *p)
{
switch (chat->state) {
case PARSER_STATE_IDLE:
if (byte == '\r')
chat->state = PARSER_STATE_INITIAL_CR;
else if (chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF) {
if (byte == '>')
chat->state = PARSER_STATE_PROMPT;
unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
unsigned int pos = 0;
unsigned char *buf = ring_buffer_read_ptr(p->buf, pos);
int strip_front = 0;
int line_length = 0;
char *line;
while (pos < p->read_so_far) {
if (*buf == '\r' || *buf == '\n')
if (!line_length)
strip_front += 1;
else
chat->state = PARSER_STATE_RESPONSE;
}
break;
case PARSER_STATE_INITIAL_CR:
if (byte == '\n')
chat->state = PARSER_STATE_INITIAL_LF;
else if (byte != '\r' && /* Echo & no <CR><LF>?! */
(chat->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF))
chat->state = PARSER_STATE_RESPONSE;
else if (byte != '\r')
chat->state = PARSER_STATE_IDLE;
break;
case PARSER_STATE_INITIAL_LF:
if (byte == '\r')
chat->state = PARSER_STATE_TERMINATOR_CR;
else if (byte == '>')
chat->state = PARSER_STATE_PROMPT;
break;
else
chat->state = PARSER_STATE_RESPONSE;
break;
line_length += 1;
case PARSER_STATE_RESPONSE:
if (byte == '\r')
chat->state = PARSER_STATE_TERMINATOR_CR;
break;
buf += 1;
pos += 1;
case PARSER_STATE_TERMINATOR_CR:
if (byte == '\n')
chat->state = PARSER_STATE_RESPONSE_COMPLETE;
else
chat->state = PARSER_STATE_IDLE;
break;
case PARSER_STATE_GUESS_MULTILINE_RESPONSE:
if (byte == '\r')
chat->state = PARSER_STATE_INITIAL_CR;
else
chat->state = PARSER_STATE_MULTILINE_RESPONSE;
break;
case PARSER_STATE_MULTILINE_RESPONSE:
if (byte == '\r')
chat->state = PARSER_STATE_MULTILINE_TERMINATOR_CR;
break;
case PARSER_STATE_MULTILINE_TERMINATOR_CR:
if (byte == '\n')
chat->state = PARSER_STATE_MULTILINE_COMPLETE;
break;
case PARSER_STATE_PDU:
if (byte == '\r')
chat->state = PARSER_STATE_PDU_CR;
break;
case PARSER_STATE_PDU_CR:
if (byte == '\n')
chat->state = PARSER_STATE_PDU_COMPLETE;
break;
case PARSER_STATE_PROMPT:
if (byte == ' ')
chat->state = PARSER_STATE_PROMPT_COMPLETE;
else
chat->state = PARSER_STATE_RESPONSE;
break;
case PARSER_STATE_RESPONSE_COMPLETE:
case PARSER_STATE_PDU_COMPLETE:
case PARSER_STATE_MULTILINE_COMPLETE:
default:
/* This really shouldn't happen */
assert(FALSE);
return;
if (pos == wrap)
buf = ring_buffer_read_ptr(p->buf, pos);
}
line = g_try_new(char, line_length + 1);
if (!line) {
ring_buffer_drain(p->buf, p->read_so_far);
return NULL;
}
ring_buffer_drain(p->buf, strip_front);
ring_buffer_read(p->buf, line, line_length);
ring_buffer_drain(p->buf, p->read_so_far - strip_front - line_length);
line[line_length] = '\0';
return line;
}
static void new_bytes(GAtChat *p)
@ -639,76 +543,45 @@ static void new_bytes(GAtChat *p)
unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
unsigned char *buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
while (p->read_so_far < len) {
parse_char(p, *buf);
GAtSyntaxResult result;
buf += 1;
p->read_so_far += 1;
while (p->read_so_far < len) {
gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
result = p->syntax->feed(p->syntax, (char *)buf, &rbytes);
buf += rbytes;
p->read_so_far += rbytes;
if (p->read_so_far == wrap) {
buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
wrap = len;
}
if (p->state == PARSER_STATE_RESPONSE_COMPLETE) {
gboolean strip_preceding;
if (result == G_AT_SYNTAX_RESULT_UNSURE)
continue;
if (p->flags & G_AT_CHAT_FLAG_NO_LEADING_CRLF)
strip_preceding = FALSE;
else
strip_preceding = TRUE;
switch (result) {
case G_AT_SYNTAX_RESULT_LINE:
case G_AT_SYNTAX_RESULT_MULTILINE:
have_line(p, extract_line(p));
break;
len -= p->read_so_far;
wrap -= p->read_so_far;
have_line(p, strip_preceding);
p->read_so_far = 0;
} else if (p->state == PARSER_STATE_MULTILINE_COMPLETE) {
len -= p->read_so_far;
wrap -= p->read_so_far;
have_line(p, FALSE);
p->read_so_far = 0;
} else if (p->state == PARSER_STATE_PDU_COMPLETE) {
len -= p->read_so_far;
wrap -= p->read_so_far;
/* Some modems like the TI Calypso send a CMT style
* notification with an extra CRLF thrown in
*/
if ((p->flags & G_AT_CHAT_FLAG_EXTRA_PDU_CRLF) &&
p->read_so_far == 2) {
p->state = PARSER_STATE_PDU;
ring_buffer_drain(p->buf, p->read_so_far);
} else
have_pdu(p);
p->read_so_far = 0;
} else if (p->state == PARSER_STATE_INITIAL_CR) {
len -= p->read_so_far - 1;
wrap -= p->read_so_far - 1;
ring_buffer_drain(p->buf, p->read_so_far - 1);
p->read_so_far = 1;
} else if (p->state == PARSER_STATE_PROMPT_COMPLETE) {
len -= p->read_so_far;
wrap -= p->read_so_far;
case G_AT_SYNTAX_RESULT_PDU:
have_pdu(p, extract_line(p));
break;
case G_AT_SYNTAX_RESULT_PROMPT:
g_at_chat_wakeup_writer(p);
ring_buffer_drain(p->buf, p->read_so_far);
break;
p->read_so_far = 0;
p->state = PARSER_STATE_IDLE;
default:
ring_buffer_drain(p->buf, p->read_so_far);
break;
}
}
if (p->state == PARSER_STATE_IDLE && p->read_so_far > 0) {
ring_buffer_drain(p->buf, p->read_so_far);
len -= p->read_so_far;
wrap -= p->read_so_far;
p->read_so_far = 0;
}
}
@ -736,13 +609,8 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
* this cannot happen under normal circumstances, so probably
* the channel is getting garbage, drop off
*/
if (toread == 0) {
if (chat->state == PARSER_STATE_RESPONSE)
return FALSE;
err = G_IO_ERROR_AGAIN;
break;
}
if (toread == 0)
return FALSE;
buf = ring_buffer_write_ptr(chat->buf);
@ -900,7 +768,7 @@ static void g_at_chat_wakeup_writer(GAtChat *chat)
(GDestroyNotify)write_watcher_destroy_notify);
}
GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax)
{
GAtChat *chat;
GIOFlags io_flags;
@ -908,6 +776,9 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
if (!channel)
return NULL;
if (!syntax)
return NULL;
chat = g_try_new0(GAtChat, 1);
if (!chat)
@ -916,7 +787,6 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
chat->ref_count = 1;
chat->next_cmd_id = 1;
chat->next_notify_id = 1;
chat->flags = flags;
chat->buf = ring_buffer_new(4096);
@ -951,6 +821,8 @@ GAtChat *g_at_chat_new(GIOChannel *channel, int flags)
received_data, chat,
(GDestroyNotify)read_watcher_destroy_notify);
chat->syntax = g_at_syntax_ref(syntax);
return chat;
error:
@ -967,22 +839,6 @@ error:
return NULL;
}
int g_at_chat_get_flags(GAtChat *chat)
{
if (chat == NULL)
return 0;
return chat->flags;
}
void g_at_chat_set_flags(GAtChat *chat, int flags)
{
if (chat == NULL)
return;
chat->flags = flags;
}
static int open_device(const char *device)
{
struct termios ti;
@ -1003,7 +859,7 @@ static int open_device(const char *device)
return fd;
}
GAtChat *g_at_chat_new_from_tty(const char *device, int flags)
GAtChat *g_at_chat_new_from_tty(const char *device, GAtSyntax *syntax)
{
GIOChannel *channel;
int fd;
@ -1018,7 +874,7 @@ GAtChat *g_at_chat_new_from_tty(const char *device, int flags)
return NULL;
}
return g_at_chat_new(channel, flags);
return g_at_chat_new(channel, syntax);
}
GAtChat *g_at_chat_ref(GAtChat *chat)

View File

@ -27,6 +27,7 @@ extern "C" {
#endif
#include "gatresult.h"
#include "gatsyntax.h"
struct _GAtChat;
@ -44,11 +45,8 @@ enum _GAtChatFlags {
typedef enum _GAtChatFlags GAtChatFlags;
GAtChat *g_at_chat_new(GIOChannel *channel, int flags);
GAtChat *g_at_chat_new_from_tty(const char *device, int flags);
int g_at_chat_get_flags(GAtChat *chat);
void g_at_chat_set_flags(GAtChat *chat, int flags);
GAtChat *g_at_chat_new(GIOChannel *channel, GAtSyntax *syntax);
GAtChat *g_at_chat_new_from_tty(const char *device, GAtSyntax *syntax);
GAtChat *g_at_chat_ref(GAtChat *chat);
void g_at_chat_unref(GAtChat *chat);

228
gatchat/gatsyntax.c Normal file
View File

@ -0,0 +1,228 @@
/*
*
* AT chat library with GLib integration
*
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include "gatsyntax.h"
enum GSMV1_STATE_ {
GSMV1_STATE_IDLE = 0,
GSMV1_STATE_INITIAL_CR,
GSMV1_STATE_INITIAL_LF,
GSMV1_STATE_RESPONSE,
GSMV1_STATE_TERMINATOR_CR,
GSMV1_STATE_GUESS_MULTILINE_RESPONSE,
GSMV1_STATE_MULTILINE_RESPONSE,
GSMV1_STATE_MULTILINE_TERMINATOR_CR,
GSMV1_STATE_PDU,
GSMV1_STATE_PDU_CR,
GSMV1_STATE_PROMPT,
GSMV1_STATE_GARBAGE,
GSMV1_STATE_GARBAGE_CHECK_LF,
};
static void gsmv1_hint(GAtSyntax *syntax, GAtSyntaxExpectHint hint)
{
switch (hint) {
case G_AT_SYNTAX_EXPECT_PDU:
syntax->state = GSMV1_STATE_PDU;
break;
case G_AT_SYNTAX_EXPECT_MULTILINE:
syntax->state = GSMV1_STATE_GUESS_MULTILINE_RESPONSE;
break;
default:
break;
};
}
static GAtSyntaxResult gsmv1_feed(GAtSyntax *syntax,
const char *bytes, gsize *len)
{
gsize i = 0;
GAtSyntaxResult res = G_AT_SYNTAX_RESULT_UNSURE;
while (i < *len) {
char byte = bytes[i];
switch (syntax->state) {
case GSMV1_STATE_IDLE:
if (byte == '\r')
syntax->state = GSMV1_STATE_INITIAL_CR;
else
syntax->state = GSMV1_STATE_GARBAGE;
break;
case GSMV1_STATE_INITIAL_CR:
if (byte == '\n')
syntax->state = GSMV1_STATE_INITIAL_LF;
else
syntax->state = GSMV1_STATE_GARBAGE;
break;
case GSMV1_STATE_INITIAL_LF:
if (byte == '\r')
syntax->state = GSMV1_STATE_TERMINATOR_CR;
else if (byte == '>')
syntax->state = GSMV1_STATE_PROMPT;
else
syntax->state = GSMV1_STATE_RESPONSE;
break;
case GSMV1_STATE_RESPONSE:
if (byte == '\r')
syntax->state = GSMV1_STATE_TERMINATOR_CR;
break;
case GSMV1_STATE_TERMINATOR_CR:
syntax->state = GSMV1_STATE_IDLE;
if (byte == '\n') {
i += 1;
res = G_AT_SYNTAX_RESULT_LINE;
} else
res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
goto out;
case GSMV1_STATE_GUESS_MULTILINE_RESPONSE:
if (byte == '\r')
syntax->state = GSMV1_STATE_INITIAL_CR;
else
syntax->state = GSMV1_STATE_MULTILINE_RESPONSE;
break;
case GSMV1_STATE_MULTILINE_RESPONSE:
if (byte == '\r')
syntax->state = GSMV1_STATE_MULTILINE_TERMINATOR_CR;
break;
case GSMV1_STATE_MULTILINE_TERMINATOR_CR:
syntax->state = GSMV1_STATE_IDLE;
if (byte == '\n') {
i += 1;
res = G_AT_SYNTAX_RESULT_MULTILINE;
} else
res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
goto out;
case GSMV1_STATE_PDU:
if (byte == '\r')
syntax->state = GSMV1_STATE_PDU_CR;
break;
case GSMV1_STATE_PDU_CR:
syntax->state = GSMV1_STATE_IDLE;
if (byte == '\n') {
i += 1;
res = G_AT_SYNTAX_RESULT_PDU;
} else
res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
goto out;
case GSMV1_STATE_PROMPT:
if (byte == ' ') {
syntax->state = GSMV1_STATE_IDLE;
i += 1;
res = G_AT_SYNTAX_RESULT_PROMPT;
goto out;
}
syntax->state = GSMV1_STATE_RESPONSE;
return G_AT_SYNTAX_RESULT_UNSURE;
case GSMV1_STATE_GARBAGE:
if (byte == '\r')
syntax->state = GSMV1_STATE_GARBAGE_CHECK_LF;
break;
case GSMV1_STATE_GARBAGE_CHECK_LF:
syntax->state = GSMV1_STATE_IDLE;
res = G_AT_SYNTAX_RESULT_UNRECOGNIZED;
if (byte == '\n')
i += 1;
goto out;
default:
break;
};
i += 1;
}
out:
*len = i;
return res;
}
GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed,
GAtSyntaxSetHintFunc hint,
int initial_state)
{
GAtSyntax *syntax;
syntax = g_new0(GAtSyntax, 1);
syntax->feed = feed;
syntax->set_hint = hint;
syntax->state = initial_state;
syntax->ref_count = 1;
return syntax;
}
GAtSyntax *g_at_syntax_new_gsmv1()
{
return g_at_syntax_new_full(gsmv1_feed, gsmv1_hint, GSMV1_STATE_IDLE);
}
GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax)
{
if (syntax == NULL)
return NULL;
g_atomic_int_inc(&syntax->ref_count);
return syntax;
}
void g_at_syntax_unref(GAtSyntax *syntax)
{
gboolean is_zero;
if (syntax == NULL)
return;
is_zero = g_atomic_int_dec_and_test(&syntax->ref_count);
if (is_zero)
g_free(syntax);
}

75
gatchat/gatsyntax.h Normal file
View File

@ -0,0 +1,75 @@
/*
*
* AT chat library with GLib integration
*
* Copyright (C) 2008-2009 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef __GATSYNTAX_H
#define __GATSYNTAX_H
#ifdef __cplusplus
extern "C" {
#endif
enum _GAtSyntaxExpectHint {
G_AT_SYNTAX_EXPECT_PDU,
G_AT_SYNTAX_EXPECT_MULTILINE,
G_AT_SYNTAX_EXPECT_PROMPT
};
typedef enum _GAtSyntaxExpectHint GAtSyntaxExpectHint;
enum _GAtSyntaxResult {
G_AT_SYNTAX_RESULT_UNRECOGNIZED,
G_AT_SYNTAX_RESULT_UNSURE,
G_AT_SYNTAX_RESULT_LINE,
G_AT_SYNTAX_RESULT_MULTILINE,
G_AT_SYNTAX_RESULT_PDU,
G_AT_SYNTAX_RESULT_PROMPT,
};
typedef enum _GAtSyntaxResult GAtSyntaxResult;
typedef struct _GAtSyntax GAtSyntax;
typedef void (*GAtSyntaxSetHintFunc)(GAtSyntax *syntax,
GAtSyntaxExpectHint hint);
typedef GAtSyntaxResult (*GAtSyntaxFeedFunc)(GAtSyntax *syntax,
const char *bytes, gsize *len);
struct _GAtSyntax {
gint ref_count;
int state;
GAtSyntaxSetHintFunc set_hint;
GAtSyntaxFeedFunc feed;
};
GAtSyntax *g_at_syntax_new_full(GAtSyntaxFeedFunc feed,
GAtSyntaxSetHintFunc hint,
int initial_state);
GAtSyntax *g_at_syntax_new_gsmv1();
GAtSyntax *g_at_syntax_ref(GAtSyntax *syntax);
void g_at_syntax_unref(GAtSyntax *syntax);
#ifdef __cplusplus
}
#endif
#endif /* __GATSYNTAX_H */