- introduce script context
This commit is contained in:
493
ascon.c
Normal file
493
ascon.c
Normal file
@ -0,0 +1,493 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "sics.h"
|
||||||
|
#include "splitter.h"
|
||||||
|
#include "ascon.i"
|
||||||
|
|
||||||
|
/*
|
||||||
|
CreateSocketAdress stolen from Tcl. Thanks to John Ousterhout
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int CreateSocketAdress(
|
||||||
|
struct sockaddr_in *sockaddrPtr, /* Socket address */
|
||||||
|
char *host, /* Host. NULL implies INADDR_ANY */
|
||||||
|
int port) /* Port number */
|
||||||
|
{
|
||||||
|
struct hostent *hostent; /* Host database entry */
|
||||||
|
struct in_addr addr; /* For 64/32 bit madness */
|
||||||
|
|
||||||
|
(void) memset((char *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
|
||||||
|
sockaddrPtr->sin_family = AF_INET;
|
||||||
|
sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
|
||||||
|
if (host == NULL) {
|
||||||
|
addr.s_addr = INADDR_ANY;
|
||||||
|
} else {
|
||||||
|
hostent = gethostbyname(host);
|
||||||
|
if (hostent != NULL) {
|
||||||
|
memcpy((char *) &addr,
|
||||||
|
(char *) hostent->h_addr_list[0], (size_t) hostent->h_length);
|
||||||
|
} else {
|
||||||
|
addr.s_addr = inet_addr(host);
|
||||||
|
if (addr.s_addr == (unsigned long)-1) {
|
||||||
|
return 0; /* error */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* There is a rumor that this assignment may require care on
|
||||||
|
* some 64 bit machines.
|
||||||
|
*/
|
||||||
|
sockaddrPtr->sin_addr.s_addr = addr.s_addr;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double DoubleTime(void) {
|
||||||
|
struct timeval now;
|
||||||
|
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
return now.tv_sec + now.tv_usec / 1e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AsconError(AsconPtr a, char *msg, int errorno) {
|
||||||
|
static char *stateText[]={
|
||||||
|
"state 0", "kill", "state 2", "notConnected",
|
||||||
|
"connect", "start connect", "connect finished", "connect failed",
|
||||||
|
"write", "start write", "write finished", "write failed",
|
||||||
|
"read", "start read", "read finished", "read failed",
|
||||||
|
"state 16", "state 17", "state 18", "idle"
|
||||||
|
};
|
||||||
|
char *state;
|
||||||
|
|
||||||
|
if (a->state < 0 || a->state > 19) {
|
||||||
|
state = "bad state";
|
||||||
|
} else {
|
||||||
|
state = stateText[a->state];
|
||||||
|
}
|
||||||
|
if (errorno != 0) {
|
||||||
|
ErrPutMsg(&a->errList, NULL, "%s %s (during %s)", msg, strerror(errorno), state);
|
||||||
|
} else {
|
||||||
|
ErrPutMsg(&a->errList, NULL, "%s (during %s)", msg, state);
|
||||||
|
}
|
||||||
|
a->state |= AsconFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AsconConnect(AsconPtr a) {
|
||||||
|
/* input state: AsconConnectStart
|
||||||
|
output state: AsconFailed or AsconConnecting */
|
||||||
|
int ret;
|
||||||
|
struct sockaddr_in adr;
|
||||||
|
char *colon;
|
||||||
|
int port;
|
||||||
|
int oldopts;
|
||||||
|
|
||||||
|
if (a->fd < 0) {
|
||||||
|
a->fd = socket(AF_INET,SOCK_STREAM,0);
|
||||||
|
if (a->fd < 0) {
|
||||||
|
AsconError(a, "socket failed:", errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
colon = strchr(a->hostport, ':');
|
||||||
|
if (colon == NULL) return;
|
||||||
|
port = atoi(colon+1);
|
||||||
|
if (port <= 0) {
|
||||||
|
AsconError(a, "bad port number", 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*colon = '\0';
|
||||||
|
ret = CreateSocketAdress(&adr, a->hostport, port);
|
||||||
|
*colon = ':';
|
||||||
|
if (ret == 0) {
|
||||||
|
AsconError(a, "bad host specification", 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* should we insert the workaround for lantronix server ? see network.c */
|
||||||
|
oldopts = fcntl(a->fd, F_GETFL, 0);
|
||||||
|
fcntl(a->fd, F_SETFL, oldopts | O_NONBLOCK);
|
||||||
|
ret = connect(a->fd, (struct sockaddr *)&adr, sizeof(struct sockaddr_in));
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno != EINPROGRESS) {
|
||||||
|
AsconError(a, "connect failed:", errno);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a->state = AsconConnecting;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsconStdInit(AsconPtr a, char *hostport) {
|
||||||
|
a->fd = -1;
|
||||||
|
a->state = AsconConnectStart;
|
||||||
|
a->timeout = 2.0; /* sec */
|
||||||
|
a->hostport = strdup(hostport);
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsconReadGarbage(int fd) {
|
||||||
|
fd_set rmask;
|
||||||
|
struct timeval tmo = {0,0};
|
||||||
|
int l, ret, result;
|
||||||
|
char garbage[100];
|
||||||
|
|
||||||
|
FD_ZERO(&rmask);
|
||||||
|
result = 0;
|
||||||
|
do {
|
||||||
|
FD_SET(fd, &rmask);
|
||||||
|
ret = select(fd + 1, &rmask, NULL, NULL, &tmo);
|
||||||
|
if (ret > 0) {
|
||||||
|
l = recv(fd, garbage, sizeof garbage, 0);
|
||||||
|
if (l > 0) {
|
||||||
|
/* swallow */
|
||||||
|
result += l;
|
||||||
|
} else if (l == 0) {
|
||||||
|
errno = ECONNRESET;
|
||||||
|
return -2;
|
||||||
|
} else if (l < 0) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (ret > 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintChar(char chr) {
|
||||||
|
if (chr <= 32 || chr >= 127) {
|
||||||
|
printf("%2.2x ", chr);
|
||||||
|
} else {
|
||||||
|
printf(" %c ", chr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsconConnectSuccess(int fd) {
|
||||||
|
fd_set wmask, rmask;
|
||||||
|
struct timeval tmo = {0,0};
|
||||||
|
int oldopts;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
oldopts = fcntl(fd, F_GETFL, 0);
|
||||||
|
assert(oldopts | O_NONBLOCK); /* fd must be in non-blocking mode */
|
||||||
|
|
||||||
|
FD_ZERO(&wmask);
|
||||||
|
FD_ZERO(&rmask);
|
||||||
|
FD_SET(fd, &wmask);
|
||||||
|
FD_SET(fd, &rmask);
|
||||||
|
ret = select(fd + 1, &rmask, &wmask, NULL, &tmo);
|
||||||
|
if (ret > 0) {
|
||||||
|
assert(FD_ISSET(fd, &wmask));
|
||||||
|
if (FD_ISSET(fd, &rmask)) { /* there may already be data for read */
|
||||||
|
if (recv(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */
|
||||||
|
ret = ASCON_RECV_ERROR; /* first recv failed */
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (send(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */
|
||||||
|
ret = ASCON_SEND_ERROR; /* first send failed */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fcntl(fd, F_SETFL, oldopts & ~ O_NONBLOCK); /* reset to blocking mode */
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsconReadChar(int fd, char *chr) {
|
||||||
|
fd_set rmask;
|
||||||
|
struct timeval tmo = {0,0};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
FD_ZERO(&rmask);
|
||||||
|
FD_SET(fd, &rmask);
|
||||||
|
ret = select(fd + 1, &rmask, NULL, NULL, &tmo);
|
||||||
|
if (ret <= 0) return ret;
|
||||||
|
ret = recv(fd, chr, 1, 0);
|
||||||
|
/* PrintChar(*chr); */
|
||||||
|
fflush(stdout);
|
||||||
|
if (ret > 0) return 1;
|
||||||
|
if (ret == 0) {
|
||||||
|
errno = ECONNRESET;
|
||||||
|
return ASCON_DISCONNECTED;
|
||||||
|
}
|
||||||
|
return ASCON_RECV_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsconWriteChars(int fd, char *data, int length) {
|
||||||
|
fd_set wmask;
|
||||||
|
struct timeval tmo = {0,0};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (length <= 0) return 0;
|
||||||
|
/*
|
||||||
|
{ int i;
|
||||||
|
for (i=0; i<length; i++) {
|
||||||
|
PrintChar(data[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("<written\n");
|
||||||
|
*/
|
||||||
|
FD_ZERO(&wmask);
|
||||||
|
FD_SET(fd, &wmask);
|
||||||
|
ret = select(fd + 1, NULL, &wmask, NULL, &tmo);
|
||||||
|
if (ret <= 0) return ASCON_SELECT_ERROR;
|
||||||
|
ret = send(fd, data, length, 0);
|
||||||
|
if (ret > 0) return ret;
|
||||||
|
if (ret == 0) {
|
||||||
|
errno = ECONNRESET;
|
||||||
|
return ASCON_DISCONNECTED;
|
||||||
|
}
|
||||||
|
return ASCON_SEND_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double lastCall = 0;
|
||||||
|
|
||||||
|
int AsconStdHandler(AsconPtr a) {
|
||||||
|
int ret;
|
||||||
|
int l;
|
||||||
|
char chr;
|
||||||
|
double now = DoubleTime();
|
||||||
|
|
||||||
|
if (now > lastCall + 0.5) { /* AsconStdHandler was not called since a long time (0.5 sec) */
|
||||||
|
if (lastCall != 0) { /* extend timeout time (for debugging purposes) */
|
||||||
|
a->start += now - lastCall - 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastCall = now;
|
||||||
|
switch (a->state) {
|
||||||
|
case AsconKillMe: return 0;
|
||||||
|
case AsconConnectStart:
|
||||||
|
AsconConnect(a);
|
||||||
|
break;
|
||||||
|
case AsconConnecting:
|
||||||
|
ret = AsconConnectSuccess(a->fd);
|
||||||
|
if (ret == 0) {
|
||||||
|
/* in progress */
|
||||||
|
} else if (ret > 0) {
|
||||||
|
a->state = AsconConnectDone; /* success */
|
||||||
|
} else if (ret < 0) {
|
||||||
|
AsconError(a, "AsconConnectSuccess failed:", errno);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AsconWriteStart:
|
||||||
|
DynStringConcatChar(a->wrBuffer, '\r');
|
||||||
|
a->wrPos = 0;
|
||||||
|
a->state = AsconWriting;
|
||||||
|
break;
|
||||||
|
case AsconWriting:
|
||||||
|
AsconReadGarbage(a->fd);
|
||||||
|
l = GetDynStringLength(a->wrBuffer) - a->wrPos;
|
||||||
|
ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l);
|
||||||
|
if (ret < 0) {
|
||||||
|
AsconError(a, "send failed:", errno);
|
||||||
|
} else {
|
||||||
|
a->wrPos += ret;
|
||||||
|
if (a->wrPos >= GetDynStringLength(a->wrBuffer)) {
|
||||||
|
a->state = AsconWriteDone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AsconReadStart:
|
||||||
|
DynStringClear(a->rdBuffer);
|
||||||
|
a->start = DoubleTime();
|
||||||
|
a->state = AsconReading;
|
||||||
|
break;
|
||||||
|
case AsconReading:
|
||||||
|
ret = AsconReadChar(a->fd, &chr);
|
||||||
|
while (ret > 0) {
|
||||||
|
a->start = DoubleTime();
|
||||||
|
|
||||||
|
if (chr == '\n') {
|
||||||
|
if (a->readState) {
|
||||||
|
/* swallow LF after CR */
|
||||||
|
DynStringClear(a->rdBuffer);
|
||||||
|
a->readState = 0;
|
||||||
|
} else {
|
||||||
|
DynStringConcatChar(a->rdBuffer, '\0');
|
||||||
|
a->state = AsconReadDone;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (chr == '\r') {
|
||||||
|
a->readState = 1;
|
||||||
|
DynStringConcatChar(a->rdBuffer, '\0');
|
||||||
|
a->state = AsconReadDone;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (DynStringConcatChar(a->rdBuffer, chr) == 0) {
|
||||||
|
AsconError(a, "DynStringConcatChar failed:", ENOMEM);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
a->readState = 0;
|
||||||
|
}
|
||||||
|
ret = AsconReadChar(a->fd, &chr);
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
AsconError(a, "AsconReadChar failed:", errno);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a->state == AsconReadDone) {
|
||||||
|
DynStringConcatChar(a->rdBuffer, '\0');
|
||||||
|
} else {
|
||||||
|
if (a->timeout > 0) {
|
||||||
|
if (DoubleTime() - a->start > a->timeout) {
|
||||||
|
AsconError(a, "read timeout", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* define type AsconProtocolList and functions AsconProtocolAdd etc. */
|
||||||
|
#define MC_NAME(T) AsconProtocol##T
|
||||||
|
#include "mclist.c"
|
||||||
|
|
||||||
|
static AsconProtocolList protocols={0};
|
||||||
|
|
||||||
|
void AsconInsertProtocol(AsconProtocol *protocol) {
|
||||||
|
AsconProtocolAdd(&protocols, protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsconHandler AsconSetHandler(AsconPtr a, int argc, char *argv[]) {
|
||||||
|
AsconProtocol *p;
|
||||||
|
|
||||||
|
if (argc < 1) return NULL;
|
||||||
|
if (strcasecmp(argv[0], "std") == 0) {
|
||||||
|
if (argc != 2) return NULL;
|
||||||
|
AsconStdInit(a, argv[1]);
|
||||||
|
return AsconStdHandler;
|
||||||
|
}
|
||||||
|
for (p = protocols.head; p!= NULL; p=p->next) {
|
||||||
|
if (strcasecmp(p->name, argv[0]) == 0) {
|
||||||
|
p->init(a, argc, argv);
|
||||||
|
return p->handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- implementation of higher level interface ---- */
|
||||||
|
|
||||||
|
char *ConcatArgs(int argc, char *argv[]) {
|
||||||
|
return Arg2Tcl(argc, argv, NULL, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsconPtr AsconMake(SConnection *con, int argc, char *argv[]) {
|
||||||
|
AsconPtr a;
|
||||||
|
char *args;
|
||||||
|
|
||||||
|
a = calloc(1, sizeof(*a));
|
||||||
|
if (a == NULL) {
|
||||||
|
SCWrite(con, "ERROR: no memory", eError);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
a->handler = AsconSetHandler(a, argc, argv);
|
||||||
|
if (a->handler == NULL) {
|
||||||
|
args = ConcatArgs(argc, argv);
|
||||||
|
if (!args) return NULL;
|
||||||
|
SCPrintf(con, eError, "ERROR: illegal protocol: %s", args);
|
||||||
|
free(args);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
a->rdBuffer = CreateDynString(60, 63);
|
||||||
|
a->wrBuffer = CreateDynString(60, 63);
|
||||||
|
a->errList.head = NULL;
|
||||||
|
a->responseValid = 0;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsconKill(AsconPtr a) {
|
||||||
|
a->state = AsconKillMe;
|
||||||
|
a->handler(a);
|
||||||
|
if (a->fd > 0) {
|
||||||
|
close(a->fd);
|
||||||
|
}
|
||||||
|
DeleteDynString(a->rdBuffer);
|
||||||
|
DeleteDynString(a->wrBuffer);
|
||||||
|
if (a->hostport) {
|
||||||
|
free(a->hostport);
|
||||||
|
}
|
||||||
|
free(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsconState AsconTask(AsconPtr a) {
|
||||||
|
a->handler(a);
|
||||||
|
while (1) {
|
||||||
|
switch (a->state) {
|
||||||
|
case AsconReading:
|
||||||
|
case AsconWriting:
|
||||||
|
return AsconPending;
|
||||||
|
case AsconNotConnected:
|
||||||
|
return AsconOffline;
|
||||||
|
break;
|
||||||
|
case AsconConnectDone:
|
||||||
|
a->state = AsconIdle;
|
||||||
|
return AsconReady;
|
||||||
|
case AsconWriteDone:
|
||||||
|
if (a->noResponse) {
|
||||||
|
return AsconReady;
|
||||||
|
}
|
||||||
|
a->state = AsconReadStart;
|
||||||
|
break;
|
||||||
|
case AsconReadDone:
|
||||||
|
a->state = AsconIdle;
|
||||||
|
a->responseValid = 1;
|
||||||
|
return AsconReady;
|
||||||
|
case AsconConnecting:
|
||||||
|
return AsconUnconnected;
|
||||||
|
default:
|
||||||
|
switch (a->state % 4) {
|
||||||
|
case AsconOnTheWay:
|
||||||
|
case AsconStart:
|
||||||
|
return AsconPending;
|
||||||
|
case AsconFailed:
|
||||||
|
if (a->state <= AsconConnectFailed) {
|
||||||
|
return AsconUnconnected;
|
||||||
|
}
|
||||||
|
return AsconFailure;
|
||||||
|
case AsconFinished:
|
||||||
|
if (a->state < AsconConnectFailed) {
|
||||||
|
return AsconUnconnected;
|
||||||
|
}
|
||||||
|
return AsconReady;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a->handler(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsconWrite(AsconPtr a, char *command, int noResponse) {
|
||||||
|
if (a->state <= AsconConnectFailed || a->state % 4 < AsconFinished) return 0;
|
||||||
|
DynStringCopy(a->wrBuffer, command);
|
||||||
|
a->noResponse = noResponse;
|
||||||
|
a->state = AsconWriteStart;
|
||||||
|
a->responseValid = 0;
|
||||||
|
AsconTask(a);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *AsconRead(AsconPtr a) {
|
||||||
|
if (a->noResponse) {
|
||||||
|
a->noResponse=0;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (a->state % 4 == AsconFailed) {
|
||||||
|
a->state = AsconIdle;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (a->responseValid) {
|
||||||
|
a->responseValid = 0;
|
||||||
|
return GetCharArray(a->rdBuffer);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrMsgList *AsconGetErrList(AsconPtr a) {
|
||||||
|
return &a->errList;
|
||||||
|
}
|
81
ascon.h
Normal file
81
ascon.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#ifndef ASCON_H
|
||||||
|
#define ASCON_H
|
||||||
|
|
||||||
|
#include "errormsg.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \brief Asynchronous connection handling for devices controlled over tcp-ip
|
||||||
|
* connections. Interface for higher level modules.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \brief the asynchronous connection
|
||||||
|
*/
|
||||||
|
typedef struct Ascon *AsconPtr;
|
||||||
|
|
||||||
|
/** \brief the possible results of AsconTask
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
AsconOffline,
|
||||||
|
AsconUnconnected,
|
||||||
|
AsconPending,
|
||||||
|
AsconReady,
|
||||||
|
AsconFailure
|
||||||
|
} AsconStatus;
|
||||||
|
|
||||||
|
/** \brief make a new asynchronous connection
|
||||||
|
* \param con the SICS connection
|
||||||
|
* \param argc number of arguments
|
||||||
|
* \param argv the arguments. argv[0] must be the protocol name, the other arguments
|
||||||
|
* are protocol specific, but argv[1] is usually host::port
|
||||||
|
* \return the created connection or NULL on failure
|
||||||
|
*/
|
||||||
|
AsconPtr AsconMake(SConnection *con, int argc, char *argv[]);
|
||||||
|
|
||||||
|
/** \brief kill function
|
||||||
|
* \param a the connection to be killed
|
||||||
|
*/
|
||||||
|
void AsconKill(AsconPtr a);
|
||||||
|
|
||||||
|
/** \brief the task handler. To be called repeatedly.
|
||||||
|
* \param a the connection
|
||||||
|
* \return the state of the connection
|
||||||
|
*/
|
||||||
|
AsconStatus AsconTask(AsconPtr a);
|
||||||
|
|
||||||
|
/** \brief write to the connection. allowed only when the state is ascon_ready
|
||||||
|
* \param a the connection
|
||||||
|
* \param command the command to be sent
|
||||||
|
* \param noResponse 0 normally, 1 if no reponse is expected
|
||||||
|
* \return 1 on success, 0 when not ready
|
||||||
|
*/
|
||||||
|
int AsconWrite(AsconPtr a, char *command, int noResponse);
|
||||||
|
|
||||||
|
/** \brief read from the connection. allowed only when a response is available
|
||||||
|
* \param a the connection
|
||||||
|
* \return the response when a response is ready
|
||||||
|
* NULL when the command has not completed and the response is not yet finished
|
||||||
|
* "" when the command has completed, but no response was expected.
|
||||||
|
* The result is only valid until the next call to other AsconXxx functions
|
||||||
|
* and has to be duplicated if needed later.
|
||||||
|
*/
|
||||||
|
char *AsconRead(AsconPtr a);
|
||||||
|
|
||||||
|
/** \brief get the connections error list
|
||||||
|
* \return the error list
|
||||||
|
*/
|
||||||
|
ErrMsgList *AsconGetErrList(AsconPtr a);
|
||||||
|
|
||||||
|
/** \brief a helper function
|
||||||
|
* \param argc the number of args
|
||||||
|
* \param argv the args to be concatenated
|
||||||
|
* \result a allocated string containing the concatenated arguments
|
||||||
|
* the args are properly quoted to be used as tcl proc arguments
|
||||||
|
*/
|
||||||
|
char *ConcatArgs(int argc, char *argv[]);
|
||||||
|
|
||||||
|
/** \brief function for dealing with times with musec resolution
|
||||||
|
* \return absolute time as double value
|
||||||
|
*/
|
||||||
|
double DoubleTime(void);
|
||||||
|
|
||||||
|
#endif
|
143
ascon.i
Normal file
143
ascon.i
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#ifndef ASCON_I
|
||||||
|
#define ASCON_I
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include "ascon.h"
|
||||||
|
#include "dynstring.h"
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \brief Asynchronous connection handling for devices controlled over tcp-ip
|
||||||
|
* connections. Interface for the implementation of custom protocols.
|
||||||
|
*
|
||||||
|
* For the implmentation of a custom protocol, hou have to implement
|
||||||
|
* the handler function and the init function, declare the protocol
|
||||||
|
* of type AsconProtocol and call AsconInsertProtocol on startup.
|
||||||
|
* The handler and init functions are normally be a wrapper around AsconStdHandler
|
||||||
|
* and AsconStdInit
|
||||||
|
*
|
||||||
|
* The functions with fd as the first argument are utility functions with
|
||||||
|
* may be used in handler wrapper functions.
|
||||||
|
* On error, the return value may be one of the defined macros ASCON_xxx,
|
||||||
|
* and errno will give more details about the error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sub-state of the connection. Only states with sub-state AsconStart may
|
||||||
|
* be set by the caller, and only when the sub-state is not AsconOnTheWay
|
||||||
|
*/
|
||||||
|
typedef enum { AsconOnTheWay=0, AsconStart=1, AsconFinished=2, AsconFailed=3 } AsconMode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of the connection. The sub-state is state % 4.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
AsconNotConnected=0+AsconFinished,
|
||||||
|
AsconKillMe=0+AsconStart,
|
||||||
|
AsconConnecting=4+AsconOnTheWay,
|
||||||
|
AsconConnectStart=AsconConnecting+AsconStart,
|
||||||
|
AsconConnectDone=AsconConnecting+AsconFinished,
|
||||||
|
AsconConnectFailed=AsconConnecting+AsconFailed,
|
||||||
|
AsconWriting=8+AsconOnTheWay,
|
||||||
|
AsconWriteStart=AsconWriting+AsconStart,
|
||||||
|
AsconWriteDone=AsconWriting+AsconFinished,
|
||||||
|
AsconReading=12+AsconOnTheWay,
|
||||||
|
AsconReadStart=AsconReading+AsconStart,
|
||||||
|
AsconReadDone=AsconReading+AsconFinished,
|
||||||
|
AsconIdle=16+AsconFinished
|
||||||
|
} AsconState;
|
||||||
|
|
||||||
|
/** \brief the task handler function prototype
|
||||||
|
*
|
||||||
|
* custom handlers must have this prototype
|
||||||
|
*/
|
||||||
|
typedef int (* AsconHandler)(AsconPtr connection);
|
||||||
|
|
||||||
|
/** Ascon struct
|
||||||
|
* all members are public, allowing access by handler wrappers
|
||||||
|
*/
|
||||||
|
typedef struct Ascon {
|
||||||
|
AsconState state; /**< the current state */
|
||||||
|
int fd; /**< socket */
|
||||||
|
int readState; /**< default implementation: 'was cr' */
|
||||||
|
pDynString rdBuffer;/**< read buffer */
|
||||||
|
pDynString wrBuffer;/**< write buffer */
|
||||||
|
int wrPos; /**< write buffer position */
|
||||||
|
float timeout; /**< read timeout (sec) */
|
||||||
|
char *hostport; /**< host:port to connect */
|
||||||
|
ErrMsgList errList; /**< error message list */
|
||||||
|
double start; /**< unix time when read was started */
|
||||||
|
void *private; /**< private data of protocol */
|
||||||
|
int noResponse; /**< no response expected */
|
||||||
|
int responseValid; /**< a valid response is ready */
|
||||||
|
AsconHandler handler; /**< handler function */
|
||||||
|
} Ascon;
|
||||||
|
|
||||||
|
#define ASCON_SELECT_ERROR -1
|
||||||
|
#define ASCON_RECV_ERROR -2
|
||||||
|
#define ASCON_SEND_ERROR -3
|
||||||
|
#define ASCON_DISCONNECTED -4
|
||||||
|
|
||||||
|
/** \brief the standard handler routine.
|
||||||
|
* \param a the connection
|
||||||
|
* \return 0 when task has finished (connection to be closed), 1 when active
|
||||||
|
*
|
||||||
|
* In most cases a custom handler may be a wrapper around AsconStdHandler
|
||||||
|
*/
|
||||||
|
int AsconStdHandler(AsconPtr a);
|
||||||
|
|
||||||
|
/** \brief initialize a standard connection
|
||||||
|
* \param a the connection
|
||||||
|
* \param hostport the tcp/ip address (syntax: host:port)
|
||||||
|
*
|
||||||
|
* In most cases a custom init function may be a wrapper around AsconStdInit
|
||||||
|
*/
|
||||||
|
void AsconStdInit(AsconPtr a, char *hostport);
|
||||||
|
|
||||||
|
/** The Ascon Protocol
|
||||||
|
*/
|
||||||
|
typedef struct AsconProtocol {
|
||||||
|
struct AsconProtocol *next;
|
||||||
|
char *name;
|
||||||
|
AsconHandler handler;
|
||||||
|
void (*init)(Ascon *s, int argc, char *argv[]);
|
||||||
|
} AsconProtocol;
|
||||||
|
|
||||||
|
/** \brief Insert a new protocol into the protocol list
|
||||||
|
* protocol the protocol (must be allocated by the caller, may be statically)
|
||||||
|
*/
|
||||||
|
void AsconInsertProtocol(AsconProtocol *protocol);
|
||||||
|
|
||||||
|
/** \brief close the connection and free internal used memory
|
||||||
|
* \param a the connection to be closed
|
||||||
|
* remark: the connection struct itself has to be freed manually
|
||||||
|
*/
|
||||||
|
void AsconClose(AsconPtr a);
|
||||||
|
|
||||||
|
/** \brief swallow garbage (utility function)
|
||||||
|
* \param fd the socket
|
||||||
|
* \return >=0: number of chars swallowed, else error
|
||||||
|
*/
|
||||||
|
int AsconReadGarbage(int fd);
|
||||||
|
|
||||||
|
/** \brief check if a connection has succeded (utility function)
|
||||||
|
* \param fd the socket
|
||||||
|
* \return 1: connection succesful, 0: connection in progress, <0: error
|
||||||
|
*/
|
||||||
|
int AsconConnectSuccess(int fd);
|
||||||
|
|
||||||
|
/** \brief read one character, if available (utility function)
|
||||||
|
* \param fd the socket
|
||||||
|
* \param chr the result
|
||||||
|
* \return 1: succes, 0: no data available, <0: error
|
||||||
|
*/
|
||||||
|
int AsconReadChar(int fd, char *chr);
|
||||||
|
|
||||||
|
/** \brief non blocking write (utility function)
|
||||||
|
* \param fd the socket
|
||||||
|
* \param data the data (not nul-terminated, may contain nul)
|
||||||
|
* \param length the length of the data
|
||||||
|
* \return >0: number of written chars,0: write not yet possible, <0: error
|
||||||
|
*/
|
||||||
|
int AsconWriteChars(int fd, char *data, int length);
|
||||||
|
|
||||||
|
#endif
|
63
errormsg.c
Normal file
63
errormsg.c
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "errormsg.h"
|
||||||
|
|
||||||
|
/* ErrMsgList implementation */
|
||||||
|
#define MC_NAME(T) ErrMsg##T
|
||||||
|
#define MC_IMPLEMENTATION
|
||||||
|
#include "mclist.c"
|
||||||
|
|
||||||
|
ErrMsg *ErrPutMsg(ErrMsgList *dump, char *data, char *fmt, ...) {
|
||||||
|
ErrMsg *m, *p;
|
||||||
|
va_list ap;
|
||||||
|
char buf[256];
|
||||||
|
char *text;
|
||||||
|
int l;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
l = vsnprintf(buf, sizeof buf, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (l < sizeof buf) {
|
||||||
|
text = buf;
|
||||||
|
} else {
|
||||||
|
/* assuming we have a C99 conforming snprintf and need a larger buffer */
|
||||||
|
text = calloc(l, 1);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(text, l, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
m = NULL;
|
||||||
|
for (p = ErrMsgFirst(dump); p!= NULL; p = ErrMsgNext(dump)) {
|
||||||
|
if (strcmp(text, p->text) == 0) {
|
||||||
|
m = ErrMsgTake(dump);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m == NULL) {
|
||||||
|
m = calloc(1, sizeof(*m));
|
||||||
|
if (text == buf) {
|
||||||
|
m->text = strdup(text);
|
||||||
|
} else {
|
||||||
|
m->text = text;
|
||||||
|
}
|
||||||
|
m->data = NULL;
|
||||||
|
m->cnt = 1;
|
||||||
|
} else {
|
||||||
|
if (text != buf) free(text);
|
||||||
|
m->cnt++;
|
||||||
|
}
|
||||||
|
if (m->data) {
|
||||||
|
free(m->data);
|
||||||
|
m->data = NULL;
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
m->data = strdup(data);
|
||||||
|
}
|
||||||
|
ErrMsgFirst(dump);
|
||||||
|
ErrMsgInsert(dump, m);
|
||||||
|
time(&m->last);
|
||||||
|
return m;
|
||||||
|
}
|
30
errormsg.h
Normal file
30
errormsg.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef ERRORMSG_H
|
||||||
|
#define ERRORMSG_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
* \brief Error message collection
|
||||||
|
*/
|
||||||
|
/** \brief Error message item
|
||||||
|
*/
|
||||||
|
typedef struct ErrMsg {
|
||||||
|
struct ErrMsg *next;
|
||||||
|
char *text; /**< the message text */
|
||||||
|
char *data; /**< additional text which may be different for the same message */
|
||||||
|
int cnt; /**< count */
|
||||||
|
time_t last; /**< time of last message */
|
||||||
|
} ErrMsg;
|
||||||
|
|
||||||
|
/* define type ErrMsgList and functions ErrMsgAdd etc. */
|
||||||
|
#define MC_NAME(T) ErrMsg##T
|
||||||
|
#include "mclist.h"
|
||||||
|
|
||||||
|
/** \brief Put a formatted message to the error message list
|
||||||
|
* \param dump the error message list
|
||||||
|
* \param data some additional text (may be NULL)
|
||||||
|
* \param fmt the format for the message
|
||||||
|
*/
|
||||||
|
ErrMsg *ErrPutMsg(ErrMsgList *dump, char *data, char *fmt, ...);
|
||||||
|
|
||||||
|
#endif
|
1
make_gen
1
make_gen
@ -31,6 +31,7 @@ SOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \
|
|||||||
hmdata.o nxscript.o tclintimpl.o sicsdata.o mcstascounter.o \
|
hmdata.o nxscript.o tclintimpl.o sicsdata.o mcstascounter.o \
|
||||||
mcstashm.o initializer.o remob.o tclmotdriv.o protocol.o \
|
mcstashm.o initializer.o remob.o tclmotdriv.o protocol.o \
|
||||||
sinfox.o sicslist.o cone.o hipadaba.o sicshipadaba.o statistics.o \
|
sinfox.o sicslist.o cone.o hipadaba.o sicshipadaba.o statistics.o \
|
||||||
|
ascon.o errormsg.o scriptcontext.o \
|
||||||
moregress.o hdbcommand.o multicounter.o regresscter.o histregress.o \
|
moregress.o hdbcommand.o multicounter.o regresscter.o histregress.o \
|
||||||
sicshdbadapter.o polldriv.o sicspoll.o statemon.o hmslave.o \
|
sicshdbadapter.o polldriv.o sicspoll.o statemon.o hmslave.o \
|
||||||
nwatch.o asyncqueue.o asyncprotocol.o sicsobj.o hdbqueue.o\
|
nwatch.o asyncqueue.o asyncprotocol.o sicsobj.o hdbqueue.o\
|
||||||
|
116
mclist.c
Normal file
116
mclist.c
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
#ifndef MC_List_TYPE
|
||||||
|
#define MC_List_TYPE MC_NAME(List)
|
||||||
|
#define MC_First_FUN MC_NAME(First)
|
||||||
|
#define MC_This_FUN MC_NAME(This)
|
||||||
|
#define MC_Next_FUN MC_NAME(Next)
|
||||||
|
#define MC_End_FUN MC_NAME(End)
|
||||||
|
#define MC_Insert_FUN MC_NAME(Insert)
|
||||||
|
#define MC_Add_FUN MC_NAME(Add)
|
||||||
|
#define MC_Take_FUN MC_NAME(Take)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MC_IMPLEMENTATION
|
||||||
|
|
||||||
|
#undef MC_IMPLEMENTATION
|
||||||
|
#ifndef MC_TYPE
|
||||||
|
#define MC_TYPE MC_NAME()*
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define MC_DO_NOT_UNDEF
|
||||||
|
#include "mclist.h"
|
||||||
|
#undef MC_DO_NOT_UNDEF
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MC_NEXT
|
||||||
|
#define MC_NEXT next
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MC_TYPE MC_First_FUN(MC_List_TYPE *list) {
|
||||||
|
list->ptr = &list->head;
|
||||||
|
return list->head;
|
||||||
|
}
|
||||||
|
|
||||||
|
MC_TYPE MC_This_FUN(MC_List_TYPE *list) {
|
||||||
|
if (list->head == NULL) {
|
||||||
|
list->ptr = &list->head;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return *list->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MC_TYPE MC_Next_FUN(MC_List_TYPE *list) {
|
||||||
|
MC_TYPE node;
|
||||||
|
if (list->head == NULL) {
|
||||||
|
list->ptr = &list->head;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
node = *list->ptr;
|
||||||
|
if (node) {
|
||||||
|
list->ptr = &node->MC_NEXT;
|
||||||
|
}
|
||||||
|
return *list->ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MC_End_FUN(MC_List_TYPE *list) {
|
||||||
|
MC_TYPE node;
|
||||||
|
if (list->head == NULL) {
|
||||||
|
list->ptr = &list->head;
|
||||||
|
}
|
||||||
|
node = *list->ptr;
|
||||||
|
if (node) {
|
||||||
|
while (node->MC_NEXT != NULL) {
|
||||||
|
node = node->MC_NEXT;
|
||||||
|
}
|
||||||
|
list->ptr = &node->MC_NEXT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MC_Insert_FUN(MC_List_TYPE *list, MC_TYPE node) {
|
||||||
|
if (list->head == NULL) {
|
||||||
|
list->ptr = &list->head;
|
||||||
|
}
|
||||||
|
node->MC_NEXT = *list->ptr;
|
||||||
|
*list->ptr = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MC_Add_FUN(MC_List_TYPE *list, MC_TYPE node) {
|
||||||
|
node->MC_NEXT = NULL;
|
||||||
|
if (list->head == NULL) {
|
||||||
|
list->head = node;
|
||||||
|
list->ptr = &list->head;
|
||||||
|
} else {
|
||||||
|
if (*list->ptr != NULL) {
|
||||||
|
MC_End_FUN(list);
|
||||||
|
}
|
||||||
|
*list->ptr = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MC_TYPE MC_Take_FUN(MC_List_TYPE *list) {
|
||||||
|
MC_TYPE node;
|
||||||
|
node = *list->ptr;
|
||||||
|
if (node != NULL) {
|
||||||
|
*list->ptr = node->MC_NEXT;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MC_Delete_FUN(MC_List_TYPE *list, void (*deleteFunc)(MC_TYPE n)) {
|
||||||
|
MC_TYPE node;
|
||||||
|
MC_TYPE victim;
|
||||||
|
node = list->head;
|
||||||
|
while (node != NULL) {
|
||||||
|
victim = node;
|
||||||
|
node = node->next;
|
||||||
|
deleteFunc(victim);
|
||||||
|
}
|
||||||
|
list->head = NULL;
|
||||||
|
list->ptr = &list->head;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef MC_NAME
|
||||||
|
#undef MC_TYPE
|
||||||
|
#undef MC_NEXT
|
177
mclist.h
Normal file
177
mclist.h
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
/** \file
|
||||||
|
* \brief Type safe list handling
|
||||||
|
*
|
||||||
|
* The definition and implementation make use of macros extensively to create
|
||||||
|
* a list type and related functions for any node type.
|
||||||
|
* <b>However, accessing the list does not use macros.</b>
|
||||||
|
* The list is implemented as a singly linked list. Sequential appending
|
||||||
|
* to the tail is fast, because the list structure contains
|
||||||
|
* a pointer to the anchor of the last accessed node.
|
||||||
|
*
|
||||||
|
* For a local list, mclist.c must be included after
|
||||||
|
* the declaration of the node type and after defining the macro MC_NAME
|
||||||
|
* and optionally MC_TYPE and MC_NEXT.
|
||||||
|
*
|
||||||
|
* For a public list, in the header file mclist.h must be included after
|
||||||
|
* the declaration of the node type and after defining the macro MC_NAME
|
||||||
|
* and optionally MC_TYPE. In the implementation mclist.c
|
||||||
|
* must be included after defining the macro MC_NAME and MC_IMPLEMENTATION
|
||||||
|
* and optionally MC_TYPE and MC_NEXT.
|
||||||
|
*
|
||||||
|
* MC_NAME has one parameter and describes how to combine the list name
|
||||||
|
* with the function names.
|
||||||
|
*
|
||||||
|
* MC_TYPE defines the node type. If undeclared it defaults to a pointer
|
||||||
|
* to the name.
|
||||||
|
*
|
||||||
|
* MC_NEXT indicates the name of the link. It defaults to 'next'.
|
||||||
|
*
|
||||||
|
* MC_IMPLEMENTATION has no value and must be defined when the list type
|
||||||
|
* was already declared. Typically this is done in a header file including mclist.h.
|
||||||
|
*
|
||||||
|
* The macros MC_NAME, MC_TYPE, MC_NEXT and MC_IMPLEMENTATION are undefined
|
||||||
|
* within mclist.c and mclist.h and must be redefined for every list.
|
||||||
|
*
|
||||||
|
* \par Usage example
|
||||||
|
* \code
|
||||||
|
* // declare the Node type
|
||||||
|
* typedef struct Node {
|
||||||
|
* struct Node *next;
|
||||||
|
* char *name;
|
||||||
|
* } Node;
|
||||||
|
*
|
||||||
|
* // this declaration leads to a list type 'NodeList' and fucntions names 'Node<fun>'
|
||||||
|
* #define MC_NAME(T) Node##T
|
||||||
|
* // the following line is not needed as 'next' is the default for the link
|
||||||
|
* #define MC_NEXT next
|
||||||
|
* // the following line is not needed as 'Node *' is the default for the type in this case
|
||||||
|
* #define MC_TYPE Node *
|
||||||
|
* // inside mclist.c, the list type is declared and the related functions are implemented
|
||||||
|
* #include "mclist.c"
|
||||||
|
*
|
||||||
|
* int main(void) {
|
||||||
|
* // declare and init the list
|
||||||
|
* NodeList list={NULL};
|
||||||
|
*
|
||||||
|
* // create a node
|
||||||
|
* Node *node;
|
||||||
|
* node = malloc(sizeof(*node));
|
||||||
|
* node->name = "First";
|
||||||
|
*
|
||||||
|
* // add node at the end of the list
|
||||||
|
* NodeAdd(&list, node);
|
||||||
|
*
|
||||||
|
* // print the names of all list nodes
|
||||||
|
* for (node = NodeFirst(&list); node != NULL; node = NodeNext(&list)) {
|
||||||
|
* printf("%s\n", node->name);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // alternative form not touching the list position
|
||||||
|
* // only for the case, where no insert or take function is used inside the loop
|
||||||
|
* for (node = list.head; node != NULL; node = node->next) {
|
||||||
|
* printf("%s\n", node->name);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // remove the node with the name "First"
|
||||||
|
* for (node = NodeFirst(&list); node != NULL; node = NodeNext(&list)) {
|
||||||
|
* if (strcmp(node->name, "First") == 0) {
|
||||||
|
* free(NodeTake(&list));
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*/
|
||||||
|
#ifndef MC_TYPE
|
||||||
|
/** \brief default node type
|
||||||
|
*/
|
||||||
|
#define MC_TYPE MC_NAME()*
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MC_List_TYPE
|
||||||
|
#define MC_List_TYPE MC_NAME(List)
|
||||||
|
#define MC_First_FUN MC_NAME(First)
|
||||||
|
#define MC_This_FUN MC_NAME(This)
|
||||||
|
#define MC_Next_FUN MC_NAME(Next)
|
||||||
|
#define MC_End_FUN MC_NAME(End)
|
||||||
|
#define MC_Insert_FUN MC_NAME(Insert)
|
||||||
|
#define MC_Add_FUN MC_NAME(Add)
|
||||||
|
#define MC_Take_FUN MC_NAME(Take)
|
||||||
|
#define MC_Delete_FUN MC_NAME(Delete)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct MC_List_TYPE {
|
||||||
|
MC_TYPE head;
|
||||||
|
MC_TYPE *ptr;
|
||||||
|
} MC_List_TYPE;
|
||||||
|
|
||||||
|
/** \brief move to first node and get it
|
||||||
|
* \param list the list
|
||||||
|
* \return the node or NULL when the list is empty
|
||||||
|
*
|
||||||
|
* Actual position on return: at the first node
|
||||||
|
*/
|
||||||
|
MC_TYPE MC_First_FUN(MC_List_TYPE *list);
|
||||||
|
|
||||||
|
/** \brief get the node at the current position
|
||||||
|
* \param list the list
|
||||||
|
* \return the node or NULL when the list is empty or the position is at end
|
||||||
|
*
|
||||||
|
* Actual position on return: not changed (= at the returned node)
|
||||||
|
*/
|
||||||
|
MC_TYPE MC_This_FUN(MC_List_TYPE *list);
|
||||||
|
|
||||||
|
/** \brief get the node after the current node
|
||||||
|
* \param list the list
|
||||||
|
* \return the node or NULL when the list is empty or the position is at end
|
||||||
|
*
|
||||||
|
* Actual position on return: incremented (= at the returned node or at end)
|
||||||
|
*/
|
||||||
|
MC_TYPE MC_Next_FUN(MC_List_TYPE *list);
|
||||||
|
|
||||||
|
/** \brief move the position to the end
|
||||||
|
* \param list the list
|
||||||
|
*
|
||||||
|
* Actual position on return: at end
|
||||||
|
*/
|
||||||
|
void MC_End_FUN(MC_List_TYPE *list);
|
||||||
|
|
||||||
|
/** \brief insert at the current position, i.e. before the current node
|
||||||
|
* \param list the list
|
||||||
|
* \param node the node to be inserted
|
||||||
|
*
|
||||||
|
* Actual position on return: at the inserted node
|
||||||
|
*/
|
||||||
|
void MC_Insert_FUN(MC_List_TYPE *list, MC_TYPE node);
|
||||||
|
|
||||||
|
/** \brief add at the end of the list
|
||||||
|
* \param list the list
|
||||||
|
* \param node the node to be added
|
||||||
|
*
|
||||||
|
* Actual position on return: at the inserted node (before the last node, not at end!)
|
||||||
|
*/
|
||||||
|
void MC_Add_FUN(MC_List_TYPE *list, MC_TYPE node);
|
||||||
|
|
||||||
|
/** \brief remove the node at the current position
|
||||||
|
* \param list the list
|
||||||
|
* \return the removed node or NULL when the list is empty or the position is at end
|
||||||
|
*
|
||||||
|
* Actual position on return: after the taken node
|
||||||
|
*
|
||||||
|
* Note: it is the responsibility of the caller to free the node if it is not used
|
||||||
|
* anymore
|
||||||
|
*/
|
||||||
|
MC_TYPE MC_Take_FUN(MC_List_TYPE *list);
|
||||||
|
|
||||||
|
/** \brief remove and delete all nodes
|
||||||
|
* \param list the list
|
||||||
|
* \param deleteFunc the kill function of the node
|
||||||
|
*
|
||||||
|
* Calls the kill function for every node. The list is
|
||||||
|
* empty on return.
|
||||||
|
*/
|
||||||
|
void MC_Delete_FUN(MC_List_TYPE *list, void (*deleteFunc)(MC_TYPE node));
|
||||||
|
|
||||||
|
#ifndef MC_DO_NOT_UNDEF
|
||||||
|
#undef MC_NAME
|
||||||
|
#undef MC_TYPE
|
||||||
|
#endif
|
1
ofac.c
1
ofac.c
@ -437,6 +437,7 @@
|
|||||||
|
|
||||||
INIT(SiteInit); /* site specific initializations */
|
INIT(SiteInit); /* site specific initializations */
|
||||||
INIT(StatisticsInit);
|
INIT(StatisticsInit);
|
||||||
|
INIT(SctStartup);
|
||||||
|
|
||||||
}
|
}
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
|
1458
scriptcontext.c
Normal file
1458
scriptcontext.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user