From b8a0936b9935ea428041f97230473d1df10f85b0 Mon Sep 17 00:00:00 2001 From: zolliker Date: Tue, 10 Nov 2009 07:47:32 +0000 Subject: [PATCH] - implemented multi-line response in AsconStdHandler --- ascon.c | 74 ++++++++++++++++++++++++++++++++++++++------------------- ascon.h | 9 +++++++ ascon.i | 26 +++++++++++++------- 3 files changed, 75 insertions(+), 34 deletions(-) diff --git a/ascon.c b/ascon.c index 6de1c333..9150c21b 100644 --- a/ascon.c +++ b/ascon.c @@ -415,55 +415,70 @@ int AsconStdHandler(Ascon * a) { int result; char chr; - int ret; + int ret, l; + char *cmd, *opt; switch (a->state) { case AsconWriteStart: - if (strstr(GetCharArray(a->wrBuffer), "@@NOSEND@@") != NULL) { + cmd = GetCharArray(a->wrBuffer); + a->lineCount = 1; + if (a->separator != NULL) { /* multiline mode enabled */ + l = strlen(cmd); + if (l> 0 && cmd[l-1] == '}') { + opt = strrchr(cmd, '{'); + if (opt != NULL) { + if (sscanf(opt, "{%d}", &a->lineCount) == 1) { + /* remove option */ + for (l = strlen(opt); l > 0; l--) { + DynStringBackspace(a->wrBuffer); + } + } + } + } + } else if (strstr(GetCharArray(a->wrBuffer), "@@NOSEND@@") != NULL) { a->state = AsconWriteDone; return 1; } break; /* go to the base handler */ - case AsconWriting: - if (a->readState) { /* last char was CR */ - ret = AsconReadChar(a->fd, &chr); - if (ret > 0) { - if (chr == '\n') { - /* swallow LF after CR */ - a->readState = 0; - } else { - /* garbage character found -> swallow */ - } - } - } - break; /* go to the base handler */ case AsconReading: + if (a->lineCount == 0) { + /* no response expected */ + a->state = AsconReadDone; + return 1; + } result = AsconBaseHandler(a); if (result == 0) return 0; chr = a->lastChar; if (a->replyTerminator != NULL) { if (strchr(a->replyTerminator, chr) != NULL) { - DynStringConcatChar(a->rdBuffer, '\0'); a->state = AsconReadDone; + if (chr == '\n' || chr == '\r') { + DynStringBackspace(a->rdBuffer); /* remove LF or CR */ + } } } else { - if (chr == '\n') { + if (chr == '\n') { /* LF */ + DynStringBackspace(a->rdBuffer); /* remove LF */ if (a->readState) { /* last char was CR */ - /* swallow LF after CR */ + /* LF after CR is not a terminator */ a->readState = 0; } else { - DynStringBackspace(a->rdBuffer); /* remove LF */ - DynStringConcatChar(a->rdBuffer, '\0'); a->state = AsconReadDone; } - } else if (chr == '\r') { - a->readState = 1; /* set 'last char was CR' */ + } else if (chr == '\r') { /* CR */ DynStringBackspace(a->rdBuffer); /* remove CR */ - DynStringConcatChar(a->rdBuffer, '\0'); + a->readState = 1; /* set 'last char was CR' */ a->state = AsconReadDone; } } + if (a->state == AsconReadDone && a->lineCount > 1) { + if (a->separator != NULL) { + DynStringConcat(a->rdBuffer, a->separator); + } + a->lineCount--; + a->state = AsconReading; + } return 1; /* base handler was already called */ default: break; @@ -488,6 +503,10 @@ int AsconStdInit(Ascon *a, SConnection *con, int argc, char *argv[]) if (argc > 4 && argv[4][0] != '\0') { a->replyTerminator = strdup(argv[4]); } + a->separator = NULL; + if (argc > 5 && argv[5][0] != '\0') { + a->separator = strdup(argv[5]); + } return 1; } @@ -516,6 +535,8 @@ Ascon *AsconMake(SConnection * con, int argc, char *argv[]) a->sendTerminator = NULL; a->hostport = NULL; a->responseValid = 0; + a->readState = 0; + a->lineCount = 1; a->handler = AsconSetHandler(a, con, argc, argv); if (a->handler == NULL) { @@ -545,12 +566,15 @@ void AsconKill(Ascon * a) if (a->hostport) { free(a->hostport); } - if(a->sendTerminator){ + if (a->sendTerminator) { free(a->sendTerminator); } - if(a->replyTerminator){ + if (a->replyTerminator) { free(a->replyTerminator); } + if (a->separator) { + free(a->separator); + } if (a->private != NULL && a->killPrivate != NULL) { a->killPrivate(a->private); } diff --git a/ascon.h b/ascon.h index 4fcf4f19..b1974282 100644 --- a/ascon.h +++ b/ascon.h @@ -84,4 +84,13 @@ char *ConcatArgs(int argc, char *argv[]); */ double DoubleTime(void); +/** \brief emit an error message. The state switches to AsconFailed. + * \param a the connection + * \param msg, a message to be emitted + * \param errorno, for user messages, this should be 0. After + * detection of a system error, eerno may be placed as argument + * for adding strerror(errno) to the message. + */ +void AsconError(Ascon *a, char *msg, int errorno); + #endif diff --git a/ascon.i b/ascon.i index 7ee8b375..97ff1d54 100644 --- a/ascon.i +++ b/ascon.i @@ -48,29 +48,33 @@ typedef enum { typedef int (* AsconHandler)(Ascon *connection); /** Ascon struct - * all members are public, allowing access by handler wrappers + * all fields are public, allowing access by handler wrappers + * the fields marked with (std) are used by the standard handler + * they may get other meanings in a custom handler */ struct Ascon { AsconState state; /**< the current state */ int fd; /**< socket */ - int readState; /**< default implementation: 'was cr' */ + int readState; /**< (std) last char was CR */ pDynString rdBuffer; /**< read buffer */ pDynString wrBuffer; /**< write buffer */ int wrPos; /**< write buffer position */ double timeout; /**< read timeout (sec) */ char *sendTerminator; /**< terminator for sending messages */ - char *replyTerminator; /**< terminator list for reply. NULL is the special case CR, LF or CR/LF */ + char *replyTerminator; /**< (std) terminator list for reply. NULL is the special case CR, LF or CR/LF */ char *hostport; /**< host:port to connect */ pDynString errmsg; /**< error message */ double start; /**< unix time when read was started */ void *private; /**< private data of protocol */ - void (*killPrivate)(void *); /** < kill function for private */ + void (*killPrivate)(void *); /**< kill function for private */ int noResponse; /**< no response expected */ int responseValid; /**< a valid response is ready */ AsconHandler handler; /**< handler function */ double reconnectInterval; /**< reconnect interval */ double lastReconnect; /**< last reconnect try */ char lastChar; /**< last char read */ + char *separator; /**< (std) separator for multiline responses */ + int lineCount; /**< number of lines expected (counting down) */ }; #define ASCON_SELECT_ERROR -1 @@ -104,13 +108,17 @@ int AsconStdHandler(Ascon *a); * \param a the connection * \param con A connection to print errors too. * \param argc number of arguments - * \param argv arguments (":" [sendTerminator] [timeout] [replyTerminators]) + * \param argv arguments (":" [sendTerminator] [timeout] [replyTerminators] [separator]) * sendTerminator is a character or string sent at the end of a command * timeout is in seconds - * replyTerminator is a string, meant as a list of terminator characters - * if not replyTerminator is given, CR, LF or CR/LF all are detected as - * terminators. In this case the terminator is removed from the result, - * where in the case before, the terminator stays in the result. + * replyTerminators is a string, meant as a list of terminator characters + * if no replyTerminator is given, or if it is empty, CR, LF or CR/LF all are detected + * as terminators. If the terminator is CR, LF or CR/LF, it is removed from the result, + * all other terminators are kept in the result. + * separator is used for multiline responses. If this parameter + * is given (and not empty) a command may be followed by a line count in curly brackets, + * indicating that a multiline response is expected. All the lines of the response are + * then returned, separated with "separator" * * In many cases a custom init function may be a wrapper around AsconStdInit */