- implemented multi-line response in AsconStdHandler
This commit is contained in:
70
ascon.c
70
ascon.c
@ -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);
|
||||||
}
|
}
|
||||||
|
9
ascon.h
9
ascon.h
@ -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
24
ascon.i
@ -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
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user