- implemented multi-line response in AsconStdHandler

This commit is contained in:
zolliker
2009-11-10 07:47:32 +00:00
parent b136700f39
commit b8a0936b99
3 changed files with 75 additions and 34 deletions

70
ascon.c
View File

@ -415,55 +415,70 @@ int AsconStdHandler(Ascon * a)
{ {
int result; int result;
char chr; char chr;
int ret; int ret, l;
char *cmd, *opt;
switch (a->state) { switch (a->state) {
case AsconWriteStart: 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; a->state = AsconWriteDone;
return 1; return 1;
} }
break; /* go to the base handler */ 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: case AsconReading:
if (a->lineCount == 0) {
/* no response expected */
a->state = AsconReadDone;
return 1;
}
result = AsconBaseHandler(a); result = AsconBaseHandler(a);
if (result == 0) if (result == 0)
return 0; return 0;
chr = a->lastChar; chr = a->lastChar;
if (a->replyTerminator != NULL) { if (a->replyTerminator != NULL) {
if (strchr(a->replyTerminator, chr) != NULL) { if (strchr(a->replyTerminator, chr) != NULL) {
DynStringConcatChar(a->rdBuffer, '\0');
a->state = AsconReadDone; a->state = AsconReadDone;
if (chr == '\n' || chr == '\r') {
DynStringBackspace(a->rdBuffer); /* remove LF or CR */
}
} }
} else { } else {
if (chr == '\n') { if (chr == '\n') { /* LF */
DynStringBackspace(a->rdBuffer); /* remove LF */
if (a->readState) { /* last char was CR */ if (a->readState) { /* last char was CR */
/* swallow LF after CR */ /* LF after CR is not a terminator */
a->readState = 0; a->readState = 0;
} else { } else {
DynStringBackspace(a->rdBuffer); /* remove LF */
DynStringConcatChar(a->rdBuffer, '\0');
a->state = AsconReadDone; a->state = AsconReadDone;
} }
} else if (chr == '\r') { } else if (chr == '\r') { /* CR */
a->readState = 1; /* set 'last char was CR' */
DynStringBackspace(a->rdBuffer); /* remove CR */ DynStringBackspace(a->rdBuffer); /* remove CR */
DynStringConcatChar(a->rdBuffer, '\0'); a->readState = 1; /* set 'last char was CR' */
a->state = AsconReadDone; 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 */ return 1; /* base handler was already called */
default: default:
break; break;
@ -488,6 +503,10 @@ int AsconStdInit(Ascon *a, SConnection *con, int argc, char *argv[])
if (argc > 4 && argv[4][0] != '\0') { if (argc > 4 && argv[4][0] != '\0') {
a->replyTerminator = strdup(argv[4]); a->replyTerminator = strdup(argv[4]);
} }
a->separator = NULL;
if (argc > 5 && argv[5][0] != '\0') {
a->separator = strdup(argv[5]);
}
return 1; return 1;
} }
@ -516,6 +535,8 @@ Ascon *AsconMake(SConnection * con, int argc, char *argv[])
a->sendTerminator = NULL; a->sendTerminator = NULL;
a->hostport = NULL; a->hostport = NULL;
a->responseValid = 0; a->responseValid = 0;
a->readState = 0;
a->lineCount = 1;
a->handler = AsconSetHandler(a, con, argc, argv); a->handler = AsconSetHandler(a, con, argc, argv);
if (a->handler == NULL) { if (a->handler == NULL) {
@ -551,6 +572,9 @@ void AsconKill(Ascon * a)
if (a->replyTerminator) { if (a->replyTerminator) {
free(a->replyTerminator); free(a->replyTerminator);
} }
if (a->separator) {
free(a->separator);
}
if (a->private != NULL && a->killPrivate != NULL) { if (a->private != NULL && a->killPrivate != NULL) {
a->killPrivate(a->private); a->killPrivate(a->private);
} }

View File

@ -84,4 +84,13 @@ char *ConcatArgs(int argc, char *argv[]);
*/ */
double DoubleTime(void); 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 #endif

24
ascon.i
View File

@ -48,18 +48,20 @@ typedef enum {
typedef int (* AsconHandler)(Ascon *connection); typedef int (* AsconHandler)(Ascon *connection);
/** Ascon struct /** 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 { struct Ascon {
AsconState state; /**< the current state */ AsconState state; /**< the current state */
int fd; /**< socket */ int fd; /**< socket */
int readState; /**< default implementation: 'was cr' */ int readState; /**< (std) last char was CR */
pDynString rdBuffer; /**< read buffer */ pDynString rdBuffer; /**< read buffer */
pDynString wrBuffer; /**< write buffer */ pDynString wrBuffer; /**< write buffer */
int wrPos; /**< write buffer position */ int wrPos; /**< write buffer position */
double timeout; /**< read timeout (sec) */ double timeout; /**< read timeout (sec) */
char *sendTerminator; /**< terminator for sending messages */ 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 */ char *hostport; /**< host:port to connect */
pDynString errmsg; /**< error message */ pDynString errmsg; /**< error message */
double start; /**< unix time when read was started */ double start; /**< unix time when read was started */
@ -71,6 +73,8 @@ struct Ascon {
double reconnectInterval; /**< reconnect interval */ double reconnectInterval; /**< reconnect interval */
double lastReconnect; /**< last reconnect try */ double lastReconnect; /**< last reconnect try */
char lastChar; /**< last char read */ 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 #define ASCON_SELECT_ERROR -1
@ -104,13 +108,17 @@ int AsconStdHandler(Ascon *a);
* \param a the connection * \param a the connection
* \param con A connection to print errors too. * \param con A connection to print errors too.
* \param argc number of arguments * \param argc number of arguments
* \param argv arguments ("<host>:<port>" [sendTerminator] [timeout] [replyTerminators]) * \param argv arguments ("<host>:<port>" [sendTerminator] [timeout] [replyTerminators] [separator])
* sendTerminator is a character or string sent at the end of a command * sendTerminator is a character or string sent at the end of a command
* timeout is in seconds * timeout is in seconds
* replyTerminator is a string, meant as a list of terminator characters * replyTerminators is a string, meant as a list of terminator characters
* if not replyTerminator is given, CR, LF or CR/LF all are detected as * if no replyTerminator is given, or if it is empty, CR, LF or CR/LF all are detected
* terminators. In this case the terminator is removed from the result, * as terminators. If the terminator is CR, LF or CR/LF, it is removed from the result,
* where in the case before, the terminator stays in 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 * In many cases a custom init function may be a wrapper around AsconStdInit
*/ */