diff --git a/SICSmain.c b/SICSmain.c
index ae5bdf88..d69ced7e 100644
--- a/SICSmain.c
+++ b/SICSmain.c
@@ -20,19 +20,25 @@
#include
#include
#include
+#include
#include "nserver.h"
#include "servlog.h"
extern void KeepStartupCommands(); /* ofac.c */
+
/***************************** Necessary Globals ****************************/
-IPair *pSICSOptions = NULL;
-pServer pServ = NULL;
-
-/* ========================= Less dreadful file statics =================== */
#define DEFAULTINIFILE "servo.tcl"
+int usage(const char *name)
+{
+ fprintf(stderr, "usage: %s [-d] [-nolog] \n", name);
+ fprintf(stderr, " -d: daemonize the process\n");
+ fprintf(stderr, " -nolog: disable log file writing\n");
+ return 1;
+}
+
/*---------------------------------------------------------------------------
The Servers Main program. May take one argument: the name of an
initialisation file
@@ -41,25 +47,54 @@ pServer pServ = NULL;
int main(int argc, char *argv[])
{
int iRet;
+ int daemonize = 0;
char *file = NULL;
- int i;
+ int i, firstArg = 1;
+ extern pServer pServ;
+ char *argv0;
+
+ argv0 = argv[0];
+ if (argc < 2)
+ return usage(argv[0]);
/* initialise, will die on you if problems */
- for (i = 1; i < argc; i++) {
- if (strcmp(argv[i], "-nolog") == 0) {
- SICSLogEnable(0);
- }else if(strcmp(argv[i],"-keepstartup") == 0){
- KeepStartupCommands();
+
+ for (i = firstArg; i < argc; i++) {
+ if (argv[i][0] == '-') {
+ if (strcasecmp(argv[i], "-nolog") == 0) {
+ SICSLogEnable(0);
+ }else if(strcmp(argv[i],"-keepstartup") == 0){
+ KeepStartupCommands();
+#ifdef SITE_ANSTO
+ } else if (strcasecmp(argv[i], "-v") == 0) {
+ extern void SiteReportVersion(void);
+ SiteReportVersion();
+ return 0;
+#endif
+ } else if (strcasecmp(argv[i], "-d") == 0) {
+ daemonize = 1;
+ } else {
+ fprintf(stderr, "Unrecognized option ignored: %s\n", argv[i]);
+ }
} else if (file == NULL) {
file = argv[i];
+ } else {
+ fprintf(stderr, "Unrecognized argument ignored: %s\n", argv[i]);
}
}
+
+ if (file == NULL)
+ return usage(argv[0]);
+
iRet = InitServer(file, &pServ);
if (!iRet) {
printf("Unrecoverable error on server startup, exiting.........\n");
exit(1);
}
-
+ /*
+ if (daemonize == 1)
+ daemon(1, 1);
+*/
RunServer(pServ);
@@ -68,28 +103,3 @@ int main(int argc, char *argv[])
exit(0);
}
-/*--------------------------------------------------------------------------*/
-SicsInterp *GetInterpreter(void)
-{
- return pServ->pSics;
-}
-
-/*--------------------------------------------------------------------------*/
-pExeList GetExecutor(void)
-{
- return pServ->pExecutor;
-}
-
-/*------------------------------------------------------------------------*/
-void StopExit(void)
-{
- if (pServ) {
- StopServer(pServ);
- }
-}
-
-/*-------------------------------------------------------------------------*/
-pTaskMan GetTasker(void)
-{
- return pServ->pTasker;
-}
diff --git a/Scommon.h b/Scommon.h
index 4d356e7e..7c4045d1 100644
--- a/Scommon.h
+++ b/Scommon.h
@@ -57,6 +57,10 @@ typedef enum {
eLogError /* error message to log: is always written to client */
} OutCode;
+const char* OutCodeToTxt(OutCode outcode);
+const char* OutCodeToText(OutCode outcode);
+int OutCodeFromText(const char *text, OutCode *outcode);
+
#include "interrupt.h"
/* define some user rights codes */
diff --git a/ascon.c b/ascon.c
index ead3be90..1eb23b90 100644
--- a/ascon.c
+++ b/ascon.c
@@ -84,6 +84,8 @@ void AsconError(Ascon *a, char *msg, int errorno)
DynStringConcat(a->errmsg, " (");
DynStringConcat(a->errmsg, state);
DynStringConcat(a->errmsg, " state)");
+ DynStringConcat(a->errmsg, " on ");
+ DynStringConcat(a->errmsg, a->hostport);
} else {
DynStringConcat(a->errmsg, strerror(errorno));
DynStringConcat(a->errmsg, " (");
@@ -91,6 +93,8 @@ void AsconError(Ascon *a, char *msg, int errorno)
DynStringConcat(a->errmsg, " state, ");
DynStringConcat(a->errmsg, msg);
DynStringConcat(a->errmsg, ")");
+ DynStringConcat(a->errmsg, " on ");
+ DynStringConcat(a->errmsg, a->hostport);
}
a->state = AsconFailed;
}
@@ -262,8 +266,7 @@ int AsconReadChar(int fd, char *chr)
if (ret <= 0)
return ret;
ret = recv(fd, chr, 1, 0);
- /* PrintChar(*chr); */
- fflush(stdout);
+ /* PrintChar(*chr); fflush(stdout); */
if (ret > 0)
return 1;
if (ret == 0) {
@@ -388,6 +391,12 @@ int AsconBaseHandler(Ascon * a)
}
void AsconInsertProtocol(AsconProtocol *protocol) {
+ AsconProtocol *p;
+
+ for (p = protocols; p != NULL; p = p->next) {
+ if (p == protocol)
+ return;
+ }
protocol->next = protocols;
protocols = protocol;
}
@@ -812,7 +821,7 @@ char *AsconRead(Ascon * a)
{
if (a->noResponse) {
a->noResponse = 0;
- return "";
+ return NULL;
}
if (a->state != AsconIdle) {
a->state = AsconIdle;
diff --git a/asyncprotocol.c b/asyncprotocol.c
index 49a0118c..cdfeae05 100644
--- a/asyncprotocol.c
+++ b/asyncprotocol.c
@@ -31,16 +31,23 @@ int defaultSendCommand(pAsyncProtocol p, pAsyncTxn txn)
int defaultHandleInput(pAsyncProtocol p, pAsyncTxn txn, int ch)
{
const char *term = "\r\n";
- if (p->replyTerminator)
- term = p->replyTerminator;
- if (ch == term[txn->txn_state])
+ if (txn->txn_state == 0) {
+ int i;
+ for (i = 0; i < 10; ++i)
+ if (p->replyTerminator[i] && ch == p->replyTerminator[i][0]) {
+ txn->txn_state = i << 16;
+ break;
+ }
+ }
+ term = p->replyTerminator[txn->txn_state >> 16];
+ if (ch == term[txn->txn_state & 0xffff])
++txn->txn_state;
else
txn->txn_state = 0;
if (txn->inp_idx < txn->inp_len)
txn->inp_buf[txn->inp_idx++] = ch;
- if (term[txn->txn_state] == 0) {
+ if (term[txn->txn_state & 0xffff] == 0) {
if (txn->inp_idx < txn->inp_len)
txn->inp_buf[txn->inp_idx] = '\0';
return AQU_POP_CMD;
@@ -64,10 +71,7 @@ int defaultPrepareTxn(pAsyncProtocol p, pAsyncTxn txn, const char *cmd,
term = p->sendTerminator;
state = 0;
for (i = 0; i < cmd_len; ++i) {
- if (cmd[i] == 0x00) { /* end of transmission */
- cmd_len = i;
- break;
- } else if (cmd[i] == term[state]) {
+ if (cmd[i] == term[state]) {
++state;
continue;
}
@@ -100,14 +104,6 @@ int defaultPrepareTxn(pAsyncProtocol p, pAsyncTxn txn, const char *cmd,
if (txn->inp_buf != NULL) {
free(txn->inp_buf);
}
- txn->inp_buf = malloc(rsp_len);
- if (txn->inp_buf == NULL) {
- SICSLogWrite("Out of memory in AsyncProtocol::defaultPrepareTxn",
- eError);
- free(txn->out_buf);
- txn->out_buf = NULL;
- return 0;
- }
txn->inp_len = rsp_len;
txn->inp_idx = 0;
txn->txn_state = 0;
@@ -122,11 +118,15 @@ static void encodeTerminator(char *result, char *terminator)
{
if (terminator)
while (*terminator) {
- *result++ = '0';
- *result++ = 'x';
- *result++ = hex[(*terminator >> 4) & 0xF];
- *result++ = hex[(*terminator) & 0xF];
- ++terminator;
+ if (*terminator <= 32 || *terminator >= 127) {
+ *result++ = '0';
+ *result++ = 'x';
+ *result++ = hex[(*terminator >> 4) & 0xF];
+ *result++ = hex[(*terminator) & 0xF];
+ ++terminator;
+ } else {
+ *result++ = *terminator++;
+ }
}
*result = '\0';
return;
@@ -208,7 +208,6 @@ int AsyncProtocolNoAction(SConnection * pCon, SicsInterp * pSics,
void *pData, int argc, char *argv[])
{
char line[132];
- pAsyncProtocol self = (pAsyncProtocol) pData;
snprintf(line, 132, "%s does not understand %s", argv[0], argv[1]);
SCWrite(pCon, line, eError);
return 0;
@@ -217,7 +216,6 @@ int AsyncProtocolNoAction(SConnection * pCon, SicsInterp * pSics,
int AsyncProtocolAction(SConnection * pCon, SicsInterp * pSics,
void *pData, int argc, char *argv[])
{
- char line[132];
pAsyncProtocol self = (pAsyncProtocol) pData;
if (argc > 1) {
/* handle genecic parameters like terminators */
@@ -240,39 +238,54 @@ int AsyncProtocolAction(SConnection * pCon, SicsInterp * pSics,
return 1;
} else if (strcasecmp(argv[1], "replyterminator") == 0) {
if (argc > 2) {
- char *pPtr = decodeTerminator(argv[2]);
- if (pPtr) {
- if (self->replyTerminator)
- free(self->replyTerminator);
- self->replyTerminator = pPtr;
+ int i;
+ for (i = 0; i < 10; ++i)
+ if (self->replyTerminator[i]) {
+ free(self->replyTerminator[i]);
+ self->replyTerminator[i] = NULL;
+ }
+ for (i = 0; i < 10 && i < argc - 2; ++i) {
+ char* pPtr = decodeTerminator(argv[i + 2]);
+ if (pPtr) {
+ self->replyTerminator[i] = pPtr;
+ }
}
SCSendOK(pCon);
} else {
+ int i;
char term[132];
char line[1024];
- encodeTerminator(term, self->replyTerminator);
- sprintf(line, "%s.replyTerminator = \"%s\"", argv[0], term);
+ term[0] = '\0';
+ sprintf(line, "%s.replyTerminator =", argv[0]);
+ for (i = 0; i < 10; ++i) {
+ if (self->replyTerminator[i] == NULL)
+ break;
+ term[0] = ' ';
+ term[1] = '"';
+ encodeTerminator(&term[2], self->replyTerminator[i]);
+ strcat(term, "\"");
+ strcat(line, term);
+ }
SCWrite(pCon, line, eValue);
}
return 1;
+ } else if (strcasecmp(argv[1], "list") == 0) {
+ int ac = 2;
+ char *av[3] = { argv[0], 0, 0 };
+ av[1] = "sendterminator";
+ AsyncProtocolAction(pCon, pSics, pData, ac, av);
+ av[1] = "replyterminator";
+ AsyncProtocolAction(pCon, pSics, pData, ac, av);
+ return 1;
}
- } else if (strcasecmp(argv[1], "list") == 0) {
- int ac = 2;
- char *av[3] = { argv[0], 0, 0 };
- av[1] = "sendterminator";
- AsyncProtocolAction(pCon, pSics, pData, ac, av);
- av[1] = "replyterminator";
- AsyncProtocolAction(pCon, pSics, pData, ac, av);
- return 1;
- }
/* handle any other actions here */
+ }
return AsyncProtocolNoAction(pCon, pSics, pData, argc, argv);
}
void defaultKillPrivate(pAsyncProtocol p)
{
if (p->privateData) {
- /* TODO: should we do anything? */
free(p->privateData);
}
}
@@ -280,14 +293,23 @@ void defaultKillPrivate(pAsyncProtocol p)
void AsyncProtocolKill(void *pData)
{
pAsyncProtocol self = (pAsyncProtocol) pData;
- if (self->pDes)
- DeleteDescriptor(self->pDes);
- if (self->sendTerminator != NULL)
- free(self->sendTerminator);
- if (self->replyTerminator != NULL)
- free(self->replyTerminator);
+ int i;
if (self->killPrivate)
self->killPrivate(self);
+ if (self->pDes)
+ DeleteDescriptor(self->pDes);
+ self->pDes = NULL;
+ if (self->protocolName)
+ free(self->protocolName);
+ self->protocolName = NULL;
+ if (self->sendTerminator != NULL)
+ free(self->sendTerminator);
+ self->sendTerminator = NULL;
+ for (i = 0; i < 10; ++i) {
+ if (self->replyTerminator[i] != NULL)
+ free(self->replyTerminator[i]);
+ self->replyTerminator[i] = NULL;
+ }
}
pAsyncProtocol AsyncProtocolCreate(SicsInterp * pSics,
@@ -322,13 +344,14 @@ pAsyncProtocol AsyncProtocolCreate(SicsInterp * pSics,
AsyncProtocolKill(self);
return NULL;
}
+ self->protocolName = strdup(protocolName);
self->sendCommand = defaultSendCommand;
self->handleInput = defaultHandleInput;
self->handleEvent = defaultHandleEvent;
self->prepareTxn = defaultPrepareTxn;
self->killPrivate = defaultKillPrivate;
self->sendTerminator = strdup("\r\n");
- self->replyTerminator = strdup("\r\n");
+ self->replyTerminator[0] = strdup("\r\n");
return self;
}
diff --git a/asyncprotocol.h b/asyncprotocol.h
index 3c943666..5e79e968 100644
--- a/asyncprotocol.h
+++ b/asyncprotocol.h
@@ -26,6 +26,12 @@ typedef enum {
ATX_DISCO = 3
} ATX_STATUS;
+#define AQU_TIMEOUT -1
+#define AQU_DISCONNECT -2
+#define AQU_RECONNECT -3
+#define AQU_RETRY_CMD -4
+#define AQU_POP_CMD -5
+
struct __async_txn {
pAsyncUnit unit; /**< unit that transaction is associated with */
int txn_state; /**< protocol handler transaction parse state */
@@ -38,6 +44,8 @@ struct __async_txn {
int inp_len; /**< length of input buffer */
int inp_idx; /**< index of next character (number already received) */
AsyncTxnHandler handleResponse; /**< Txn response handler of command sender */
+ void *proto_private; /**< Protocol Private structure */
+ void (*kill_private) (pAsyncTxn pTxn); /**< if it needs killing */
void *cntx; /**< opaque context used by command sender */
/* The cntx field may be used by protocol handler from sendCommand
* as long as it is restored when response is complete
@@ -51,7 +59,7 @@ struct __async_protocol {
pObjectDescriptor pDes;
char *protocolName;
char *sendTerminator;
- char *replyTerminator;
+ char *replyTerminator[10];
void *privateData;
int (*sendCommand) (pAsyncProtocol p, pAsyncTxn txn);
int (*handleInput) (pAsyncProtocol p, pAsyncTxn txn, int ch);
diff --git a/asyncqueue.c b/asyncqueue.c
index 66800fe5..b55f0835 100644
--- a/asyncqueue.c
+++ b/asyncqueue.c
@@ -8,18 +8,20 @@
* single command channel.
*
* Douglas Clowes, February 2007
- *
+ *
*/
#include
#include
#include
#include
+#include
+#include
#include
-#include
#include "network.h"
#include "asyncqueue.h"
#include "nwatch.h"
+#include
typedef struct __async_command AQ_Cmd, *pAQ_Cmd;
@@ -40,6 +42,9 @@ struct __AsyncUnit {
void *notify_cntx;
};
+typedef enum { eAsyncIdle, eAsyncWaiting, eAsyncConnecting,
+ eAsyncConnected } AsyncState;
+
struct __AsyncQueue {
pObjectDescriptor pDes;
char *queue_name;
@@ -48,6 +53,9 @@ struct __AsyncQueue {
int iDelay; /* intercommand delay in milliseconds */
int timeout;
int retries;
+ int retryTimer; /* mSec delay before next retry */
+ bool translate; /* translate binary output with escaped chars */
+ bool trace;
struct timeval tvLastCmd; /* time of completion of last command */
int unit_count; /* number of units connected */
pAsyncUnit units; /* head of unit chain */
@@ -56,12 +64,56 @@ struct __AsyncQueue {
pNWContext nw_ctx; /* NetWait context handle */
pNWTimer nw_tmr; /* NetWait timer handle */
mkChannel *pSock; /* socket address */
+ AsyncState state; /* Queue Connection State */
pAsyncProtocol protocol;
+ char *noreply_text;
+ int noreply_len;
+ void *context; /**< opaque caller queue context */
};
static pAsyncQueue queue_array[FD_SETSIZE];
static int queue_index = 0;
+static const char *state_name(AsyncState the_state)
+{
+ switch (the_state) {
+ case eAsyncIdle:
+ return "eAsyncIdle";
+ case eAsyncWaiting:
+ return "eAsyncWaiting";
+ case eAsyncConnecting:
+ return "eAsyncConnecting";
+ case eAsyncConnected:
+ return "eAsyncConnected";
+ }
+ return "";
+}
+
+/*
+ * Free the command and transaction structures and their contents
+ */
+static void free_command(pAQ_Cmd myCmd)
+{
+ if (myCmd) {
+ pAsyncTxn myTxn = myCmd->tran;
+ if (myTxn) {
+ /*
+ * Allow kill_private to clean it all if it wants
+ */
+ if (myTxn->kill_private)
+ myTxn->kill_private(myTxn);
+ if (myTxn->out_buf)
+ free(myTxn->out_buf);
+ if (myTxn->inp_buf)
+ free(myTxn->inp_buf);
+ if (myTxn->proto_private)
+ free(myTxn->proto_private);
+ free(myTxn);
+ }
+ free(myCmd);
+ }
+}
+
/* ---------------------------- Local ------------------------------------
CreateSocketAdress stolen from Tcl. Thanks to John Ousterhout
*/
@@ -99,30 +151,175 @@ static int CreateSocketAdress(struct sockaddr_in *sockaddrPtr, /* Socket addres
return 1;
}
+static int AQ_ClearTimer(pAsyncQueue self) {
+ if (self->nw_tmr) {
+ NetWatchRemoveTimer(self->nw_tmr);
+ self->nw_tmr = 0;
+ return 1;
+ }
+ return 0;
+}
+
+static int AQ_SetTimer(pAsyncQueue self, int msecs, pNWCallback callback, void *context) {
+ int ret = 1;
+ if (self->nw_tmr) {
+ ret = AQ_ClearTimer(self);
+ }
+ NetWatchRegisterTimer(&self->nw_tmr, msecs, callback, context);
+ return ret;
+}
+
+static void AQ_Purge(pAsyncQueue self)
+{
+ pAQ_Cmd myCmd = self->command_head;
+ if (self->nw_tmr)
+ AQ_ClearTimer(self);
+
+ gettimeofday(&self->tvLastCmd, NULL);
+ while (myCmd) {
+ /* Process any callback */
+ if (myCmd->tran->handleResponse) {
+ myCmd->tran->txn_status = ATX_TIMEOUT; /* TODO should be ATX_DISCO */
+ myCmd->tran->handleResponse(myCmd->tran);
+ }
+ /*
+ * Remove this transaction from the queue
+ */
+ if (myCmd->next) {
+ self->command_head = myCmd->next;
+ } else
+ self->command_head = self->command_tail = NULL;
+ free_command(myCmd);
+ myCmd = self->command_head;
+ }
+}
+
static void AQ_Notify(pAsyncQueue self, int event)
{
pAsyncUnit unit;
+ if (self->state != eAsyncConnected)
+ SICSLogPrintf(eStatus, "Function: %s:%s\n", self->queue_name,
+ __func__);
for (unit = self->units; unit; unit = unit->next)
if (unit->notify_func != NULL)
unit->notify_func(unit->notify_cntx, event);
}
+static int TimedReconnect(void *cntx, int mode)
+{
+ int iRet;
+ char line[132];
+ pAsyncQueue self = (pAsyncQueue) cntx;
+ self->nw_tmr = 0;
+
+ if (self->state != eAsyncConnected)
+ SICSLogPrintf(eStatus, "Function: %s:%s\n", self->queue_name,
+ __func__);
+
+ AQ_Purge(self);
+
+ /* TODO: if self->pSock is NULL we haven't connected yet */
+
+ iRet = NETReconnect(self->pSock);
+ /*
+ * iRet can take the following values:
+ * -1: The request failed
+ * 0: The request is still in progress
+ * +1: The request succeeded
+ */
+ if (iRet <= 0) {
+ if (iRet < 0) {
+ snprintf(line, 132, "Failed reconnect on AsyncQueue '%s'",
+ self->queue_name);
+ SICSLogWrite(line, eStatus);
+ /* Timer for retry */
+ NetWatchSetMode(self->nw_ctx, 0);
+ /* implement an exponential backoff within limits */
+ self->retryTimer = 2 * self->retryTimer;
+ if (self->retryTimer < 125)
+ self->retryTimer = 125;
+ if (self->retryTimer > 16000)
+ self->retryTimer = 16000;
+ AQ_SetTimer(self, self->retryTimer,
+ TimedReconnect, self);
+ SICSLogPrintf(eStatus, "In %s:%s: state %s => eAsyncWaiting\n",
+ self->queue_name, __func__, state_name(self->state));
+ self->state = eAsyncWaiting;
+ } else {
+ NetWatchSetMode(self->nw_ctx, nwatch_write);
+ SICSLogPrintf(eStatus, "In %s:%s: state %s => eAsyncConnecting\n",
+ self->queue_name, __func__, state_name(self->state));
+ self->state = eAsyncConnecting;
+ /* await reconnect result in MyCallback */
+ }
+ return 1;
+ }
+ NetWatchSetMode(self->nw_ctx, nwatch_read);
+ SICSLogPrintf(eStatus, "In %s:%s: state %s => eAsyncConnected\n",
+ self->queue_name, __func__, state_name(self->state));
+ self->state = eAsyncConnected;
+ snprintf(line, 132, "Reconnect on AsyncQueue '%s'", self->queue_name);
+ SICSLogWrite(line, eStatus);
+ AQ_Purge(self);
+ AQ_Notify(self, AQU_RECONNECT);
+ return 1;
+}
+
static int AQ_Reconnect(pAsyncQueue self)
{
int iRet;
- int sock;
- int flag = 1;
char line[132];
- iRet = NETReconnect(self->pSock);
- if (iRet <= 0) {
- snprintf(line, 132, "Disconnect on AsyncQueue '%s'", self->queue_name);
- SICSLogWrite(line, eStatus);
+ if (self->state != eAsyncConnected)
+ SICSLogPrintf(eStatus, "Function: %s:%s\n", self->queue_name,
+ __func__);
+ /*
+ * Remove any old timer
+ */
+ if (self->nw_tmr)
+ AQ_ClearTimer(self);
+
+ if (self->state == eAsyncConnected) {
+ self->state = eAsyncIdle;
+ SICSLogPrintf(eStatus, "Disconnect on AsyncQueue '%s'", self->queue_name);
AQ_Notify(self, AQU_DISCONNECT);
+ AQ_Purge(self);
+ }
+
+ iRet = NETReconnect(self->pSock);
+ /*
+ * iRet can take the following values:
+ * -1: The request failed
+ * 0: The request is still in progress
+ * +1: The request succeeded
+ */
+ if (iRet <= 0) {
+ if (iRet < 0) {
+ /* Timer for retry */
+ NetWatchSetMode(self->nw_ctx, 0);
+ /* implement an exponential backoff within limits */
+ self->retryTimer = 125; /* initial delay */
+ AQ_SetTimer(self, self->retryTimer,
+ TimedReconnect, self);
+ SICSLogPrintf(eStatus, "In %s:%s: state %s => eAsyncWaiting\n",
+ self->queue_name, __func__, state_name(self->state));
+ self->state = eAsyncWaiting;
+ } else {
+ NetWatchSetMode(self->nw_ctx, nwatch_write);
+ SICSLogPrintf(eStatus, "In %s:%s: state %s => eAsyncConnecting\n",
+ self->queue_name, __func__, state_name(self->state));
+ self->state = eAsyncConnecting;
+ /* await reconnect result in MyCallback */
+ }
return iRet;
}
+ NetWatchSetMode(self->nw_ctx, nwatch_read);
+ SICSLogPrintf(eStatus, "In %s:%s: state %s => eAsyncConnected\n",
+ self->queue_name, __func__, state_name(self->state));
+ self->state = eAsyncConnected;
snprintf(line, 132, "Reconnect on AsyncQueue '%s'", self->queue_name);
SICSLogWrite(line, eStatus);
+ AQ_Purge(self);
AQ_Notify(self, AQU_RECONNECT);
return 1;
}
@@ -135,7 +332,11 @@ static int StartCommand(pAsyncQueue self)
{
pAQ_Cmd myCmd = self->command_head;
mkChannel *sock = self->pSock;
+ int iRet = 0;
+ if (self->state != eAsyncConnected)
+ SICSLogPrintf(eStatus, "Function: %s:%s\n", self->queue_name,
+ __func__);
if (myCmd == NULL)
return OKOK;
@@ -143,7 +344,7 @@ static int StartCommand(pAsyncQueue self)
* Remove any old command timeout timer
*/
if (self->nw_tmr)
- NetWatchRemoveTimer(self->nw_tmr);
+ AQ_ClearTimer(self);
/*
* Implement the inter-command delay
@@ -164,7 +365,7 @@ static int StartCommand(pAsyncQueue self)
int delay = when.tv_sec - now.tv_sec;
delay *= 1000;
delay += (when.tv_usec - now.tv_usec + (1000 - 1)) / 1000;
- NetWatchRegisterTimer(&self->nw_tmr, delay, DelayedStart, self);
+ AQ_SetTimer(self, delay, DelayedStart, self);
return OKOK;
}
}
@@ -172,33 +373,56 @@ static int StartCommand(pAsyncQueue self)
/*
* Discard any input before sending command
*/
- while (NETAvailable(sock, 0)) {
- /* TODO: handle unsolicited input */
- char reply[1];
- int iRet;
- iRet = NETRead(sock, reply, 1, 0);
- if (iRet < 0) { /* EOF */
- iRet = AQ_Reconnect(self);
- if (iRet <= 0) {
- myCmd->tran->txn_state = ATX_DISCO;
- if (myCmd->tran->handleResponse) {
- myCmd->tran->handleResponse(myCmd->tran);
- }
- PopCommand(self);
+ if (NETAvailable(sock, 0)) {
+ while (NETAvailable(sock, 0)) {
+ /* TODO: handle unsolicited input */
+ char reply[128];
+ iRet = NETRead(sock, reply, 128, 0);
+ if (iRet < 0) { /* EOF */
+ iRet = AQ_Reconnect(self);
return 0;
+ } else if (iRet > 0) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ SICSLogTimePrintf(eError, &tv,
+ "ERROR: %d unsolicited chars in AsyncQueue %s",
+ iRet, self->queue_name);
+ SICSLogWriteHexTime(reply, iRet, eError, &tv);
}
}
}
+
+ myCmd->tran->txn_status = ATX_ACTIVE;
+ if (self->protocol->sendCommand) {
+ iRet = self->protocol->sendCommand(self->protocol, myCmd->tran);
+ } else {
+ pAsyncTxn txn = myCmd->tran;
+ iRet = AsyncUnitWrite(txn->unit, txn->out_buf, txn->out_len);
+ if (iRet < 0)
+ iRet = 0;
+ else
+ iRet = 1;
+ }
+ /*
+ * Handle case of no response expected
+ */
+ if (myCmd->tran->inp_len == 0 || myCmd->tran->inp_buf == NULL) {
+ myCmd->tran->txn_status = ATX_COMPLETE;
+ return PopCommand(self);
+ }
+ if (iRet > 0)
+ if (myCmd->tran->txn_status == ATX_COMPLETE)
+ return PopCommand(self);
/*
* Add a new command timeout timer
*/
if (myCmd->timeout > 0)
- NetWatchRegisterTimer(&self->nw_tmr, myCmd->timeout,
+ AQ_SetTimer(self, myCmd->timeout,
CommandTimeout, self);
else
- NetWatchRegisterTimer(&self->nw_tmr, 30000, CommandTimeout, self);
+ AQ_SetTimer(self, 30000, CommandTimeout, self);
myCmd->active = 1;
- return self->protocol->sendCommand(self->protocol, myCmd->tran);
+ return iRet;
}
static int QueCommandHead(pAsyncQueue self, pAQ_Cmd cmd)
@@ -239,13 +463,17 @@ static int QueCommand(pAsyncQueue self, pAQ_Cmd cmd)
self->command_tail = cmd;
return 1;
}
+
static int PopCommand(pAsyncQueue self)
{
pAQ_Cmd myCmd = self->command_head;
if (self->nw_tmr)
- NetWatchRemoveTimer(self->nw_tmr);
- self->nw_tmr = 0;
+ AQ_ClearTimer(self);
+
gettimeofday(&self->tvLastCmd, NULL);
+ /* Process any callback */
+ if (myCmd->tran->handleResponse)
+ myCmd->tran->handleResponse(myCmd->tran);
/*
* If this is not the last in queue, start transmission
*/
@@ -255,10 +483,7 @@ static int PopCommand(pAsyncQueue self)
StartCommand(self);
} else
self->command_head = self->command_tail = NULL;
- free(myCmd->tran->out_buf);
- free(myCmd->tran->inp_buf);
- free(myCmd->tran);
- free(myCmd);
+ free_command(myCmd);
return 1;
}
@@ -267,6 +492,13 @@ static int CommandTimeout(void *cntx, int mode)
pAsyncQueue self = (pAsyncQueue) cntx;
pAQ_Cmd myCmd = self->command_head;
self->nw_tmr = 0;
+ if (self->trace) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ SICSLogTimePrintf(eLog, &tv,
+ "Timeout Trace on AsyncQueue %s", self->queue_name);
+ SICSLogWriteHexTime(myCmd->tran->inp_buf, myCmd->tran->inp_idx, eLog, &tv);
+ }
if (myCmd->retries > 0) {
--myCmd->retries;
StartCommand(self);
@@ -276,8 +508,6 @@ static int CommandTimeout(void *cntx, int mode)
self->protocol->handleEvent(self->protocol, myCmd->tran,
AQU_TIMEOUT);
if (iRet == AQU_POP_CMD) {
- if (myCmd->tran->handleResponse)
- myCmd->tran->handleResponse(myCmd->tran);
PopCommand(self); /* remove command */
} else if (iRet == AQU_RETRY_CMD)
StartCommand(self); /* restart command */
@@ -290,6 +520,9 @@ static int CommandTimeout(void *cntx, int mode)
static int DelayedStart(void *cntx, int mode)
{
pAsyncQueue self = (pAsyncQueue) cntx;
+ if (self->state != eAsyncConnected)
+ SICSLogPrintf(eStatus, "Function: %s:%s\n", self->queue_name,
+ __func__);
self->nw_tmr = 0;
StartCommand(self);
return 1;
@@ -299,27 +532,18 @@ static int MyCallback(void *context, int mode)
{
pAsyncQueue self = (pAsyncQueue) context;
+ if (self->state != eAsyncConnected)
+ SICSLogPrintf(eStatus, "Function: %s:%s\n", self->queue_name,
+ __func__);
if (mode & nwatch_read) {
int iRet;
- char reply[1];
+ char reply[100];
- iRet = NETRead(self->pSock, reply, 1, 0);
- /* printf(" iRet, char = %d, %d\n", iRet, (int)reply[0]); */
+ iRet = NETRead(self->pSock, reply, 100, 0);
if (iRet < 0) { /* EOF */
iRet = AQ_Reconnect(self);
- if (iRet <= 0) {
- /* changed to call handleResponse with a bad status code: MK
- */
- pAQ_Cmd myCmd = self->command_head;
- if (myCmd) {
- myCmd->tran->txn_state = ATX_DISCO;
- if (myCmd->tran->handleResponse) {
- myCmd->tran->handleResponse(myCmd->tran);
- }
- PopCommand(self);
- }
+ if (iRet <= 0)
return iRet;
- }
/* restart the command */
StartCommand(self);
return 1;
@@ -327,22 +551,74 @@ static int MyCallback(void *context, int mode)
if (iRet == 0) { /* TODO: timeout or error */
return 0;
} else {
+ int nchars = iRet;
+ int i = 0;
pAQ_Cmd myCmd = self->command_head;
if (myCmd) {
- iRet =
- self->protocol->handleInput(self->protocol, myCmd->tran,
- reply[0]);
- if (iRet == 0 || iRet == AQU_POP_CMD) { /* end of command */
- if (myCmd->tran->handleResponse)
- myCmd->tran->handleResponse(myCmd->tran);
- PopCommand(self);
- } else if (iRet < 0) /* TODO: error */
- ;
+ for (i = 0; i < nchars; ++i) {
+ iRet =
+ self->protocol->handleInput(self->protocol, myCmd->tran,
+ reply[i] & 0xFF);
+ if (iRet == 0 || iRet == AQU_POP_CMD) { /* end of command */
+ if (self->trace) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ SICSLogTimePrintf(eLog, &tv,
+ "Input Trace on AsyncQueue %s", self->queue_name);
+ SICSLogWriteHexTime(myCmd->tran->inp_buf, myCmd->tran->inp_idx, eLog, &tv);
+ }
+ PopCommand(self);
+ break;
+ } else if (iRet < 0) {
+ int excess = nchars - 1 - i;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ SICSLogTimePrintf(eError, &tv,
+ "ERROR: Protocol error %d in AsyncQueue %s",
+ iRet, self->queue_name);
+ SICSLogWriteTime("Sent:", eError, &tv);
+ SICSLogWriteHexTime(myCmd->tran->out_buf, myCmd->tran->out_len, eError, &tv);
+ SICSLogWriteTime("Received:", eError, &tv);
+ SICSLogWriteHexTime(myCmd->tran->inp_buf, myCmd->tran->inp_idx, eError, &tv);
+ SICSLogWriteTime("Processed:", eError, &tv);
+ SICSLogWriteHexTime(&reply[0], i, eError, &tv);
+ SICSLogWriteTime("Unprocessed:", eError, &tv);
+ SICSLogWriteHexTime(&reply[i], excess, eError, &tv);
+ /* TODO: error */
+ break;
+ }
+ }
+ if (i < nchars - 1) {
+ int excess = nchars - 1 - i;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ SICSLogTimePrintf(eError, &tv,
+ "ERROR: %d excess chars in AsyncQueue %s",
+ excess, self->queue_name);
+ SICSLogWriteHexTime(&reply[i], excess, eError, &tv);
+ /* TODO: handle unsolicited */
+ }
} else {
+ int excess = nchars - 1 - i;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ SICSLogTimePrintf(eError, &tv,
+ "ERROR: %d unsolicited chars in AsyncQueue %s",
+ excess, self->queue_name);
+ SICSLogWriteHexTime(&reply[i], excess, eError, &tv);
/* TODO: handle unsolicited input */
}
}
}
+ if (mode & nwatch_write) {
+ char line[132];
+ SICSLogPrintf(eStatus, "Writeable socket callback on AsyncQueue %s",
+ self->queue_name);
+ NetWatchSetMode(self->nw_ctx, nwatch_read);
+ SICSLogPrintf(eStatus, "In %s:%s: state %s => eAsyncConnected\n",
+ self->queue_name, __func__, state_name(self->state));
+ self->state = eAsyncConnected;
+ }
return 1;
}
@@ -395,20 +671,43 @@ pAsyncTxn AsyncUnitPrepareTxn(pAsyncUnit unit,
myTxn = (pAsyncTxn) malloc(sizeof(AsyncTxn));
if (myTxn == NULL) {
SICSLogWrite("ERROR: Out of memory in AsyncUnitPrepareTxn", eError);
- return 0;
+ return NULL;
}
memset(myTxn, 0, sizeof(AsyncTxn));
+ if (unit->queue->noreply_text) {
+ if (cmd_len > unit->queue->noreply_len
+ && strncasecmp(&command[cmd_len - unit->queue->noreply_len],
+ unit->queue->noreply_text, unit->queue->noreply_len) == 0) {
+ rsp_len = 0;
+ cmd_len -= unit->queue->noreply_len;
+ }
+ } else {
+ if (cmd_len > 3 && strncmp(&command[cmd_len - 3], "{0}", 3) == 0) {
+ rsp_len = 0;
+ cmd_len -= 3;
+ }
+ else if (cmd_len > 11 && strncasecmp(&command[cmd_len - 11], "@@NOREPLY@@", 11) == 0) {
+ rsp_len = 0;
+ cmd_len -= 11;
+ }
+ }
if (unit->queue->protocol->prepareTxn) {
int iRet;
+ myTxn->inp_len = rsp_len; /* allowing protocol to change it */
iRet =
unit->queue->protocol->prepareTxn(unit->queue->protocol, myTxn,
command, cmd_len, rsp_len);
+ if (iRet == 0) {
+ free(myTxn);
+ return NULL;
+ }
+ rsp_len = myTxn->inp_len; /* allowed protocol to change it */
} else {
myTxn->out_buf = (char *) malloc(cmd_len + 5);
if (myTxn->out_buf == NULL) {
SICSLogWrite("ERROR: Out of memory in AsyncUnitPrepareTxn", eError);
free(myTxn);
- return 0;
+ return NULL;
}
memcpy(myTxn->out_buf, command, cmd_len);
myTxn->out_len = cmd_len;
@@ -423,15 +722,12 @@ pAsyncTxn AsyncUnitPrepareTxn(pAsyncUnit unit,
if (rsp_len == 0)
myTxn->inp_buf = NULL;
else {
- if (myTxn->inp_buf != NULL) {
- free(myTxn->inp_buf);
- }
myTxn->inp_buf = malloc(rsp_len + 1);
if (myTxn->inp_buf == NULL) {
SICSLogWrite("ERROR: Out of memory in AsyncUnitPrepareTxn", eError);
free(myTxn->out_buf);
free(myTxn);
- return 0;
+ return NULL;
}
memset(myTxn->inp_buf, 0, rsp_len + 1);
}
@@ -458,6 +754,7 @@ int AsyncUnitSendTxn(pAsyncUnit unit,
typedef struct txn_s {
char *transReply;
int transWait;
+ int respLen;
} TXN, *pTXN;
/**
@@ -469,30 +766,33 @@ static int TransCallback(pAsyncTxn pCmd)
int resp_len = pCmd->inp_idx;
pTXN self = (pTXN) pCmd->cntx;
- if (pCmd->txn_status == ATX_TIMEOUT) {
+ self->respLen = resp_len;
+ if (resp_len > 0) {
memcpy(self->transReply, resp, resp_len);
self->transReply[resp_len] = '\0';
+ } else
self->transReply[0] = '\0';
+ if (pCmd->txn_status == ATX_TIMEOUT)
self->transWait = -1;
- } else {
- memcpy(self->transReply, resp, resp_len);
- self->transReply[resp_len] = '\0';
+ else
self->transWait = 0;
- }
+
return 0;
}
int AsyncUnitTransact(pAsyncUnit unit,
const char *command, int cmd_len,
- char *response, int rsp_len)
+ char *response, int *rsp_len)
{
TXN txn;
assert(unit);
txn.transReply = response;
+ txn.respLen = *rsp_len;
txn.transWait = 1;
- AsyncUnitSendTxn(unit, command, cmd_len, TransCallback, &txn, rsp_len);
+ AsyncUnitSendTxn(unit, command, cmd_len, TransCallback, &txn, *rsp_len);
while (txn.transWait == 1)
TaskYield(pServ->pTasker);
+ *rsp_len = txn.respLen;
if (txn.transWait < 0)
return txn.transWait;
return 1;
@@ -505,6 +805,13 @@ int AsyncUnitWrite(pAsyncUnit unit, void *buffer, int buflen)
assert(unit);
assert(unit->queue);
if (buflen > 0) {
+ if (unit->queue->trace) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ SICSLogTimePrintf(eLog, &tv,
+ "Output Trace on AsyncQueue %s", unit->queue->queue_name);
+ SICSLogWriteHexTime(buffer, buflen, eLog, &tv);
+ }
sock = AsyncUnitGetSocket(unit);
iRet = NETWrite(sock, buffer, buflen);
/* TODO handle errors */
@@ -593,24 +900,75 @@ int AsyncQueueAction(SConnection * pCon, SicsInterp * pSics,
char line[132];
pAsyncQueue self = (pAsyncQueue) pData;
if (argc > 1) {
- if (strcasecmp("send", argv[1]) == 0) {
+ if (strcasecmp("send", argv[1]) == 0 || strcasecmp("transact", argv[1]) == 0) {
AsyncUnit myUnit;
char cmd[10240];
char rsp[10240];
int idx = 0;
- int i, j;
+ int i, j, len;
cmd[0] = '\0';
+ /* Managers only */
+ if (!SCMatchRights(pCon, usMugger))
+ return 0;
for (i = 2; i < argc; ++i) {
- j = snprintf(&cmd[idx], 10240 - idx, "%s%s",
- (i > 2) ? " " : "", argv[i]);
- if (j < 0)
+ if (idx >= 10240)
break;
- idx += j;
+ if (i > 2)
+ cmd[idx++] = ' ';
+ len = strlen(argv[i]);
+ for (j = 0; j < len; ++j) {
+ if (idx >= 10240)
+ break;
+ if (argv[i][j] == '\\') {
+ ++j;
+ if (argv[i][j] == '\\')
+ cmd[idx++] = '\\';
+ else if (argv[i][j] == 'r')
+ cmd[idx++] = '\r';
+ else if (argv[i][j] == 'n')
+ cmd[idx++] = '\n';
+ else if (isdigit(argv[i][j])) {
+ char *nptr = &argv[i][j];
+ char *endptr;
+ long k = strtol(nptr, &endptr, 0);
+ cmd[idx++] = k;
+ j += (endptr - nptr);
+ --j; /* prepare for loop increment */
+ } else
+ cmd[idx++] = argv[i][j];
+ } else
+ cmd[idx++] = argv[i][j];
+ }
}
+ cmd[idx] = '\0';
memset(&myUnit, 0, sizeof(AsyncUnit));
myUnit.queue = self;
- AsyncUnitTransact(&myUnit, cmd, idx, rsp, 10240);
- SCWrite(pCon, rsp, eValue);
+ len = 10240;
+ (void) AsyncUnitTransact(&myUnit, cmd, idx, rsp, &len);
+ /* escape control characters in response */
+ j = 0;
+ for (i = 0; i < len; ++i) {
+ if (j >= 10230)
+ break;
+ if (self->translate) {
+ if (rsp[i] < 32 || rsp[i] >= 127) {
+ if (rsp[i] == '\r') {
+ cmd[j++] = '\r';
+ } else if (rsp[i] == '\n') {
+ cmd[j++] = '\n';
+ } else {
+ j += snprintf(&cmd[j], 6, "\\0x%02x", rsp[i]);
+ }
+ } else if (rsp[i] == '\\') {
+ cmd[j++] = '\\';
+ cmd[j++] = '\\';
+ } else
+ cmd[j++] = rsp[i];
+ } else
+ cmd[j++] = rsp[i];
+ }
+ cmd[j++] = '\0';
+ SCWrite(pCon, cmd, eValue);
return 1;
}
if (strcasecmp(argv[1], "reconnect") == 0) {
@@ -627,7 +985,7 @@ int AsyncQueueAction(SConnection * pCon, SicsInterp * pSics,
SCWrite(pCon, line, eError);
return 0;
} else {
- if (delay < 0 || delay > 30000) {
+ if (delay < 0 || delay > 300000) {
snprintf(line, 132, "Value out of range: %d", delay);
SCWrite(pCon, line, eError);
return 0;
@@ -692,6 +1050,85 @@ int AsyncQueueAction(SConnection * pCon, SicsInterp * pSics,
}
return OKOK;
}
+ if (strcasecmp(argv[1], "translate") == 0) {
+ if (argc > 2) {
+ int value;
+ int iRet;
+ iRet = sscanf(argv[2], "%d", &value);
+ if (iRet != 1) {
+ snprintf(line, 132, "Invalid argument: %s", argv[2]);
+ SCWrite(pCon, line, eError);
+ return 0;
+ } else {
+ if (value == 0) {
+ self->translate = false;
+ return OKOK;
+ } else if (value == 1) {
+ self->translate = true;
+ return OKOK;
+ }
+ snprintf(line, 132, "Invalid argument: %s", argv[2]);
+ SCWrite(pCon, line, eError);
+ return 0;
+ }
+ } else {
+ snprintf(line, 132, "%s.translate = %d", argv[0], self->translate);
+ SCWrite(pCon, line, eStatus);
+ return OKOK;
+ }
+ return OKOK;
+ }
+ if (strcasecmp(argv[1], "trace") == 0) {
+ if (argc > 2) {
+ int value;
+ int iRet;
+ iRet = sscanf(argv[2], "%d", &value);
+ if (iRet != 1) {
+ snprintf(line, 132, "Invalid argument: %s", argv[2]);
+ SCWrite(pCon, line, eError);
+ return 0;
+ } else {
+ if (value == 0) {
+ self->trace = false;
+ return OKOK;
+ } else if (value == 1) {
+ self->trace = true;
+ return OKOK;
+ }
+ snprintf(line, 132, "Invalid argument: %s", argv[2]);
+ SCWrite(pCon, line, eError);
+ return 0;
+ }
+ } else {
+ snprintf(line, 132, "%s.trace = %d", argv[0], self->trace);
+ SCWrite(pCon, line, eStatus);
+ return OKOK;
+ }
+ return OKOK;
+ }
+ if (strcasecmp(argv[1], "noreply") == 0) {
+ if (argc > 2) {
+ if (self->noreply_text)
+ free(self->noreply_text);
+ self->noreply_text = strdup(argv[2]);
+ self->noreply_len = strlen(argv[2]);
+ } else {
+ SCPrintf(pCon, eValue, "%s.noreply = %s", argv[0], self->noreply_text);
+ }
+ return OKOK;
+ }
+ if (strcasecmp(argv[1], "list") == 0) {
+ SCPrintf(pCon, eValue, "%s.delay = %d", argv[0], self->iDelay);
+ SCPrintf(pCon, eValue, "%s.timeout = %d", argv[0], self->timeout);
+ SCPrintf(pCon, eValue, "%s.retries = %d", argv[0], self->retries);
+ SCPrintf(pCon, eValue, "%s.translate = %d", argv[0], self->translate);
+ SCPrintf(pCon, eValue, "%s.trace = %d", argv[0], self->trace);
+ if (self->noreply_text)
+ SCPrintf(pCon, eValue, "%s.noreply = %s", argv[0], self->noreply_text);
+ if (self->protocol && self->protocol->protocolName)
+ SCPrintf(pCon, eValue, "%s.protocol = %s", argv[0], self->protocol->protocolName);
+ return OKOK;
+ }
}
snprintf(line, 132, "%s does not understand %s", argv[0], argv[1]);
SCWrite(pCon, line, eError);
@@ -741,6 +1178,7 @@ static pAsyncQueue AQ_Create(const char *host, const char *port)
}
if (self == NULL) {
+ /* TODO: if channel (self->pSock) is NULL we haven't connected yet, do it later */
if (channel == NULL)
return NULL;
@@ -760,6 +1198,15 @@ static pAsyncQueue AQ_Create(const char *host, const char *port)
if (i == queue_index)
queue_array[queue_index++] = self;
+ /* TODO: if self->pSock is NULL we haven't connected yet */
+#if 0
+ if (channel == NULL) {
+ /* TODO: all the rest */
+ AQ_SetTimer(self, self->retryTimer, TimedReconnect,
+ self);
+ }
+#endif
+
return self;
}
@@ -769,6 +1216,7 @@ static int AQ_Init(pAsyncQueue self)
if (self->nw_ctx == NULL)
NetWatchRegisterCallback(&self->nw_ctx,
self->pSock->sockid, MyCallback, self);
+ NetWatchSetMode(self->nw_ctx, nwatch_write | nwatch_read);
return 1;
}
@@ -784,7 +1232,7 @@ static void AQ_Kill(void *pData)
if (self->nw_ctx)
NetWatchRemoveCallback(self->nw_ctx);
if (self->nw_tmr)
- NetWatchRemoveTimer(self->nw_tmr);
+ AQ_ClearTimer(self);
if (self->queue_name)
free(self->queue_name);
NETClosePort(self->pSock);
@@ -979,3 +1427,31 @@ pAsyncUnit AsyncUnitFromQueue(pAsyncQueue queue)
result->queue = queue;
return result;
}
+
+void *AsyncUnitSetQueueContext(pAsyncUnit unit, void *cntx)
+{
+ void *hold;
+ assert(unit);
+ assert(unit->queue);
+ hold = unit->queue->context;
+ unit->queue->context = cntx;
+ return hold;
+}
+
+void *AsyncUnitGetQueueContext(pAsyncUnit unit)
+{
+ assert(unit);
+ assert(unit->queue);
+ return unit->queue->context;
+}
+
+
+int AsyncUnitIsQueueConnected(pAsyncUnit unit)
+{
+ assert(unit);
+ assert(unit->queue);
+ if (unit && unit->queue)
+ if (unit->queue->state == eAsyncConnected)
+ return 1;
+ return 0;
+}
diff --git a/asyncqueue.h b/asyncqueue.h
index 2359d575..669916c3 100644
--- a/asyncqueue.h
+++ b/asyncqueue.h
@@ -11,12 +11,6 @@
#include "asyncprotocol.h"
-#define AQU_TIMEOUT -1
-#define AQU_DISCONNECT -2
-#define AQU_RECONNECT -3
-#define AQU_RETRY_CMD -4
-#define AQU_POP_CMD -5
-
typedef struct __AsyncQueue AsyncQueue, *pAsyncQueue;
@@ -28,7 +22,7 @@ typedef struct __AsyncQueue AsyncQueue, *pAsyncQueue;
*/
int AsyncUnitCreate(const char *queueName, pAsyncUnit * unit);
/** \brief Get an AsyncUnit from a given AsyncQueue
- * \param queue The AsyncQueue fro which this AsyncUnit is valid
+ * \param queue The AsyncQueue for which this AsyncUnit is valid
* \return a new AsyncUnit or NULL on error
*/
pAsyncUnit AsyncUnitFromQueue(pAsyncQueue queue);
@@ -36,9 +30,13 @@ pAsyncUnit AsyncUnitFromQueue(pAsyncQueue queue);
/** \brief create an AsyncUnit attached to an anonymous AsyncQueue.
*
* \param host name or address of the target host
- * \param port number or service name on the target host
+ * \param port number or service name on the target host
* \param unit pointer to the AsyncUnit created on positive return
* \return positive if successful
+ *
+ * If is null then points to an existing queue name.
+ * If a queue exists for /, then that queue is used.
+ * If neither of the above hold, a new queue is created.
*/
int AsyncUnitCreateHost(const char *host,
const char *port, pAsyncUnit * unit);
@@ -98,11 +96,12 @@ int AsyncUnitSendTxn(pAsyncUnit unit,
* \param cmd_len length of data in command
* \param responseHandler function to handle the response
* \param context to be used by handler function
- * \param resp_len maximum length to be allowed for response
+ * \param resp_len [in] maximum length to be allowed for response
+ * [out] actual length returned
*/
int AsyncUnitTransact(pAsyncUnit unit,
const char *command, int cmd_len,
- char *response, int rsp_len);
+ char *response, int *rsp_len);
/** \brief write to the AsyncQueue file descriptor
*
@@ -182,4 +181,16 @@ int AsyncQueueFactory(SConnection * pCon, SicsInterp * pSics,
int AsyncQueueAction(SConnection * pCon, SicsInterp * pSics,
void *pData, int argc, char *argv[]);
+/** \brief store queue level context
+ */
+void *AsyncUnitSetQueueContext(pAsyncUnit handle, void *cntx);
+
+/** \brief retrieve queue level context
+ */
+void *AsyncUnitGetQueueContext(pAsyncUnit handle);
+
+/** \brief return one if queue is connected, zero otherwise
+ */
+int AsyncUnitIsQueueConnected(pAsyncUnit handle);
+
#endif /* SICSASYNCQUEUE */
diff --git a/asynnet.c b/asynnet.c
index a4f39d33..67f4d7c6 100644
--- a/asynnet.c
+++ b/asynnet.c
@@ -397,14 +397,20 @@ void ANETprocess(void)
{
int i, status, count = 0, socke = 0, length;
fd_set readMask, writeMask;
- struct timeval tmo = { 0, 20 };
+ struct timeval tmo = { 0, 10000 };
FD_ZERO(&readMask);
FD_ZERO(&writeMask);
for (i = 0; i < noConnections; i++) {
socke = connections[i].socket;
FD_SET(socke, &readMask);
- FD_SET(socke, &writeMask);
+ if (connections[i].writeBuffer != NULL) {
+ GetRWBufferData(connections[i].writeBuffer, &length);
+ if (length > 0)
+ FD_SET(socke, &writeMask);
+ else
+ connections[i].lastOpenForWrite = time(NULL);
+ }
if (socke > count) {
count = socke;
}
@@ -575,6 +581,7 @@ void *ANETreadPtr(int handle, int *length)
con = findSocketDescriptor(handle);
if (con == NULL) {
+ *length = 0;
return NULL;
} else {
data = GetRWBufferData(con->readBuffer, length);
diff --git a/binprot.c b/binprot.c
index f0ce52f8..99261994 100644
--- a/binprot.c
+++ b/binprot.c
@@ -53,18 +53,19 @@
* modbus-crc: CRC-16-IBM, order: lo,hi
* keller-crc: CRC-16-IBM, order: hi,lo
* sycon-crc: sort of mod 256 checksum, byte stuffing included (no float allowed)
+ * chksum-crc: simple 8bit checksum
*/
typedef enum {intType, hexType, floatType,
skipType, codeType, crcType, dumpType, errorType} BinDataType;
// dumpType, errorType must be the last items
-typedef enum {modbusCrc, kellerCrc, rsportCrc, syconCrc} CrcAlgorithm;
+typedef enum {modbusCrc, kellerCrc, rsportCrc, syconCrc, chksumCrc} CrcAlgorithm;
typedef struct {
CrcAlgorithm crcAlgorithm;
pDynString inp;
- char *nextFmt;
+ char *nextFmt; // position of next format token
pDynString result;
int expectedChars;
BinDataType type;
@@ -135,6 +136,23 @@ static int calc_crc8(char *inp, int inpLen)
}
return crc;
}
+/*----------------------------------------------------------------------------*/
+static int calc_chksum(char *inp, int inpLen)
+{
+/** simple 8bit checksum
+ */
+
+ unsigned char crc = 0;
+ unsigned char data;
+ int n;
+
+ while (inpLen--) {
+ data = *(unsigned char *) inp;
+ crc += data;
+ inp++;
+ }
+ return crc;
+}
/*----------------------------------------------------------------------------*/
int BinReadItem(Ascon *a) {
@@ -154,7 +172,11 @@ int BinReadItem(Ascon *a) {
if (strcasecmp(item, "crc") == 0) {
p->type = crcType;
- p->expectedChars = 2;
+ if (p->crcAlgorithm == chksumCrc) {
+ p->expectedChars = 1;
+ } else {
+ p->expectedChars = 2;
+ }
p->nextFmt += valen;
return 1;
} else if (strncasecmp(item, "int", 3) == 0) {
@@ -281,6 +303,10 @@ int BinHandler(Ascon *a) {
DynStringConcatChar(dyn, crc % 256);
DynStringConcatChar(dyn, crc / 256);
break;
+ case chksumCrc:
+ crc = calc_chksum(GetCharArray(dyn), l);
+ DynStringConcatChar(dyn, crc % 256);
+ break;
case rsportCrc:
crc = calc_crc8(GetCharArray(dyn), l);
DynStringConcatChar(dyn, crc);
@@ -509,6 +535,11 @@ int BinHandler(Ascon *a) {
DynStringConcat(p->result, "badCRC ");
}
break;
+ case chksumCrc:
+ if (calc_chksum(str, l-1) != (unsigned char)str[l-1]) {
+ DynStringConcat(p->result, "badCRC ");
+ }
+ break;
case kellerCrc:
i = str[l-2];
str[l-2] = str[l-1];
@@ -560,6 +591,8 @@ static int BinInit(Ascon * a, SConnection * con, int argc, char *argv[])
p->crcAlgorithm = syconCrc;
} else if (strcasecmp(argv[2], "rsport-crc") == 0) {
p->crcAlgorithm = rsportCrc;
+ } else if (strcasecmp(argv[2], "chksum-crc") == 0) {
+ p->crcAlgorithm = chksumCrc;
} else if (strcasecmp(argv[2], "modbus-crc") != 0) {
SCPrintf(con, eError, "ERROR: unknown crc-algorithm %s", argv[2]);
a->private = NULL;
diff --git a/callback.c b/callback.c
index d6a2331c..561ef49b 100644
--- a/callback.c
+++ b/callback.c
@@ -271,6 +271,36 @@ int RemoveCallback2(pICallBack self, void *pUserData)
return 1;
}
+/*--------------------------------------------------------------------------*/
+/*
+ * Remove callback in context -
+ * if the callback function matches and the user function returns zero
+ */
+int RemoveCallbackUsr(pICallBack self, SICSCallBack pFunc, int (*userfunc)(const void* pContext, const void* pUserData), void *pCtx)
+{
+ pCallBackItem pItem;
+
+ if (!CheckPointer(self))
+ {
+ return 0;
+ }
+
+ pItem = self->head;
+ while (pItem != NULL) {
+ if (pItem->pFunc == pFunc) {
+ if(userfunc(pCtx, pItem->pUserData) == 0) {
+ if (debug) {
+ printf("Killing callback at %p\n", self);
+ }
+ pItem->killFlag = 1;
+ }
+ }
+ pItem = pItem->next;
+ }
+ cleanCallbackList(self);
+ return 1;
+}
+
/*--------------------------------------------------------------------------*/
int RemoveCallbackCon(pICallBack self, SConnection * con)
{
diff --git a/chadapter.c b/chadapter.c
index 290ebe53..93a8ab4c 100644
--- a/chadapter.c
+++ b/chadapter.c
@@ -177,7 +177,7 @@ static void KillAdapter(void *pData)
if (self->pParName){
free(self->pParName);
}
-
+
free(self);
}
diff --git a/commandlog.c b/commandlog.c
index ed06f88a..64b5c896 100644
--- a/commandlog.c
+++ b/commandlog.c
@@ -538,7 +538,7 @@ int CommandLog(SConnection * pCon, SicsInterp * pSics, void *pData,
if (iCompact > 0)
iIntervall = 0;
}
- SCPrintf(pCon, eValue, "%s.compact [sec] = %d", argv[0], iCompact);
+ SCPrintf(pCon, eValue, "%s.compact [sec] = %d", argv[0], (int)iCompact);
return 1;
} else if (strcmp(argv[1], "close") == 0) { /* close command */
fclose(fd);
diff --git a/confvirtmot.i b/confvirtmot.i
index f1fc5739..931208ab 100644
--- a/confvirtmot.i
+++ b/confvirtmot.i
@@ -17,6 +17,7 @@ typedef struct __CONFVIRTMOT {
float targetValue;
int targetReached;
int posCount;
+ double last_report_time;
char scriptError[512];
int parseOK;
}ConfigurableVirtualMotor, *pConfigurableVirtualMotor;
diff --git a/confvirtualmot.c b/confvirtualmot.c
index 677a6021..4cb60028 100644
--- a/confvirtualmot.c
+++ b/confvirtualmot.c
@@ -35,7 +35,11 @@
#include "confvirtmot.h"
#include "confvirtmot.i"
+#define BAD_VALUE (-9999.99)
+
extern char *stptok(char *s, char *t, int len, char *brk);
+extern int CheckMotiMatch(const void* context, const void* pUserData);
+extern double DoubleTime(void);
/*---------------------------------------------------------------------------
An internal data structure which holds information required to control
@@ -214,6 +218,7 @@ static long ConfSetValue(void *pData, SConnection * pCon, float newValue)
self->targetValue = newValue;
self->targetReached = 0;
self->posCount = 0;
+ self->last_report_time = 0.0;
status = startMotorList(self, pCon);
if (status != OKOK) {
@@ -303,7 +308,6 @@ static int InterestCallback(int iEvent, void *pEvent, void *pUser)
return -1;
}
-
if (pInfo->lastValue != pEventData->fVal) {
pInfo->lastValue = pEventData->fVal;
(pInfo->pCon)->conEventType = POSITION;
@@ -370,12 +374,14 @@ static int ConfCheckStatus(void *pData, SConnection * pCon)
InvokeCallBack(self->pCall, MOTDRIVE, &event);
}
} else if (result == HWBusy) {
- self->posCount++;
- if (self->posCount >= 10 /*ObVal(self->ParArray,MOVECOUNT) */ ) {
+ double current_time, skip_time;
+ current_time = DoubleTime();
+ skip_time = 0.500;
+ if (self->last_report_time + skip_time <= current_time) {
event.pName = self->name;
event.fVal = self->pDriv->GetValue(self, pCon);
InvokeCallBack(self->pCall, MOTDRIVE, &event);
- self->posCount = 0;
+ self->last_report_time = current_time;
}
}
return result;
@@ -431,7 +437,7 @@ static void checkMotorValues(pConfigurableVirtualMotor self,
static float ConfGetValue(void *pData, SConnection * pCon)
{
pConfigurableVirtualMotor self = (pConfigurableVirtualMotor) pData;
- float currentValue = -9999.99;
+ float currentValue = BAD_VALUE;
assert(self != NULL);
@@ -493,6 +499,14 @@ static void KillConfigurableVirtualMotor(void *data)
free(self->state);
self->state = NULL;
}
+ if (self->pCall != NULL) {
+ DeleteCallBackInterface(self->pCall);
+ self->pCall = NULL;
+ }
+ if (self->pDriv != NULL) {
+ free(self->pDriv);
+ self->pDriv = NULL;
+ }
free(self);
self = NULL;
}
@@ -521,6 +535,7 @@ int MakeConfigurableVirtualMotor(SConnection * pCon, SicsInterp * pSics,
pNew->name = strdup(argv[1]);
pNew->posCount = 0;
+ pNew->last_report_time = 0.0;
pNew->pDes = CreateDescriptor("ConfigurableVirtualMotor");
pNew->pDriv = CreateDrivableInterface();
pNew->motorList = LLDcreate(sizeof(RealMotor));
@@ -570,7 +585,6 @@ int ConfigurableVirtualMotorAction(SConnection * pCon, SicsInterp * pSics,
pConfigurableVirtualMotor self = NULL;
char pBueffel[512];
float value;
- int iRet;
long lID;
pRegisteredInfo pRegInfo = NULL;
@@ -685,12 +699,17 @@ int ConfigurableVirtualMotorAction(SConnection * pCon, SicsInterp * pSics,
}
pRegInfo->lastValue = value;
+ RemoveCallbackUsr(self->pCall, InterestCallback, CheckMotiMatch, pCon); /* only this one */
lID = RegisterCallback(self->pCall, MOTDRIVE,
InterestCallback, pRegInfo, KillInfo);
SCSendOK(pCon);
return 1;
+ } else if(strcmp(argv[1],"uninterest") == 0) {
+ RemoveCallbackUsr(self->pCall, InterestCallback, CheckMotiMatch, pCon);
+ SCSendOK(pCon);
+ return 1;
} else {
- snprintf(pBueffel, 5120, "ERROR: subcommand %s to %s unknown",
+ snprintf(pBueffel, sizeof(pBueffel), "ERROR: subcommand %s to %s unknown",
argv[1], argv[0]);
SCWrite(pCon, pBueffel, eError);
return 0;
diff --git a/confvirtualmot.w b/confvirtualmot.w
index 169ac0c1..f0e501b0 100644
--- a/confvirtualmot.w
+++ b/confvirtualmot.w
@@ -22,6 +22,7 @@ typedef struct __CONFVIRTMOT {
float targetValue;
int targetReached;
int posCount;
+ double last_report_time;
char scriptError[512];
int parseOK;
}ConfigurableVirtualMotor, *pConfigurableVirtualMotor;
diff --git a/conman.c b/conman.c
index 88b2ad1f..13a4dd2b 100644
--- a/conman.c
+++ b/conman.c
@@ -3,7 +3,7 @@
Connection management for SICS. This is one the core files for
SICS. Does a lot. See the descriptions with individual functions
below.
-
+
Mark Koennecke, October 1996
@@ -73,6 +73,16 @@
#include "statusfile.h"
#include "sicshipadaba.h"
#include "protocol.h"
+#include "sicsvar.h"
+#include
+
+/*
+ Greetings from protocol.c for SCLogWrite...
+*/
+extern struct json_object *mkJSON_Object(SConnection * pCon, char *pBuffer,
+ int iOut);
+
+
/*
#define UUDEB 1
define UUDEB , for buffer writing for checking encoding */
@@ -81,6 +91,13 @@ extern pServer pServ;
#include "outcode.c" /* text for OutCode */
+int KillCapture(SConnection * pCon);
+
+int LogCapture(SConnection * pCon, SicsInterp * pInter, void *pData,
+ int argc, char *argv[]);
+
+int LogOutput(SConnection * pCon, SicsInterp * pInter, void *pData,
+ int argc, char *argv[]);
/*------ Max Size of Command Stack */
#define MAXSTACK 1024
@@ -108,6 +125,24 @@ struct SCStore {
long macroStack;
commandContext cc;
};
+
+/*
+ * This will return the value of a SICS int variable called "sicsdebug"
+ * If sicsdebug is not defined it will return 1
+ * TODO Maybe define debugging levels.
+ *
+ * return 0=debug off, 1=debug on or sicsdebug not defined.
+ */
+int sicsdebug() {
+ pSicsVariable debug;
+
+ debug = FindVariable(pServ->pSics, "sicsdebug");
+ if (debug) {
+ return debug->iVal;
+ } else {
+ return 1;
+ }
+}
/*===========================================================================*/
static char *ConName(long ident)
{
@@ -229,6 +264,7 @@ static SConnection *CreateConnection(SicsInterp * pSics)
pRes->conStart = time(NULL);
pRes->write = SCNormalWrite;
pRes->runLevel = RUNDRIVE;
+ pRes->remote = 0;
/* initialise context variables */
pRes->iCmdCtr = 0;
@@ -461,6 +497,7 @@ SConnection *SCCopyConnection(SConnection * pCon)
result->iList = -1;
result->runLevel = pCon->runLevel;
result->data = pCon->data;
+ result->remote = pCon->remote;
return result;
}
@@ -650,7 +687,7 @@ int SCPrintf(SConnection * self, int iOut, char *fmt, ...)
va_list ap;
char buf[256];
char *dyn;
- int l;
+ unsigned int l;
int res;
va_start(ap, fmt);
@@ -779,6 +816,64 @@ static int testAndWriteSocket(SConnection * pCon, char *buffer, int iOut)
return 0;
}
+/*--------------------------------------------------------------------------*/
+static int isOK(const char *buffer)
+{
+ if ((buffer[0] == 'O' && buffer[1] == 'K')
+ && (buffer[2] == '\0' || buffer[2] == '\r' || buffer[2] == '\n'))
+ return 1;
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+static void testAndWriteSICSLog(SConnection * self, char *buffer, int iOut)
+{
+ char tp;
+ /* We don't want to log the "OK" messages */
+ if (isOK(buffer))
+ return;
+
+ if (SCinMacro(self)) {
+ tp = 'M';
+ if (sicsdebug())
+ tp = 'D';
+ } else
+ tp = 'N';
+ switch (iOut) {
+ case eStatus:
+ case eStart:
+ case eFinish:
+ case eEvent:
+ case eHdbValue:
+ case eHdbEvent:
+ case eLog:
+ case eLogError:
+ case eError:
+ case eWarning:
+ /* Log it */
+ SICSLogWriteCode(buffer, iOut, tp);
+ return;
+ case eValue:
+ if (sicsdebug() || !SCinMacro(self)) {
+ /* Log it */
+ SICSLogWriteCode(buffer, iOut, tp);
+ return;
+ } else {
+ /* Suppressed */
+#if 0
+ tp = tolower(tp);
+ SICSLogWriteCode(buffer, iOut, tp);
+#endif
+ return;
+ }
+ break;
+ default:
+ printf("Unrecognized ouput code %d in testAndWriteSICSLog: FIXME!!!\n", iOut);
+ SICSLogWriteCode(buffer, iOut, tp);
+ return;
+ }
+}
+
/*--------------------------------------------------------------------------*/
int SCNormalWrite(SConnection * self, char *buffer, int iOut)
{
@@ -793,7 +888,11 @@ int SCNormalWrite(SConnection * self, char *buffer, int iOut)
}
/* log it for any case */
- SICSLogWrite(buffer, iOut);
+#if 0
+ SICSLogWrite(buffer, iOut);
+#else
+ testAndWriteSICSLog(self, buffer, iOut);
+#endif
testAndWriteCommandLog(self, buffer, iOut);
@@ -992,13 +1091,14 @@ int SCPureSockWrite(SConnection * self, char *buffer, int iOut)
{
char pBueffel[1024];
char *pPtr;
+ json_object *myJson = NULL;
/* for commandlog tail */
if (!VerifyConnection(self)) {
return 0;
}
- if(self->iProtocolID == 5) {
+ if(self->iProtocolID == PROTACT) { /* act */
if (strlen(buffer) + 30 > 1024) {
pPtr = (char *) malloc((strlen(buffer) + 30) * sizeof(char));
memset(pPtr, 0, strlen(buffer) + 20);
@@ -1010,6 +1110,12 @@ int SCPureSockWrite(SConnection * self, char *buffer, int iOut)
if(pPtr != pBueffel){
free(pPtr);
}
+ } else if(self->iProtocolID == PROTJSON) {
+ myJson = mkJSON_Object(self,buffer,iOut);
+ if(myJson != NULL){
+ SCDoSockWrite(self,(char *)json_object_to_json_string(myJson));
+ json_object_put(myJson);
+ }
} else {
testAndWriteSocket(self, buffer, iOut);
}
@@ -1023,6 +1129,7 @@ int SCLogWrite(SConnection * self, char *buffer, int iOut)
{
char pBueffel[1024];
char *pPtr;
+ json_object *myJson = NULL;
if (!VerifyConnection(self)) {
return 0;
@@ -1031,7 +1138,7 @@ int SCLogWrite(SConnection * self, char *buffer, int iOut)
WriteToCommandLogId(NULL, self->sockHandle, buffer);
SetSendingConnection(NULL);
- if(self->iProtocolID == 5) {
+ if(self->iProtocolID == PROTACT) { /* act */
if (strlen(buffer) + 30 > 1024) {
pPtr = (char *) malloc((strlen(buffer) + 30) * sizeof(char));
memset(pPtr, 0, strlen(buffer) + 20);
@@ -1043,7 +1150,7 @@ int SCLogWrite(SConnection * self, char *buffer, int iOut)
if(pPtr != pBueffel){
free(pPtr);
}
- } else if(self->iProtocolID == 2) {
+ } else if(self->iProtocolID == PROTCODE) { /* withcode */
if (strlen(buffer) + 30 > 1024) {
pPtr = (char *) malloc((strlen(buffer) + 30) * sizeof(char));
memset(pPtr, 0, strlen(buffer) + 20);
@@ -1055,6 +1162,12 @@ int SCLogWrite(SConnection * self, char *buffer, int iOut)
if(pPtr != pBueffel){
free(pPtr);
}
+ } else if(self->iProtocolID == PROTJSON) { /* json */
+ myJson = mkJSON_Object(self,buffer,iOut);
+ if(myJson != NULL){
+ SCDoSockWrite(self,(char *)json_object_to_json_string(myJson));
+ json_object_put(myJson);
+ }
} else {
testAndWriteSocket(self, buffer, iOut);
}
@@ -1235,7 +1348,7 @@ int SCWriteZipped(SConnection * self, char *pName, void *pData,
memset(outBuf, 0, 65536);
protocolID = GetProtocolID(self);
- if (protocolID == 5) {
+ if (protocolID == PROTACT) {
cc = SCGetContext(self);
sprintf(outBuf, "SICSBIN ZIP %s %d %d\r\n", pName,
compressedLength, cc.transID);
@@ -1310,7 +1423,7 @@ int SCWriteBinary(SConnection * self, char *pName, void *pData,
memset(outBuf, 0, 65536);
protocolID = GetProtocolID(self);
- if (protocolID == 5) {
+ if (protocolID == PROTACT) {
cc = SCGetContext(self);
sprintf(outBuf, "SICSBIN BIN %s %d %d\r\n", pName,
iDataLen, cc.transID);
@@ -1416,7 +1529,7 @@ int SCWriteZippedOld(SConnection * self, char *pName, void *pData,
memset(outBuf, 0, 65536);
protocolID = GetProtocolID(self);
- if (protocolID == 5) {
+ if (protocolID == PROTACT) {
cc = SCGetContext(self);
sprintf(outBuf, "SICSBIN ZIP %s %d %d\r\n", pName,
compressedLength, cc.transID);
@@ -1742,7 +1855,7 @@ int SCInvoke(SConnection * self, SicsInterp * pInter, char *pCommand)
memset(pBueffel, 0, 80);
stptok(trim(pCommand), pBueffel, 79, " ");
self->iCmdCtr++;
- if (999999 < self->iCmdCtr) {
+ if (self->iCmdCtr > 99998) {
self->iCmdCtr = 0;
}
self->transID = self->iCmdCtr;
@@ -1768,6 +1881,7 @@ int SCInvoke(SConnection * self, SicsInterp * pInter, char *pCommand)
config File Filename Logs to another file
config output normal | withcode | ACT Sets output mode
config listen 0 | 1 enables commandlog listen mode
+ config remote sets the remote connection flag
---------------------------------------------------------------------------*/
int ConfigCon(SConnection * pCon, SicsInterp * pSics, void *pData,
@@ -1825,7 +1939,11 @@ int ConfigCon(SConnection * pCon, SicsInterp * pSics, void *pData,
SCSendOK(pCon);
return 1;
}
- }
+ } else if(strcmp(argv[1],"remote") == 0) {
+ pMaster->remote = 1;
+ pCon->remote = 1;
+ return 1;
+ }
/* check no or args */
if (argc < 3) {
@@ -2137,6 +2255,190 @@ int ConSicsAction(SConnection * pCon, SicsInterp * pSics, void *pData,
return 0;
}
+static void hookFunc(const char *pText, OutCode eOut, void*pData)
+{
+ SConnection *pCon = (SConnection *) pData;
+ int text_len = strlen(pText);
+ char txt[5];
+
+ if (!VerifyConnection(pCon)) {
+ return;
+ }
+ if (!ANETvalidHandle(pCon->sockHandle)) {
+ return;
+ }
+ snprintf(txt, 5, "%3s:", OutCodeToTxt(eOut));
+ ANETwrite(pCon->sockHandle, txt, 4);
+ ANETwrite(pCon->sockHandle, (char *)pText, text_len);
+ if (pText[text_len - 1] != '\n')
+ ANETwrite(pCon->sockHandle, "\n", 1);
+}
+
+int KillCapture(SConnection * pCon)
+{
+ RemSICSLogHook(pCon);
+ return 1;
+}
+
+
+/* ------------------------------------------------------------------------
+ the command function:
+ Syntax:
+ Kill kills all logging
+ Log OutCode starts loggin OutCode events
+ All starts logging all events
+-------------------------------------------------------------------------- */
+
+int LogCapture(SConnection * pCon, SicsInterp * pSics, void *pData,
+ int argc, char *argv[])
+{
+ SConnection * pConMaster;
+ char pBueffel[512];
+ int i;
+
+ pConMaster = SCfindMaster(pCon);
+ if (pConMaster == NULL)
+ return 0;
+ /* check no af args */
+ if (argc < 2) {
+ snprintf(pBueffel,sizeof(pBueffel)-1, "Insufficient number of arguments to %s", argv[0]);
+ SCWrite(pCon, pBueffel, eError);
+ return 0;
+ }
+ argtolower(argc, argv);
+
+ /* Branch according to argv[1] */
+ if (strcmp(argv[1], "kill") == 0 || strcmp(argv[1], "none") == 0) {
+ SCPrintf(pCon, eLog, "getlog %s", argv[1]);
+ KillCapture(pConMaster);
+ return 1;
+ } else if (strcmp(argv[1], "what") == 0) {
+ unsigned int code_bits = 0;
+ pDynString buffer;
+ code_bits = (1 << iNoCodes) - 1;
+ buffer = CreateDynString(100, 100);
+ for (i = 0; i < iNoCodes; ++i) {
+ if (code_bits & (1 << i)) {
+ if (GetDynStringLength(buffer) > 0)
+ DynStringConcatChar(buffer, ',');
+ DynStringConcat(buffer, (char *)OutCodeToTxt(i));
+ }
+ }
+ SCPrintf(pCon, eLog, "getlog %s", GetCharArray(buffer));
+ DeleteDynString(buffer);
+ return 1;
+ } else if (strcmp(argv[1], "list") == 0) {
+ unsigned int code_bits = 0;
+ pDynString buffer;
+ code_bits = GetSICSLogHook(pConMaster);
+ if (code_bits == 0) {
+ SCPrintf(pCon, eLog, "getlog none");
+ return 1;
+ }
+ buffer = CreateDynString(100, 100);
+ for (i = 0; i < iNoCodes; ++i) {
+ if (code_bits & (1 << i)) {
+ if (GetDynStringLength(buffer) > 0)
+ DynStringConcatChar(buffer, ',');
+ DynStringConcat(buffer, (char *)OutCodeToTxt(i));
+ }
+ }
+ SCPrintf(pCon, eLog, "getlog %s", GetCharArray(buffer));
+ DeleteDynString(buffer);
+ return 1;
+ } else if (strcmp(argv[1], "all") == 0) {
+ SCPrintf(pCon, eLog, "getlog all");
+ AddSICSLogHook(hookFunc, "all", pConMaster);
+ return 1;
+ } else if (argc == 2) {
+ /* must be outcode, try find it */
+ SCPrintf(pCon, eLog, "getlog %s", argv[1]);
+ AddSICSLogHook(hookFunc, argv[1], pConMaster);
+ return 1;
+ } else {
+ /* make it a list */
+ int i;
+ size_t len;
+ char *pBuff;
+ for (i = 1, len = 0; i < argc; ++i)
+ len += strlen(argv[i]) + 1;
+ if (len > sizeof(pBueffel))
+ pBuff = malloc(len);
+ else
+ pBuff = pBueffel;
+ if (pBuff == NULL) {
+ SCWrite(pCon, "Out of memory in LogCapture\n", eError);
+ return 1;
+ }
+ for (i = 1, len = 0; i < argc; ++i) {
+ if (i > 1)
+ pBuff[len++] = ',';
+ strcpy(&pBuff[len], argv[i]);
+ len += strlen(argv[i]);
+ }
+ SCPrintf(pCon, eLog, "getlog %s", pBuff);
+ AddSICSLogHook(hookFunc, pBuff, pConMaster);
+ if (pBuff != pBueffel)
+ free(pBuff);
+ return 1;
+ }
+ return 0;
+}
+/* ------------------------------------------------------------------------
+ the command function:
+ Syntax:
+ LogOutput [OutCode]
+ Logs with outcode OutCode default eLog
+-------------------------------------------------------------------------- */
+
+int LogOutput(SConnection * pCon, SicsInterp * pSics, void *pData,
+ int argc, char *argv[])
+{
+ char pBueffel[512];
+ char *pBuff;
+ int i, result, start;
+ size_t len;
+ OutCode outcode;
+
+ /* check no af args */
+ if (argc < 2) {
+ snprintf(pBueffel,sizeof(pBueffel)-1, "Insufficient number of arguments to %s", argv[0]);
+ SCWrite(pCon, pBueffel, eError);
+ return 0;
+ }
+
+ /* assume default eLog unless told otherwise */
+ start = 1;
+ outcode = eLog;
+ if (argv[1][0] == '@') {
+ result = OutCodeFromText(&argv[1][1], &outcode);
+ if (result >= 0) {
+ start = 2;
+ }
+ }
+
+ /* make it a string */
+ for (i = start, len = 0; i < argc; ++i)
+ len += strlen(argv[i]) + 1;
+ if (len > sizeof(pBueffel))
+ pBuff = malloc(len+10);
+ else
+ pBuff = pBueffel;
+ if (pBuff == NULL) {
+ SCWrite(pCon, "Out of memory in LogOutput\n", eError);
+ return 1;
+ }
+ for (i = start, len = 0; i < argc; ++i) {
+ if (i > start)
+ pBuff[len++] = ' ';
+ strcpy(&pBuff[len], argv[i]);
+ len += strlen(argv[i]);
+ }
+ SICSLogWrite(pBuff, outcode);
+ if (pBuff != pBueffel)
+ free(pBuff);
+ return 1;
+}
/*--------------------------------------------------------------------------*/
int SCTaskFunction(void *pData)
{
@@ -2207,7 +2509,7 @@ int SCTaskFunction(void *pData)
pPassword = strtok(NULL, " \t\r\n");
iRet = IsValidUser(pUser, pPassword);
if (iRet >= 0) {
- SCWrite(self, "Login OK", eError);
+ SCWrite(self, "Login OK", eLog);
self->iLogin = 1;
SCSetRights(self, iRet);
pHost[0] = '\0';
diff --git a/conman.h b/conman.h
index 91befe22..85c4f319 100644
--- a/conman.h
+++ b/conman.h
@@ -71,6 +71,7 @@ typedef struct __SConnection {
pCosta pStack; /* stack of pending commands */
int contextStack; /* context stack: may go? */
mkChannel *pSock; /* for temporary backwards compatability */
+ int remote; /* true if this is a remote object connection */
} SConnection;
#include "nserver.h"
@@ -92,7 +93,14 @@ SConnection *GetSendingConnection(void);
/* ***************************** I/O ************************************** */
void SCSetOutputClass(SConnection * self, int iClass);
int SCWrite(SConnection * self, char *pBuffer, int iOut);
-int SCPrintf(SConnection * self, int iOut, char *fmt, ...);
+#if __GNUC__ > 2
+#define G_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#else
+#define G_GNUC_PRINTF( format_idx, arg_idx )
+#endif
+int SCPrintf(SConnection * self, int iOut, char *fmt, ...) G_GNUC_PRINTF (3, 4);
+#undef G_GNUC_PRINTF
int SCRead(SConnection * self, char *pBuffer, int iBufLen);
int SCPrompt(SConnection * pCon, char *pPrompt, char *pResult, int iLen);
int SCPromptTMO(SConnection * pCon, char *pPrompt, char *pResult, int iLen, int timeout);
diff --git a/costa.c b/costa.c
index 875d80d5..bbdc17a7 100644
--- a/costa.c
+++ b/costa.c
@@ -108,10 +108,6 @@ int CostaTop(pCosta self, char *pCommand)
assert(self);
- /* check for lock */
- if (self->iLock) {
- return 0;
- }
/* check Size */
if (self->iCount >= self->iMaxSize) {
return 0;
@@ -138,11 +134,6 @@ int CostaBottom(pCosta self, char *pCommand)
int iRet, iRes = 1;
assert(self);
- /* check for lock */
- if (self->iLock) {
- return 0;
- }
-
/* do not want 0 commands */
if (strlen(pCommand) < 1) {
return 1;
@@ -187,3 +178,10 @@ void CostaUnlock(pCosta self)
{
self->iLock = 0;
}
+
+/*--------------------------------------------------------------------------*/
+ int CostaLocked(pCosta self)
+ {
+ return self->iLock;
+ }
+
diff --git a/costa.h b/costa.h
index 5703b210..40bd69c2 100644
--- a/costa.h
+++ b/costa.h
@@ -25,5 +25,6 @@ int CostaPop(pCosta self, char **pPtr);
/*----------------------------------------------------------------------*/
void CostaLock(pCosta self);
void CostaUnlock(pCosta self);
+int CostaLocked(pCosta self);
#endif
diff --git a/countdriv.c b/countdriv.c
index e9a3d6fa..29a46692 100644
--- a/countdriv.c
+++ b/countdriv.c
@@ -1,6 +1,6 @@
/*------------------------------------------------------------------------
G E N C O U N T
-
+
Some general stuff for handling a CounterDriver.
@@ -61,6 +61,7 @@ pCounterDriver CreateCounterDriver(char *name, char *type)
pRes->fPreset = 1000.;
pRes->fTime = 0.;
pRes->iNoOfMonitors = 0;
+ pRes->iControlMonitor = 0;
pRes->iPause = 0;
pRes->Start = NULL;
pRes->GetStatus = NULL;
diff --git a/countdriv.h b/countdriv.h
index adf4344b..66b61443 100644
--- a/countdriv.h
+++ b/countdriv.h
@@ -1,20 +1,20 @@
/*---------------------------------------------------------------------------
C O U N T E R D R I V E R
-
-
+
+
This is the interface to a Sics-Counter driver. This means a
- single counter managing possibly several monitors in one go.
-
+ single counter managing possibly several monitors in one go.
+
A counter can run for a predefined time or until a predefined
monitor count has been reached.
-
+
Mark Koennecke, January 1996
General parameter setting added:
- Mark Koennecke, April 1999
+ Mark Koennecke, April 1999
copyright: see implementation file.
-
+
---------------------------------------------------------------------------*/
#ifndef SICSCOUNTERDRIVER
#define SICSCOUNTERDRIVER
@@ -42,6 +42,7 @@ typedef struct __COUNTER {
float fLastCurrent;
float fTime;
int iNoOfMonitors;
+ int iControlMonitor;
long lCounts[MAXCOUNT];
int iPause;
int iErrorCode;
diff --git a/counter.c b/counter.c
index 7b75da54..d01de97c 100644
--- a/counter.c
+++ b/counter.c
@@ -551,7 +551,7 @@ static int SetCounterModeImpl(pCounter self, CounterMode eNew)
/*-------------------------------------------------------------------------*/
CounterMode GetCounterMode(pCounter self)
{
- return self->getMode(self);
+ return self->getMode(self);
}
/*--------------------------------------------------------------------------*/
static CounterMode GetCounterModeImpl(pCounter self)
@@ -562,7 +562,7 @@ static CounterMode GetCounterModeImpl(pCounter self)
/*------------------------------------------------------------------------*/
int GetNMonitor(pCounter self)
{
- return self->getNMonitor(self);
+ return self->getNMonitor(self);
}
/*------------------------------------------------------------------------*/
static int GetNMonitorImpl(pCounter self)
@@ -571,13 +571,28 @@ static int GetNMonitorImpl(pCounter self)
return self->pDriv->iNoOfMonitors;
}
+int GetControlMonitor(pCounter self)
+{
+ return self->pDriv->iControlMonitor;
+}
+
+int SetControlMonitor(pCounter self, int channel)
+{
+ int maxchan = self->pDriv->iNoOfMonitors - 1;
+ if (channel < 0 || channel > maxchan) {
+ return 0;
+ }
+ self->pDriv->iControlMonitor = channel;
+ return 1;
+}
+
#ifdef NONINTF
extern float nintf(float f);
#endif
/*------------------------------------------------------------------------*/
int SetCounterPreset(pCounter self, float fVal)
{
- return self->setPreset(self,fVal);
+ return self->setPreset(self,fVal);
}
/*------------------------------------------------------------------------*/
static int SetCounterPresetImpl(pCounter self, float fVal)
@@ -603,17 +618,17 @@ static int SetCounterPresetImpl(pCounter self, float fVal)
/*------------------------------------------------------------------------*/
float GetCounterPreset(pCounter self)
{
- return self->getPreset(self);
+ return self->getPreset(self);
}
/*------------------------------------------------------------------------*/
float GetControlValue(pCounter self)
{
- return self->getControlValue(self);
+ return self->getControlValue(self);
}
/*------------------------------------------------------------------------*/
static float GetControlValueImpl(pCounter self)
{
- return self->pDriv->fLastCurrent;
+ return self->pDriv->fLastCurrent;
}
/*------------------------------------------------------------------------*/
static float GetCounterPresetImpl(pCounter self)
@@ -634,7 +649,7 @@ static float GetCounterPresetImpl(pCounter self)
/*-----------------------------------------------------------------------*/
long GetCounts(pCounter self, SConnection * pCon)
{
- return self->getCounts(self, pCon);
+ return self->getCounts(self, pCon);
}
/*-----------------------------------------------------------------------*/
static long GetCountsImpl(pCounter self, SConnection * pCon)
@@ -643,12 +658,12 @@ static long GetCountsImpl(pCounter self, SConnection * pCon)
if (!self->isUpToDate) {
self->pCountInt->TransferData(self, pCon);
}
- return self->pDriv->lCounts[0];
+ return self->pDriv->lCounts[self->pDriv->iControlMonitor];
}
/*------------------------------------------------------------------------*/
long GetMonitor(pCounter self, int iNum, SConnection * pCon)
{
- return self->getMonitor(self, iNum, pCon);
+ return self->getMonitor(self, iNum, pCon);
}
/*------------------------------------------------------------------------*/
static long GetMonitorImpl(pCounter self, int iNum, SConnection * pCon)
@@ -658,7 +673,7 @@ static long GetMonitorImpl(pCounter self, int iNum, SConnection * pCon)
if (!self->isUpToDate) {
self->pCountInt->TransferData(self, pCon);
}
- if ((iNum < 0) || (iNum > self->pDriv->iNoOfMonitors)) {
+ if ((iNum < 0) || (iNum >= self->pDriv->iNoOfMonitors)) {
return -1L;
} else {
return self->pDriv->lCounts[iNum];
@@ -667,7 +682,7 @@ static long GetMonitorImpl(pCounter self, int iNum, SConnection * pCon)
/*-----------------------------------------------------------------------*/
void SetMonitorValue(pCounter self, int index, long value)
{
- return self->setMonitor(self, index, value);
+ return self->setMonitor(self, index, value);
}
/*-----------------------------------------------------------------------*/
static void SetMonitorValueImpl(pCounter self, int index, long value)
@@ -681,7 +696,7 @@ static void SetMonitorValueImpl(pCounter self, int index, long value)
/*------------------------------------------------------------------------*/
float GetCountTime(pCounter self, SConnection * pCon)
{
- return self->getTime(self, pCon);
+ return self->getTime(self, pCon);
}
/*------------------------------------------------------------------------*/
static float GetCountTimeImpl(pCounter self, SConnection * pCon)
@@ -832,6 +847,8 @@ int CountAction(SConnection * pCon, SicsInterp * pSics, void *pData,
{"getnmon", 0, {0, 0}},
{"state", 0, {0, 0}},
{"error", 0, {0, 0}},
+ {"getchannel",0,{0}},
+ {"setchannel",1,{FUPAINT}},
{"countstatus", 0, {0, 0}}
};
char *pMode[] = {
@@ -849,8 +866,8 @@ int CountAction(SConnection * pCon, SicsInterp * pSics, void *pData,
argtolower(argc, argv);
argx = &argv[1];
iRet =
- EvaluateFuPa((pFuncTemplate) & ActionTemplate, 26, argc - 1, argx,
- &PaRes);
+ EvaluateFuPa((pFuncTemplate) & ActionTemplate, 28, argc - 1, argx,
+ &PaRes);
if (iRet < 0) {
snprintf(pBueffel, 255,"%s", PaRes.pError);
SCWrite(pCon, pBueffel, eError);
@@ -958,7 +975,7 @@ int CountAction(SConnection * pCon, SicsInterp * pSics, void *pData,
SCSendOK(pCon);
return 1;
case 11: /* status */
- case 25:
+ case 27:
self->pCountInt->TransferData(self, pCon);
if (GetCounterMode(self) == ePreset) {
lVal = GetCounterPreset(self);
@@ -1144,8 +1161,8 @@ int CountAction(SConnection * pCon, SicsInterp * pSics, void *pData,
iRet = self->pDriv->Get(self->pDriv, PaRes.Arg[0].text,
PaRes.Arg[1].iVal, &fVal);
if (iRet == 1) {
- snprintf(pBueffel,255, "%s.%s %d = %f", argv[0], PaRes.Arg[0].text,
- PaRes.Arg[1].iVal, fVal);
+ snprintf(pBueffel,255, "%s.%s %s = %f", argv[0], PaRes.Arg[0].text,
+ PaRes.Arg[1].text, fVal);
SCWrite(pCon, pBueffel, eValue);
return 1;
} else {
@@ -1172,6 +1189,19 @@ int CountAction(SConnection * pCon, SicsInterp * pSics, void *pData,
SCWrite(pCon, pBueffel, eValue);
return 1;
break;
+ case 25: /* getchannel */
+ snprintf(pBueffel,131,"%s.getchannel = %d", argv[0], GetControlMonitor(self));
+ SCWrite(pCon,pBueffel,eValue);
+ return 1;
+ break;
+ case 26: /* setchannel */
+ if (SetControlMonitor(self, PaRes.Arg[0].iVal)) {
+ return 1;
+ } else {
+ SCWrite(pCon,"ERROR: Invalid channel id",eError);
+ return 0;
+ }
+ break;
default:
assert(0); /* internal error */
}
diff --git a/counter.h b/counter.h
index 298dc7dc..81105e98 100644
--- a/counter.h
+++ b/counter.h
@@ -66,6 +66,8 @@ float GetControlValue(pCounter self);
long GetCounts(pCounter self, SConnection * pCon);
long GetMonitor(pCounter self, int iNum, SConnection * pCon);
int GetNMonitor(pCounter self);
+int GetControlMonitor(pCounter self);
+int SetControlMonitor(pCounter self, int channel);
void SetMonitorValue(pCounter self, int index, long value);
float GetCountTime(pCounter self, SConnection * pCon);
diff --git a/countersec.c b/countersec.c
index 5eb78ad5..2ec4f3e8 100644
--- a/countersec.c
+++ b/countersec.c
@@ -176,11 +176,28 @@ static int SecCtrCheckStatus(void *pData, SConnection *pCon)
}
ReleaseHdbValue(&v);
-
- node = GetHipadabaNode(self->pDes->parNode,"control");
- assert(node != NULL);
- GetHipadabaPar(node,&v,pCon);
- fControl = v.v.doubleValue;
+
+ if(self->getMode(self) == eTimer){
+ node = GetHipadabaNode(self->pDes->parNode,"time");
+ assert(node != NULL);
+ GetHipadabaPar(node,&v,pCon);
+ fControl = v.v.doubleValue;
+ } else {
+ node = GetHipadabaNode(self->pDes->parNode,"values");
+ if(node != NULL) {
+ /*
+ This can be NULL if the counter is a HM. The values does not
+ exist and fControl is useless
+
+ The 1 below is only correct for PSI where only the first
+ monitor can be the control monitor. Elsewhere this must be the
+ control monitor channel
+ */
+ fControl = v.v.intArray[1];
+ }
+ }
+
+
node = GetHipadabaNode(self->pDes->parNode,"preset");
assert(node != NULL);
GetHipadabaPar(node,&v,pCon);
diff --git a/devexec.c b/devexec.c
index 52d51632..e9b9121b 100644
--- a/devexec.c
+++ b/devexec.c
@@ -785,7 +785,7 @@ int DevexecAction(SConnection * pCon, SicsInterp * pSics, void *pData,
pExeList self = (pExeList) pData;
if (argc < 2) {
- SCWrite(pCon, "ERROR: not enough argumentd to devexec command",
+ SCWrite(pCon, "ERROR: not enough arguments to devexec command",
eError);
return 0;
}
diff --git a/devser.c b/devser.c
index fc1f90b2..bb6a7a92 100644
--- a/devser.c
+++ b/devser.c
@@ -151,9 +151,6 @@ static DevAction *DevNextAction(DevSer * devser)
}
static void LogStart(DevSer *self)
{
- if(self->startTime > 0){
- printf("DEVSER: there is something fucked up in LogStart. Investigate!\n");
- }
self->startTime = DoubleTime();
}
static void LogResponse(DevSer *self, int error)
diff --git a/diffscan.c b/diffscan.c
index ce76a955..87e1b616 100644
--- a/diffscan.c
+++ b/diffscan.c
@@ -15,6 +15,8 @@
#include "drive.h"
#include "counter.h"
+extern double DoubleTime(void);
+
#define DIFFMONITOR 0
#define SKIP 1
@@ -121,6 +123,57 @@ int DiffScanWrapper(SConnection * pCon, SicsInterp * pSics, void *pData,
return status;
}
+ if (strcasecmp(argv[1], "sicsvar") == 0) {
+ if (argc > 2) {
+ if (strcasecmp(argv[2], "none") == 0) {
+ self->sicsvar = NULL;
+ return 1;
+ } else {
+ self->sicsvar = FindVariable(pServ->pSics, argv[2]);
+ if (self->sicsvar == NULL) {
+ SCPrintf(pCon, eError, "Cannot find SICS Variable: %s\n", argv[2]);
+ return 0;
+ }
+ else if (self->sicsvar->eType != veText) {
+ SCPrintf(pCon, eError, "SICS Variable must be TEXT not %s\n",
+ self->sicsvar->eType == veInt ? "INT" :
+ self->sicsvar->eType == veFloat ? "FLOAT" : "unknown");
+ self->sicsvar = NULL;
+ return 0;
+ }
+ }
+ return 1;
+ } else {
+ SCPrintf(pCon, eValue, "sicsvar = %s\n", self->sicsvar ? self->sicsvar->name : "none");
+ return 1;
+ }
+ }
+ if (strcasecmp(argv[1], "hdbnode") == 0) {
+ if (argc > 2) {
+ if (strcasecmp(argv[2], "none") == 0) {
+ self->hdbnode = NULL;
+ return 1;
+ } else {
+ self->hdbnode = FindHdbNode(NULL, argv[2], pCon);
+ if (self->hdbnode == NULL) {
+ SCPrintf(pCon, eError, "Cannot find HDB Node: %s\n", argv[2]);
+ return 0;
+ }
+ else if (self->hdbnode->value.dataType != HIPTEXT) {
+ SCPrintf(pCon, eError, "HDB Node %s must be TEXT\n", argv[2]);
+ self->hdbnode = NULL;
+ return 0;
+ }
+ }
+ return 1;
+ } else {
+ char *path = NULL;
+ if (self->hdbnode != NULL)
+ path = GetHipadabaPath(self->hdbnode);
+ SCPrintf(pCon, eValue, "hdbnode = %s\n", path ? path : "none");
+ return 1;
+ }
+ }
/*
if we end here we are treating variables
*/
@@ -180,6 +233,7 @@ static int StartDiffScan(pDiffScan self, pScanData pScan,
self->scaleMonitor = (int) ObVal(self->parArray, DIFFMONITOR);
self->normalizationScale = -1;
pScan->iCounts = 0;
+ pScan->iNP = 0;
/*
get variable
@@ -190,6 +244,7 @@ static int StartDiffScan(pDiffScan self, pScanData pScan,
SCWrite(pCon, "ERROR: cannot access scan variable", eError);
return 0;
}
+ InitScanVar(pVar);
/*
drive to start position
@@ -250,6 +305,9 @@ static float normalizeEntry(pCountEntry pCount, pCountEntry last,
pCount->Monitors[i] =
(pCount->Monitors[i] - last->Monitors[i]) * fScale;
}
+ fScale = pCount->fTime - last->fTime;
+ if (fScale > 0)
+ value /= fScale;
return value;
}
@@ -271,12 +329,26 @@ static int DiffScanTask(void *pData)
char pBueffel[255];
long rawCount, rawMon;
CountEntry rawCopy;
+ double now;
pDiffScan self = (pDiffScan) pData;
/*
manage skip
*/
+#if 1
+ now = DoubleTime();
+ if (self->skip > 0) {
+ if (now - self->last_report_time < 0.001 * self->skip) {
+ return 1;
+ }
+ } else {
+ if (now - self->last_report_time < 0.1) {
+ return 1;
+ }
+ }
+ self->last_report_time = now;
+#else
if (self->skip > 0) {
if (self->skipCount > self->skip) {
self->skipCount = 0;
@@ -285,6 +357,7 @@ static int DiffScanTask(void *pData)
return 1;
}
}
+#endif
/*
read motor status
@@ -303,7 +376,7 @@ static int DiffScanTask(void *pData)
status = GetDrivablePosition(pVar->pObject, self->scanObject->pCon,
&fPos);
if (status == 0) {
- ReleaseCountLock(pCount->pCountInt);
+ ReleaseCountLock(pCount->pCountInt);
return finish;
}
AppendScanVar(pVar, fPos);
@@ -323,21 +396,21 @@ static int DiffScanTask(void *pData)
rawCount = data->lCount;
rawMon = data->Monitors[self->scaleMonitor - 1];
if(rawMon > 100){
- self->normalizationScale = rawMon;
- traceSys("diffscan","START:normalizing on %d, scale monitor is %d",
- self->normalizationScale, self->scaleMonitor);
+ self->normalizationScale = rawMon;
+ traceSys("diffscan","START:normalizing on %d, scale monitor is %d",
+ self->normalizationScale, self->scaleMonitor);
} else {
- traceSys("diffscan","START:normalization count %d, on scale monitor is %d and is to low",
- rawMon, self->scaleMonitor);
- SCWrite(self->scanObject->pCon,"WARNING: Skipping first point because of low count rate", eWarning);
- return finish;
+ traceSys("diffscan","START:normalization count %ld, on scale monitor is %d and is to low",
+ rawMon, self->scaleMonitor);
+ SCWrite(self->scanObject->pCon,"WARNING: Skipping first point because of low count rate", eWarning);
+ return finish;
}
} else {
if (data->Monitors[self->scaleMonitor - 1] -
self->last.Monitors[self->scaleMonitor - 1] < 5) {
SCWrite(self->scanObject->pCon, "WARNING: low count rate", eLog);
- traceSys("diffscan","RUN:low monitor difference from %d to %d",data->Monitors[self->scaleMonitor-1],
- self->last.Monitors[self->scaleMonitor -1]);
+ traceSys("diffscan","RUN:low monitor difference from %ld to %ld",data->Monitors[self->scaleMonitor-1],
+ self->last.Monitors[self->scaleMonitor -1]);
}
rawCount = data->lCount;
rawMon = data->Monitors[self->scaleMonitor - 1];
@@ -350,13 +423,26 @@ static int DiffScanTask(void *pData)
/*
print progress
*/
- snprintf(pBueffel, 255, "%5d %12.4f %12.4f RAW: %10ld %10ld",
+ snprintf(pBueffel, 255, "%5d %12.4f %12.4f RAW: %10ld %10ld %10.3f",
self->scanObject->iCounts - 1,
- fPos, countValue, rawCount, rawMon);
+ fPos, countValue, rawCount, rawMon, data->fTime);
SCWrite(self->scanObject->pCon, pBueffel, eLog);
+ if (self->sicsvar != NULL && self->sicsvar->eType == veText)
+ VarSetText(self->sicsvar, pBueffel, usInternal);
+ if (self->hdbnode != NULL && self->hdbnode->value.dataType == HIPTEXT) {
+ hdbValue v;
+ char error[512];
+ cloneHdbValue(&self->hdbnode->value, &v);
+ if (!readHdbValue(&v, pBueffel, error, 512)) {
+ SCWrite(self->scanObject->pCon, error, eError);
+ } else {
+ UpdateHipadabaPar(self->hdbnode, v, self->scanObject->pCon);
+ }
+ ReleaseHdbValue(&v);
+ }
InvokeCallBack(self->scanObject->pCall, SCANPOINT, self->scanObject);
- traceSys("diffscan","RUN: pos, count, rawcount, rawmon: %f, %d, %d, %d",
- fPos, countValue, rawCount, rawMon);
+ traceSys("diffscan","RUN: pos, count, rawcount, rawmon: %f, %f, %ld, %ld",
+ fPos, countValue, rawCount, rawMon);
/*
check for interrupt
@@ -369,7 +455,7 @@ static int DiffScanTask(void *pData)
}
if(finish == 0) {
- ReleaseCountLock(pCount->pCountInt);
+ ReleaseCountLock(pCount->pCountInt);
}
return finish;
@@ -385,6 +471,7 @@ int RunDiffScan(pDiffScan self, pScanData pScan,
if (StartDiffScan(self, pScan, pCon, fEnd) != 1) {
return 0;
}
+ self->last_report_time = DoubleTime();
InvokeCallBack(self->scanObject->pCall, SCANSTART, self->scanObject);
diff --git a/diffscan.h b/diffscan.h
index 75adce42..b4e3c707 100644
--- a/diffscan.h
+++ b/diffscan.h
@@ -12,6 +12,9 @@
#include "obpar.h"
#include "scan.h"
#include "scan.i"
+#include "sicsvar.h"
+#include "hipadaba.h"
+#include "sicshipadaba.h"
typedef struct {
pObjectDescriptor pDes;
@@ -22,6 +25,9 @@ typedef struct {
int skip;
int skipCount;
pScanData scanObject;
+ double last_report_time;
+ pSicsVariable sicsvar;
+ pHdb hdbnode;
} DiffScan, *pDiffScan;
/*==================================================================*/
diff --git a/doc/manager/managerman.aux b/doc/manager/managerman.aux
deleted file mode 100644
index bfff1d09..00000000
--- a/doc/manager/managerman.aux
+++ /dev/null
@@ -1,110 +0,0 @@
-\relax
-\@writefile{toc}{\contentsline {chapter}{\numberline {1}Introduction}{4}}
-\@writefile{lof}{\addvspace {10\p@ }}
-\@writefile{lot}{\addvspace {10\p@ }}
-\@writefile{toc}{\contentsline {chapter}{\numberline {2}SICS programs, Scripts and Prerequisites}{5}}
-\@writefile{lof}{\addvspace {10\p@ }}
-\@writefile{lot}{\addvspace {10\p@ }}
-\newlabel{f0}{{2}{5}}
-\@writefile{toc}{\contentsline {section}{\numberline {2.1}Hardware}{5}}
-\@writefile{toc}{\contentsline {section}{\numberline {2.2}Server programs}{5}}
-\@writefile{toc}{\contentsline {chapter}{\numberline {3}General SICS Setup}{7}}
-\@writefile{lof}{\addvspace {10\p@ }}
-\@writefile{lot}{\addvspace {10\p@ }}
-\@writefile{toc}{\contentsline {section}{\numberline {3.1}System Control}{8}}
-\@writefile{toc}{\contentsline {section}{\numberline {3.2}Moving SICS}{9}}
-\newlabel{f2}{{3.2}{9}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.2.1} Moving the SICS Server to a new Computer}{9}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.2.2}Exchanging the Serial Port Server}{9}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.2.3}Exchanging the Histogram Memory}{10}}
-\@writefile{toc}{\contentsline {section}{\numberline {3.3}SICS Trouble Shooting }{10}}
-\newlabel{f3}{{3.3}{10}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.1}Check Server Status}{10}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.2}Inspecting Log Files}{10}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.3}Restarting SICS}{11}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.4}Restart Everything}{11}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.5}Starting SICS Manually}{11}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.6}Test the SerPortServer Program}{11}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.7}Trouble with Environment Devices}{12}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.8} HELP debugging!!!!}{12}}
-\@writefile{toc}{\contentsline {chapter}{\numberline {4}The SICS Initialization File}{13}}
-\@writefile{lof}{\addvspace {10\p@ }}
-\@writefile{lot}{\addvspace {10\p@ }}
-\@writefile{toc}{\contentsline {section}{\numberline {4.1}Overview of SICS Initialization}{13}}
-\newlabel{f4}{{4.1}{13}}
-\@writefile{toc}{\contentsline {section}{\numberline {4.2}SICS Options and Users}{14}}
-\newlabel{f5}{{4.2}{14}}
-\@writefile{toc}{\contentsline {section}{\numberline {4.3}SICS Variables }{15}}
-\newlabel{f6}{{4.3}{15}}
-\@writefile{toc}{\contentsline {section}{\numberline {4.4}SICS Hardware Configuration}{16}}
-\newlabel{f7}{{4.4}{16}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.4.1}Bus Access}{16}}
-\@writefile{toc}{\contentsline {subsubsection}{Direct Access to RS232 Controllers or TCP/IP Controllers.}{16}}
-\@writefile{toc}{\contentsline {subsubsection}{Accessing Serial Ports (Old System)}{17}}
-\@writefile{toc}{\contentsline {subsubsection}{GPIB Controller Access}{19}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.4.2}Controllers}{20}}
-\@writefile{toc}{\contentsline {subsubsection}{ECB Controllers}{20}}
-\@writefile{toc}{\contentsline {subsubsection}{Siematic SPS Controllers}{21}}
-\@writefile{toc}{\contentsline {subsubsection}{General Controller Object and Choppers}{22}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.4.3} Motors}{23}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.4.4}Counting Devices}{24}}
-\@writefile{toc}{\contentsline {subsubsection}{Histogram Memory}{25}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.4.5}Velocity Selectors}{26}}
-\@writefile{toc}{\contentsline {section}{\numberline {4.5}Initialization of General Commands}{27}}
-\newlabel{f10}{{4.5}{27}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.5.1}Monochromators}{28}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.5.2}Reoccuring Tasks}{28}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.5.3}The SICS Online Help System}{29}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.5.4}Aliases in SICS}{29}}
-\@writefile{toc}{\contentsline {subsubsection}{Object Aliases}{29}}
-\@writefile{toc}{\contentsline {subsubsection}{Runtime Aliases}{29}}
-\@writefile{toc}{\contentsline {subsubsection}{Command Aliases}{30}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.5.5}The AntiCollision Module}{30}}
-\@writefile{toc}{\contentsline {section}{\numberline {4.6}The Internal Scan Commands}{31}}
-\newlabel{f11}{{4.6}{31}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.6.1}Scan Concepts}{31}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.6.2}User Definable Scan Functions}{34}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.6.3}User Defined Scans(Old Style)}{35}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.6.4}The Scan Command Header Description File}{35}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.6.5}Differential Scans}{36}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.6.6}Peak Analysis}{37}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.6.7}Common Scan Scripts}{37}}
-\@writefile{toc}{\contentsline {section}{\numberline {4.7}Scripting NeXus Files}{37}}
-\newlabel{f12}{{4.7}{37}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.7.1}Usage}{38}}
-\@writefile{toc}{\contentsline {subsubsection}{File Opening and Closing}{38}}
-\@writefile{toc}{\contentsline {subsubsection}{Writing Things}{38}}
-\@writefile{toc}{\contentsline {section}{\numberline {4.8}Automatic Updating of NeXus Files}{39}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.8.1}Prerequisites for Using the Automatic Update Manager}{39}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.8.2}Installing Automatic Update}{40}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.8.3}Configuring Automatic Update}{40}}
-\@writefile{toc}{\contentsline {section}{\numberline {4.9}Instrument Specific SICS Initializations}{40}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.9.1}Initialization for Four Circle Diffractometers}{40}}
-\newlabel{f13}{{4.9.1}{40}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.9.2}Triple Axis Spectrometer Specific Commands}{42}}
-\newlabel{f14}{{4.9.2}{42}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.9.3}Special Commands for the Reflectometer (AMOR)}{42}}
-\newlabel{f15}{{4.9.3}{42}}
-\@writefile{toc}{\contentsline {subsubsection}{AMOR Status Display Commands}{43}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.9.4}SANS Special Commands}{44}}
-\newlabel{f16}{{4.9.4}{44}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {4.9.5}Special FOCUS Initializations}{45}}
-\newlabel{f17}{{4.9.5}{45}}
-\@writefile{toc}{\contentsline {subsubsection}{Special Internal FOCUS Support Commands}{45}}
-\@writefile{toc}{\contentsline {chapter}{\numberline {5}Programming SICS Macros}{46}}
-\@writefile{lof}{\addvspace {10\p@ }}
-\@writefile{lot}{\addvspace {10\p@ }}
-\newlabel{f18}{{5}{46}}
-\@writefile{toc}{\contentsline {section}{\numberline {5.1}Input/Output}{46}}
-\@writefile{toc}{\contentsline {section}{\numberline {5.2}Error Handling}{47}}
-\@writefile{toc}{\contentsline {section}{\numberline {5.3}Interacting with SICS within a Script}{47}}
-\@writefile{toc}{\contentsline {section}{\numberline {5.4}SICS Interfaces in Tcl}{47}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {5.4.1}The Object Interface}{47}}
-\@writefile{toc}{\contentsline {subsection}{\numberline {5.4.2}Overriding the Drivable Interface with Tcl}{48}}
-\@writefile{toc}{\contentsline {chapter}{\numberline {6}The McStas SICS Interface}{49}}
-\@writefile{lof}{\addvspace {10\p@ }}
-\@writefile{lot}{\addvspace {10\p@ }}
-\newlabel{f8}{{6}{49}}
-\@writefile{toc}{\contentsline {section}{\numberline {6.1}McStas Requirements and SICS Requirements}{50}}
-\@writefile{toc}{\contentsline {section}{\numberline {6.2}The McStas Reader}{50}}
-\@writefile{toc}{\contentsline {section}{\numberline {6.3}The McStas Controller}{51}}
diff --git a/doc/manager/managerman.pdf b/doc/manager/managerman.pdf
deleted file mode 100644
index 90ed39df..00000000
Binary files a/doc/manager/managerman.pdf and /dev/null differ
diff --git a/doc/programmer/...aux b/doc/programmer/...aux
deleted file mode 100644
index f23e5468..00000000
--- a/doc/programmer/...aux
+++ /dev/null
@@ -1 +0,0 @@
-\relax
diff --git a/doc/programmer/command.tex b/doc/programmer/command.tex
index 95382780..b8467eb6 100644
--- a/doc/programmer/command.tex
+++ b/doc/programmer/command.tex
@@ -226,7 +226,7 @@ A valid SICS object structure has to look like this:
Please note that the first item in the data structure MUST be a
pointer to an SICS object descriptor. Add your own stuff below
that. If you do not adhere to this requirement, SICS will dump core on
-you rather sooner then later.
+you rather sooner than later.
SICS needs this object descriptor for its own internal purposes. The
@@ -566,7 +566,7 @@ This code also shows the necessary error checking. It also shows how
to check for possible interrupts after such an operation. It is very
advisable to do this because the user may have interrupted the
process. And she might not be all to happy if the new command just
-continues with the next step rather then aborting the process.
+continues with the next step rather than aborting the process.
\section{SICS Interfaces}\label{interface}\label{inter}
diff --git a/doc/programmer/motor.tex b/doc/programmer/motor.tex
index a7fb83b8..c5e3f695 100644
--- a/doc/programmer/motor.tex
+++ b/doc/programmer/motor.tex
@@ -8,7 +8,7 @@ subdivided into a driver and the logical object.
There is a problem here. There are some data fields and functions
which must be present for any motor driver. Then there are fields
which are specific just to a special implementation of a mot
-driver. There are several ways to deal with this. The way choosen for
+driver. There are several ways to deal with this. The way chosen for
the motor driver is a kind of overlay. The first few fields of a valid
motor driver structure MUST be specified in the same order as given
below. A special motor driver can add additional fields at the end of
@@ -122,12 +122,12 @@ creates a simulation motor driver.
\end{description}
\subsubsection{The Motor Logical Object}
-The motor object represents the motor to SICS. One of its responsabilities
+The motor object represents the motor to SICS. One of its responsibilities
is to drive motor operations and error checking. The scheme
implemented is that the motor object tries to bring the motor to its
position at least three times before a failure is recorded. Also the
motor object keeps track of a count of failed operations. If this
-count gets to high an interrupt is issued to stop the instrument. This
+count gets too high an interrupt is issued to stop the instrument. This
was put in after Druechal tried to drive over a slab of concrete for a
whole night and subsequently broke a clutch.
Motors are represented by the
@@ -164,7 +164,7 @@ object.
Much of the action of the motor is hidden in the implementation of the
drivable interface to the motor. Additionally the functions as given below
are defined. All functions take a pointer to the motor object data structure
-as a parameter. They retun 0 on success or 1 on failure while not stated
+as a parameter. They return 0 on success or 1 on failure while not stated
otherwise.
\begin{description}
\item[int MotorGetPar(pMotor self, char *name, float *fVal)] retrieves the
diff --git a/doc/user/hrptdev.htm b/doc/user/hrptdev.htm
index 60e16abb..92227721 100755
--- a/doc/user/hrptdev.htm
+++ b/doc/user/hrptdev.htm
@@ -1,47 +1,47 @@
-
-
-HRPT motor list
-
-
-HRPT motor list
-
-
-- CEX1
-
- inner collimator drum
-
- CEX2
-
- outer collimator drum
-
- MOMU, A1
-
- omega rotation of upper monochromator crystal.
-
- MTVU, A12
-
- translation vertical to the upper crystal.
-
- MTPU, A13
-
- translation paralell to the upper crystal
-
- MGVU, A14
-
- tilt goniometer vertical to upper crystal.
-
- MGPU, A15
-
- tilt goniometer paralell to upper crystal.
-
- MCVU, A16
-
- vertical curvature of upper crystal.
-
- MOML, B1
-
- omega rotation of lower monochromator crystal.
-
- MTVL, A22
-
- translation vertical to the lower crystal.
-
- MTPL, A23
-
- translation paralell to the lower crystal
-
- MGVL, A24
-
- tilt goniometer vertical to lower crystal.
-
- MGPL, A25
-
- tilt goniometer paralell to lower crystal.
-
- MCVL, A26
-
- vertical curvature of lower crystal.
-
- MEXZ, A37
-
- lift
-
- Table, A3
-
- Sample rotation.
-
- TwoThetaD, A4
-
- Two Theta detector.
-
-
-
-
-
+
+
+HRPT motor list
+
+
+HRPT motor list
+
+
+- CEX1
+
- inner collimator drum
+
- CEX2
+
- outer collimator drum
+
- MOMU, A1
+
- omega rotation of upper monochromator crystal.
+
- MTVU, A12
+
- translation vertical to the upper crystal.
+
- MTPU, A13
+
- translation paralell to the upper crystal
+
- MGVU, A14
+
- tilt goniometer vertical to upper crystal.
+
- MGPU, A15
+
- tilt goniometer paralell to upper crystal.
+
- MCVU, A16
+
- vertical curvature of upper crystal.
+
- MOML, B1
+
- omega rotation of lower monochromator crystal.
+
- MTVL, A22
+
- translation vertical to the lower crystal.
+
- MTPL, A23
+
- translation paralell to the lower crystal
+
- MGVL, A24
+
- tilt goniometer vertical to lower crystal.
+
- MGPL, A25
+
- tilt goniometer paralell to lower crystal.
+
- MCVL, A26
+
- vertical curvature of lower crystal.
+
- MEXZ, A37
+
- lift
+
- Table, A3
+
- Sample rotation.
+
- TwoThetaD, A4
+
- Two Theta detector.
+
+
+
+
+
diff --git a/drive.c b/drive.c
index 43e0d44f..e49aef99 100644
--- a/drive.c
+++ b/drive.c
@@ -1,7 +1,7 @@
/*--------------------------------------------------------------------------
Implementation file for the drive command
-
+
This version as Test for Device Executor.
Mark Koennecke, December 1996
@@ -39,6 +39,7 @@
#include
#include
#include
+#include
#include "fortify.h"
#include "sics.h"
#include "drive.h"
@@ -66,7 +67,7 @@ int Drive(SConnection * pCon, SicsInterp * pInter, char *name, float fNew)
/* check if user is allowed to drive */
if (!SCMatchRights(pCon, usUser)) {
- snprintf(pBueffel, 511, "Insuficient Privilege to drive %s", name);
+ snprintf(pBueffel, 511, "Insufficient Privilege to drive %s", name);
SCWrite(pCon, pBueffel, eError);
return 0;
}
@@ -301,7 +302,7 @@ int DriveWrapper(SConnection * pCon, SicsInterp * pSics, void *pData,
return 0;
}
- /* interprete arguments as pairs name value and try to start */
+ /* interpret arguments as pairs (name, value) and check */
for (i = 1; i < argc; i += 2) {
if (argv[i + 1] == NULL) {
snprintf(pBueffel, 511, "ERROR: no value found for driving %s", argv[i]);
@@ -311,7 +312,12 @@ int DriveWrapper(SConnection * pCon, SicsInterp * pSics, void *pData,
iRet = Tcl_GetDouble(tcl_interp, argv[i + 1], &dTarget);
if (iRet == TCL_ERROR) {
SCWrite(pCon, Tcl_GetStringResult(tcl_interp), eError);
- StopExe(GetExecutor(), "ALL");
+ return 0;
+ } else if (!isfinite(dTarget)) {
+ snprintf(pBueffel, 511,
+ "ERROR: target %s value for %s is not a finite number",
+ argv[i + 1], argv[i]);
+ SCWrite(pCon, pBueffel, eError);
return 0;
}
iRet = Start2Run(pCon, pSics, argv[i], RUNDRIVE, dTarget);
diff --git a/dynstring.c b/dynstring.c
index c45a6afd..1c5987a5 100644
--- a/dynstring.c
+++ b/dynstring.c
@@ -101,6 +101,7 @@ void DeleteDynString(pDynString self)
if (self->pBuffer)
free(self->pBuffer);
+ self->iMAGIC = 0;
free(self);
}
diff --git a/errormsg.h b/errormsg.h
index 5f792147..e7630edc 100644
--- a/errormsg.h
+++ b/errormsg.h
@@ -35,7 +35,14 @@ typedef struct {
* \return the new error message list head
*/
-void ErrPutMsg(ErrList *list, char *fmt, ...);
+#if __GNUC__ > 2
+#define G_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#else
+#define G_GNUC_PRINTF( format_idx, arg_idx )
+#endif
+void ErrPutMsg(ErrList *list, char *fmt, ...) G_GNUC_PRINTF (2, 3);
+#undef G_GNUC_PRINTF
/** \brief Get the most recent error message
* \param list the error list
diff --git a/exebuf.c b/exebuf.c
index 69e2fbf9..aefa8b7c 100644
--- a/exebuf.c
+++ b/exebuf.c
@@ -178,7 +178,7 @@ static pDynString findBlockEnd(pExeBuf self)
{
pDynString command = NULL;
char *buffer = NULL;
- int i;
+ int i, j;
assert(self);
@@ -190,7 +190,8 @@ static pDynString findBlockEnd(pExeBuf self)
if (self->end != -1) {
self->start = self->end + 1;
}
- for (i = self->start; i < strlen(buffer); i++) {
+ j = strlen(buffer);
+ for (i = self->start; i < j; i++) {
DynStringConcatChar(command, buffer[i]);
if (buffer[i] == '\n') {
self->lineno++;
diff --git a/fortify.doc b/fortify.doc
index 9d2fcf90..7b48a986 100644
--- a/fortify.doc
+++ b/fortify.doc
@@ -1,409 +1,409 @@
-
- Fortify - fortified memory allocation shell for C and C++
- ---------------------------------------------------------
- written by Simon P. Bullen
- Cybergraphic
-
- Release 1.0, 2/2/1995
-
-
-
-
- This software is not public domain. All material in
- this archive is © Copyright 1995 Simon P. Bullen. The
- software is freely distrubtable, with the condition that no
- more than a nominal fee is charged for media. Everything in
- this distrubution must be kept together, in original,
- unmodified form.
- The files may be modified for your own personal use, but
- modified files may not be distributed.
- The material is provided "as is" without warranty of any
- kind. The author accepts no responsibilty for damage caused
- by this software.
-
-
-
- email: sbullen@ozemail.com.au
- snail: Simon P. Bullen
- PO BOX 12138
- A'Beckett St
- Melbourne 3000
- Australia
-
-
-
- CONTENTS
- --------
- Your archive should have the following files:
-
- file_id.diz
- fortify.c
- fortify.doc
- fortify.h
- read.me
- test.c
- ufortify.h
- ufortify.hpp
- zfortify.cpp
- zfortify.hpp
- ztest.cpp
-
-
-
-
- OVERVIEW
- --------
- Fortify is a descendant of a library I wrote way back in 1990 called
-SafeMem. It is a (fortified) shell for memory allocations. It works with
-the malloc/free family of functions, as well as new/delete in C++. It can
-be adapted to most memory management functions; the original version of
-SafeMem worked only with the Amiga's AllocMem/FreeMem. I haven't been
-writing much Amiga specific software lately, so the Amiga version of
-Fortify is way out of date, hence it's absense from this archive.
-
- Fortify is designed to detect bad things happening, or at the very
-least encourage intermittent problems to occur all the time. It is capable
-of detecting memory leaks, writes beyond and before memory blocks, and
-breaks software that relies on the state of uninitialized memory, and
-software that uses memory after it's been freed.
-
- It works by allocating extra space on each block. This includes a
-private header (which is used to keep track of Fortify's list of allocated
-memory), and two "fortification" zones or sentinals (which are used to
-detect writing outside the bound of the user's memory).
-
-
-
-
- Fortify VERSUS ZFortify
- -----------------------
- Fortify provides fortification for malloc/realloc/free, and ZFortify
-provides fortifications for new/delete. It is possible to use both
-versions in the one program, if you are mixing C and C++. Just make sure
-(as you already should be) that you never free() memory you new'd, and
-other such illegal mismatching.
- (Why _Z_ fortify? It's a long story...)
-
-
-
- Fortify INSTALLATION (C)
- ------------------------
- To use Fortify, each source file will need to #include "fortify.h". To
-enable Fortify, define the symbol FORTIFY. If FORTIFY is not defined, it
-will compile away to nothing. If you do not have stdout available, you may
-wish to set an alternate output function. See Fortify_SetOutputFunc(),
-below.
- You will also need to link in fortify.o
-
-
-
-
- ZFortify INSTALLATION (C++)
- ---------------------------
- The minimum you need to do for ZFortify is to define the symbol
-ZFORTIFY, and link zfortify.o. Each source file should also include
-"zfortify.hpp", but this isn't strictly necessary. If a file doesn't
-#include "Fortify.hpp", then it's allocations will still be fortified,
-however you will not have any source-code details in any of the output
-messages (this will be the case for all libraries, etc, unless you have the
-source for the library and can recompile it with Fortify).
- If you do not have stdout available, you may wish to set an alternate
-output function, or turn on the AUTOMATIC_LOGFILE. See
-ZFortify_SetOutputFunc() and AUTOMATIC_LOGFILE, below.
-
-
-
-
- COMPILE TIME CUSTOMIZATIONS
- ---------------------------
- The files "ufortify.h" and "ufortify.hpp" contain a number of #defines
-that you can use to customize Fortify's behavior.
-
-
- #define ZFORTIFY_PROVIDE_ARRAY_NEW
-
- Some C++ compilers have a separate operator for newing arrays. If your
-compiler does, you will need to define this symbol. If you are unsure,
-dont worry about it too much, your program won't compile or link without
-the correct setting. GCC 2.6.3 and Borland C++ 4.5 both need this symbol.
-Microsoft C++ 1.5 and SAS 6.5 C++ both dont.
-
-
- #define FORTIFY_STORAGE
-
- #define ZFORTIFY_STORAGE
-
-You can use this to apply a storage type to all of Fortify's exportable
-functions. If you are putting Fortify in an export library for example,
-you may need to put __export here, or some such rubbish.
-
-
- #define FORTIFY_BEFORE_SIZE 32
- #define FORTIFY_BEFORE_VALUE 0xA3
-
- #define FORTIFY_AFTER_SIZE 32
- #define FORTIFY_AFTER_VALUE 0xA5
-
- #define ZFORTIFY_BEFORE_SIZE 32
- #define ZFORTIFY_BEFORE_VALUE 0xA3
-
- #define ZFORTIFY_AFTER_SIZE 32
- #define ZFORTIFY_AFTER_VALUE 0xA5
-
- These values define how much "fortification" is placed around each
-memory block you allocate. Fortify will place _BEFORE_SIZE bytes worth of
-memory right before your allocation block, and _AFTER_SIZE bytes worth
-after your allocation block, and these will be initialized to _BEFORE_VALUE
-and _AFTER_VALUE respectively. If your program then accidentally writes
-too far beyond the end of the block, for example, Fortify will be able to
-detect this (so long as you didn't happen to write in _AFTER_VALUE!).
-
- If you don't want these fortifications to be allocated, specify a
-_SIZE of 0. Note that the _VALUE parameters are 8 bits.
-
-
-
- #define FILL_ON_MALLOC
- #define FILL_ON_MALLOC_VALUE 0xA7
-
- #define FILL_ON_NEW
- #define FILL_ON_NEW_VALUE 0xA7
-
- Programs often rely on uninitialized memory being certain values
-(usually 0). If you define FILL_ON_NEW, all memory that you new will be
-initialized to FILL_ON_NEW_VALUE, which you should define to be some horrid
-value (definately NOT 0). This will encourage all code that relies on
-uninitialized memory to behave rather differently when Fortify is running.
-
-
-
- #define FILL_ON_FREE
- #define FILL_ON_FREE_VALUE 0xA9
-
- #define FILL_ON_DELETE
- #define FILL_ON_DELETE_VALUE 0xA9
-
- Programmers often try to use memory after they've freed it, which can
-sometimes work (so long as noboby else has modified the memory before you
-look at it), but is incredibly dangerous and definately bad practice. If
-FILL_ON_DELETE is defined, all memory you free will be stomped out with
-FILL_ON_DELETE_VALUE, which ensures that any attempt to read freed memory
-will give incorrect results.
-
-
-
- #define CHECK_ALL_MEMORY_ON_MALLOC
- #define CHECK_ALL_MEMORY_ON_FREE
-
- #define CHECK_ALL_MEMORY_ON_NEW
- #define CHECK_ALL_MEMORY_ON_DELETE
-
- CHECK_ALL_MEMORY_ON... means that for every single memory allocation
-or deallocation, every single block of memory will be checked. This can
-considerably slow down programs if you have a large number of blocks
-allocated. You would normally only need to turn this on if you are trying
-to pinpoint where a corruption was occurring.
- A block of memory is always checked when it is freed, so if
-CHECK_ALL... isn't turned on, corruptions will still be detected
-eventually.
- You can also force Fortify to check all memory with a call to
-Fortify_CheckAllMemory(). If you have a memory corruption you can't find,
-sprinkling these through the suspect code will help narrow it down.
-
-
-
- #define PARANOID_FREE
-
- #define PARANOID_DELETE
-
- PARANOID_... - This means that zFortify traverses the memory list to
-ensure the memory you are about to free was really allocated by it. You
-probably only need this in extreme circumstances. Not having this defined
-will still trap attempts to free memory that wasn't allocated, unless
-someone is deliberately trying to fool zFortify.
- Paranoid mode adds considerable overhead to freeing memory, especially
-if you are freeing things in the same order you allocated them (Paranoid
-mode is most efficient if you are freeing things in the reverse order they
-were allocated).
-
-
-
- #define WARN_ON_MALLOC_FAIL
- #define WARN_ON_ZERO_MALLOC
- #define WARN_ON_FALSE_FAIL
- #define WARN_ON_UNSIGNED_LONG_OVERFLOW
-
- #define WARN_ON_NEW_FAIL
- #define WARN_ON_ZERO_NEW
-
- These defines enable the output of warning that aren't strictly errors,
-but can be useful to determine what lead to a program crashing.
- WARN_ON_NEW_FAIL causes a debug to be issued whenever new fails.
- WARN_ON_ZERO_NEW causes a debug to be issued whenever a new of a zero
-byte object is attempted. This is fairly unlikely in C++, and is much more
-likely when using malloc().
- WARN_ON_FALSE_FAIL causes a debug to be issued when a new is "false"
-failed. ZSee Fortify_SetNewFailRate() for more information.
- WARN_ON_UNSIGNED_LONG_OVERFLOW causes Fortify to check for breaking the
-32 bit limit. This was more of a problem in 16-bit applications where
-breaking the 16 bit limit was much more likely. The problem is that
-Fortify adds a small amount of overhead to a memory block; so in a 16-bit
-size_t environment, if you tried to allocate 64K, Fortify would make that
-block bigger than 64K and your allocation would fail due to the presence of
-Fortify. With size_t being 32 bits for all environments worth programming
-in, this problem is extremely unlikely (Unless you plan to allocate 4
-gigabytes).
-
-
- #define AUTOMATIC_LOG_FILE
- #define LOG_FILENAME "fortify.log"
- #define FIRST_ERROR_FUNCTION
-
- If AUTOMATIC_LOG_FILE is defined (C++ version /ZFortify only), then
-Fortify will be automatically started for you, Fortify messages sent to the
-named log file, and a list of unfreed memory dumped on termination (where
-the log file will be automatically closed for you. If no Fortify was
-output, the log file will not be altered. There are timestamps in the log
-file to ensure you're reading the correct messages.
- FIRST_ERROR_FUNCTION will be called upon generation of the first
-Fortify message, so that the user can tell a Fortify report has been
-generated. Otherwise, Fortify would quietly write all this useful stuff
-out to the log file, and no-one would know to look there!
-
-
-
- #define FORTIFY_LOCK()
- #define FORTIFY_UNLOCK()
-
- #define ZFORTIFY_LOCK()
- #define ZFORTIFY_UNLOCK()
-
- In a multi-threaded environment, we need to arbitrate access to the
-foritfy memory list. This is what ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK()
-are used for. The calls to ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK() must
-nest. If no two threads/tasks/processes will be using the same Fortify at
-the same time, then ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK() can safely be
-#defined away to nothing.
-
-
-
-
- RUN TIME CONTROL
- ----------------
- Fortify can also be controlled at run time with a few special
-functions, which compile away to nothing if FORTIFY isn't defined, and do
-nothing if Fortify has been disabled with Fortify_Disable(). These
-functions all apply to ZFortify as well (The ZFortify versions have a
-ZFortify_ prefix, of course).
-
- Fortify_Disable() - This function provides a mechanism to disable
-Fortify without recompiling all the sourcecode. It can only be called,
-though, when there is no memory on the Fortify list. (Ideally, at the
-start of the program before any memory has been allocated). If you call
-this function when there IS memory on the Fortify list, it will issue an
-error, and Fortify will not be disabled.
-
- Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output) - Sets the function
-used to output all error and diagnostic messages by Fortify. The output
-function takes a single (const char *) argument, and must be able to handle
-newlines. The function returns the old pointer.
- The default output func is a printf() to stdout. Unless you are using
-AUTOMATIC_LOG_FILE, where the default is to output to the log file.
-
- Fortify_SetNewFailRate(int Percent) - Fortify will make a new attempt
-"fail" this "Percent" of the time, even if the memory IS available. Useful
-to "stress-test" an application. Returns the old value. The fail rate
-defaults to 0.
-
-
-
- DIAGNOSTIC FUNCTIONS
- --------------------
- Fortify also provides some additional diagnostic functions which can be
-used to track down memory corruption and memory leaks. If Fortify is
-disabled, these functions do nothing. If calling these functions directly
-from a debugger, remember to add the "char *file" and "unsigned long line"
-paramters to each of the calls. (The ZFortify versions have a
-ZFortify_ prefix, of course).
-
- Fortify_CheckPointer(void *uptr) - Returns true if the uptr points to a
-valid piece of Fortify'd memory. The memory must be on Fortify's list, and
-it's sentinals must be in tact. If anything is wrong, an error message is
-issued (Note - if Fortify is disabled, this function always returns true).
-
- Fortify_CheckAllMemory() - Checks the sentinals of all malloc'd memory.
-Returns the number of blocks that failed. (If Fortify is disabled, this
-function always returns 0).
-
- Fortify_OutputAllMemory() - Outputs the entire list of currently
-allocated memory. For each block is output it's Address, Size, the
-SourceFile and Line that allocated it, and the fortify scope from within it
-was allocated. If there is no memory on the list, this function outputs
-nothing. It returns the number of blocks on the list, unless Fortify has
-been disabled, in which case it always returns 0.
-
- Fortify_DumpAllMemory(scope) - Just like Fortify_OutputAllMemory,
-except all memory inside the given scope is output, and a hex dump of each
-block is included in the output.
-
- Fortify_EnterScope() - enters a level of fortify scope. Returns the
-new scope level.
-
- Fortify_LeaveScope() - leaves a level of fortify scope, it also prints
-a dump of all memory allocated within the scope being left. This can be
-very useful in tracking down memory leaks in a part of a program. If you
-place a EnterScope/LeaveScope pair around a set of functions that should
-have no memory allocated when it's done, Fortify will let you know if this
-isn't the case.
-
-
- PROBLEMS WITH THE new AND delete MACROS
- ---------------------------------------
- Due to limitations of the preprocessor, getting caller sourcecode
-information isn't as easy as it is for malloc() and free(). The macro for
-"new" which adds this information onto the new call causes syntax errors if
-you try to declare a custom new operator. The actual Fortifying works
-fine, it's just the macro expansion which causes problems.
- If this happens, you will need to place #undef's and #define's around
-the offending code (sorry). Alternatively, you can not #include
-"zfortify.hpp" for the offending file. But remember that none of the
-allocation done in that file will have sourcecode information.
-
-eg.
-#undef new
-void *X::operator new(size_t) { return malloc(size_t); }
-#define new Fortify_New
-
-
- Due to a limitation with delete, Fortify has limited information about
-where delete is being called called from, and so the the line and source
-information will often say "delete.0". If a delete is occuring from within
-another delete, Fortify will always endeavour to report the highest level
-delete as the caller.
-
- It should be possible to replace the "new.0" and "delete.0" with the
-return address of the function, which would be useful when in a debugger,
-but this would be highly architecture dependant, so I leave that as an
-exercise for the students :-).
-
-
-
- WHEN TO USE FORTIFY
- -------------------
- The simple answer to this is "All The Time". You should never be
-without Fortify when you're actually developing software. It will detect
-your bugs _as_you_write_them_, which makes them a lot easier to find. One
-programmer who recently started using Fortify when he had a very strange
-memory problem, spent at least 3 or 4 days tracking down _other_ memory
-corruption bugs that he wasn't even aware of before the program would stay
-up long enough to get to his original problem. If he'd been using Fortify
-from the beginning, this wouldn't have been a problem.
- Leave fortify enabled until the final test and release of your
-software. You probably won't want some of the slower options, such as
-CHECK_ALL_MEMORY_ON_FREE, and PARANOID_FREE. With the exception of those
-options, Fortify doesn't have a great deal of overhead. If posing a great
-problem, this overhead can be greatly reduced by cutting down on the size
-of the fortifications, and turning off the pre/post fills, but each thing
-you turn off gives fortify less information to work with in tracking your
-bugs.
-
+
+ Fortify - fortified memory allocation shell for C and C++
+ ---------------------------------------------------------
+ written by Simon P. Bullen
+ Cybergraphic
+
+ Release 1.0, 2/2/1995
+
+
+
+
+ This software is not public domain. All material in
+ this archive is © Copyright 1995 Simon P. Bullen. The
+ software is freely distrubtable, with the condition that no
+ more than a nominal fee is charged for media. Everything in
+ this distrubution must be kept together, in original,
+ unmodified form.
+ The files may be modified for your own personal use, but
+ modified files may not be distributed.
+ The material is provided "as is" without warranty of any
+ kind. The author accepts no responsibilty for damage caused
+ by this software.
+
+
+
+ email: sbullen@ozemail.com.au
+ snail: Simon P. Bullen
+ PO BOX 12138
+ A'Beckett St
+ Melbourne 3000
+ Australia
+
+
+
+ CONTENTS
+ --------
+ Your archive should have the following files:
+
+ file_id.diz
+ fortify.c
+ fortify.doc
+ fortify.h
+ read.me
+ test.c
+ ufortify.h
+ ufortify.hpp
+ zfortify.cpp
+ zfortify.hpp
+ ztest.cpp
+
+
+
+
+ OVERVIEW
+ --------
+ Fortify is a descendant of a library I wrote way back in 1990 called
+SafeMem. It is a (fortified) shell for memory allocations. It works with
+the malloc/free family of functions, as well as new/delete in C++. It can
+be adapted to most memory management functions; the original version of
+SafeMem worked only with the Amiga's AllocMem/FreeMem. I haven't been
+writing much Amiga specific software lately, so the Amiga version of
+Fortify is way out of date, hence it's absense from this archive.
+
+ Fortify is designed to detect bad things happening, or at the very
+least encourage intermittent problems to occur all the time. It is capable
+of detecting memory leaks, writes beyond and before memory blocks, and
+breaks software that relies on the state of uninitialized memory, and
+software that uses memory after it's been freed.
+
+ It works by allocating extra space on each block. This includes a
+private header (which is used to keep track of Fortify's list of allocated
+memory), and two "fortification" zones or sentinals (which are used to
+detect writing outside the bound of the user's memory).
+
+
+
+
+ Fortify VERSUS ZFortify
+ -----------------------
+ Fortify provides fortification for malloc/realloc/free, and ZFortify
+provides fortifications for new/delete. It is possible to use both
+versions in the one program, if you are mixing C and C++. Just make sure
+(as you already should be) that you never free() memory you new'd, and
+other such illegal mismatching.
+ (Why _Z_ fortify? It's a long story...)
+
+
+
+ Fortify INSTALLATION (C)
+ ------------------------
+ To use Fortify, each source file will need to #include "fortify.h". To
+enable Fortify, define the symbol FORTIFY. If FORTIFY is not defined, it
+will compile away to nothing. If you do not have stdout available, you may
+wish to set an alternate output function. See Fortify_SetOutputFunc(),
+below.
+ You will also need to link in fortify.o
+
+
+
+
+ ZFortify INSTALLATION (C++)
+ ---------------------------
+ The minimum you need to do for ZFortify is to define the symbol
+ZFORTIFY, and link zfortify.o. Each source file should also include
+"zfortify.hpp", but this isn't strictly necessary. If a file doesn't
+#include "Fortify.hpp", then it's allocations will still be fortified,
+however you will not have any source-code details in any of the output
+messages (this will be the case for all libraries, etc, unless you have the
+source for the library and can recompile it with Fortify).
+ If you do not have stdout available, you may wish to set an alternate
+output function, or turn on the AUTOMATIC_LOGFILE. See
+ZFortify_SetOutputFunc() and AUTOMATIC_LOGFILE, below.
+
+
+
+
+ COMPILE TIME CUSTOMIZATIONS
+ ---------------------------
+ The files "ufortify.h" and "ufortify.hpp" contain a number of #defines
+that you can use to customize Fortify's behavior.
+
+
+ #define ZFORTIFY_PROVIDE_ARRAY_NEW
+
+ Some C++ compilers have a separate operator for newing arrays. If your
+compiler does, you will need to define this symbol. If you are unsure,
+dont worry about it too much, your program won't compile or link without
+the correct setting. GCC 2.6.3 and Borland C++ 4.5 both need this symbol.
+Microsoft C++ 1.5 and SAS 6.5 C++ both dont.
+
+
+ #define FORTIFY_STORAGE
+
+ #define ZFORTIFY_STORAGE
+
+You can use this to apply a storage type to all of Fortify's exportable
+functions. If you are putting Fortify in an export library for example,
+you may need to put __export here, or some such rubbish.
+
+
+ #define FORTIFY_BEFORE_SIZE 32
+ #define FORTIFY_BEFORE_VALUE 0xA3
+
+ #define FORTIFY_AFTER_SIZE 32
+ #define FORTIFY_AFTER_VALUE 0xA5
+
+ #define ZFORTIFY_BEFORE_SIZE 32
+ #define ZFORTIFY_BEFORE_VALUE 0xA3
+
+ #define ZFORTIFY_AFTER_SIZE 32
+ #define ZFORTIFY_AFTER_VALUE 0xA5
+
+ These values define how much "fortification" is placed around each
+memory block you allocate. Fortify will place _BEFORE_SIZE bytes worth of
+memory right before your allocation block, and _AFTER_SIZE bytes worth
+after your allocation block, and these will be initialized to _BEFORE_VALUE
+and _AFTER_VALUE respectively. If your program then accidentally writes
+too far beyond the end of the block, for example, Fortify will be able to
+detect this (so long as you didn't happen to write in _AFTER_VALUE!).
+
+ If you don't want these fortifications to be allocated, specify a
+_SIZE of 0. Note that the _VALUE parameters are 8 bits.
+
+
+
+ #define FILL_ON_MALLOC
+ #define FILL_ON_MALLOC_VALUE 0xA7
+
+ #define FILL_ON_NEW
+ #define FILL_ON_NEW_VALUE 0xA7
+
+ Programs often rely on uninitialized memory being certain values
+(usually 0). If you define FILL_ON_NEW, all memory that you new will be
+initialized to FILL_ON_NEW_VALUE, which you should define to be some horrid
+value (definately NOT 0). This will encourage all code that relies on
+uninitialized memory to behave rather differently when Fortify is running.
+
+
+
+ #define FILL_ON_FREE
+ #define FILL_ON_FREE_VALUE 0xA9
+
+ #define FILL_ON_DELETE
+ #define FILL_ON_DELETE_VALUE 0xA9
+
+ Programmers often try to use memory after they've freed it, which can
+sometimes work (so long as noboby else has modified the memory before you
+look at it), but is incredibly dangerous and definately bad practice. If
+FILL_ON_DELETE is defined, all memory you free will be stomped out with
+FILL_ON_DELETE_VALUE, which ensures that any attempt to read freed memory
+will give incorrect results.
+
+
+
+ #define CHECK_ALL_MEMORY_ON_MALLOC
+ #define CHECK_ALL_MEMORY_ON_FREE
+
+ #define CHECK_ALL_MEMORY_ON_NEW
+ #define CHECK_ALL_MEMORY_ON_DELETE
+
+ CHECK_ALL_MEMORY_ON... means that for every single memory allocation
+or deallocation, every single block of memory will be checked. This can
+considerably slow down programs if you have a large number of blocks
+allocated. You would normally only need to turn this on if you are trying
+to pinpoint where a corruption was occurring.
+ A block of memory is always checked when it is freed, so if
+CHECK_ALL... isn't turned on, corruptions will still be detected
+eventually.
+ You can also force Fortify to check all memory with a call to
+Fortify_CheckAllMemory(). If you have a memory corruption you can't find,
+sprinkling these through the suspect code will help narrow it down.
+
+
+
+ #define PARANOID_FREE
+
+ #define PARANOID_DELETE
+
+ PARANOID_... - This means that zFortify traverses the memory list to
+ensure the memory you are about to free was really allocated by it. You
+probably only need this in extreme circumstances. Not having this defined
+will still trap attempts to free memory that wasn't allocated, unless
+someone is deliberately trying to fool zFortify.
+ Paranoid mode adds considerable overhead to freeing memory, especially
+if you are freeing things in the same order you allocated them (Paranoid
+mode is most efficient if you are freeing things in the reverse order they
+were allocated).
+
+
+
+ #define WARN_ON_MALLOC_FAIL
+ #define WARN_ON_ZERO_MALLOC
+ #define WARN_ON_FALSE_FAIL
+ #define WARN_ON_UNSIGNED_LONG_OVERFLOW
+
+ #define WARN_ON_NEW_FAIL
+ #define WARN_ON_ZERO_NEW
+
+ These defines enable the output of warning that aren't strictly errors,
+but can be useful to determine what lead to a program crashing.
+ WARN_ON_NEW_FAIL causes a debug to be issued whenever new fails.
+ WARN_ON_ZERO_NEW causes a debug to be issued whenever a new of a zero
+byte object is attempted. This is fairly unlikely in C++, and is much more
+likely when using malloc().
+ WARN_ON_FALSE_FAIL causes a debug to be issued when a new is "false"
+failed. ZSee Fortify_SetNewFailRate() for more information.
+ WARN_ON_UNSIGNED_LONG_OVERFLOW causes Fortify to check for breaking the
+32 bit limit. This was more of a problem in 16-bit applications where
+breaking the 16 bit limit was much more likely. The problem is that
+Fortify adds a small amount of overhead to a memory block; so in a 16-bit
+size_t environment, if you tried to allocate 64K, Fortify would make that
+block bigger than 64K and your allocation would fail due to the presence of
+Fortify. With size_t being 32 bits for all environments worth programming
+in, this problem is extremely unlikely (Unless you plan to allocate 4
+gigabytes).
+
+
+ #define AUTOMATIC_LOG_FILE
+ #define LOG_FILENAME "fortify.log"
+ #define FIRST_ERROR_FUNCTION
+
+ If AUTOMATIC_LOG_FILE is defined (C++ version /ZFortify only), then
+Fortify will be automatically started for you, Fortify messages sent to the
+named log file, and a list of unfreed memory dumped on termination (where
+the log file will be automatically closed for you. If no Fortify was
+output, the log file will not be altered. There are timestamps in the log
+file to ensure you're reading the correct messages.
+ FIRST_ERROR_FUNCTION will be called upon generation of the first
+Fortify message, so that the user can tell a Fortify report has been
+generated. Otherwise, Fortify would quietly write all this useful stuff
+out to the log file, and no-one would know to look there!
+
+
+
+ #define FORTIFY_LOCK()
+ #define FORTIFY_UNLOCK()
+
+ #define ZFORTIFY_LOCK()
+ #define ZFORTIFY_UNLOCK()
+
+ In a multi-threaded environment, we need to arbitrate access to the
+foritfy memory list. This is what ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK()
+are used for. The calls to ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK() must
+nest. If no two threads/tasks/processes will be using the same Fortify at
+the same time, then ZFORTIFY_LOCK() and ZFORTIFY_UNLOCK() can safely be
+#defined away to nothing.
+
+
+
+
+ RUN TIME CONTROL
+ ----------------
+ Fortify can also be controlled at run time with a few special
+functions, which compile away to nothing if FORTIFY isn't defined, and do
+nothing if Fortify has been disabled with Fortify_Disable(). These
+functions all apply to ZFortify as well (The ZFortify versions have a
+ZFortify_ prefix, of course).
+
+ Fortify_Disable() - This function provides a mechanism to disable
+Fortify without recompiling all the sourcecode. It can only be called,
+though, when there is no memory on the Fortify list. (Ideally, at the
+start of the program before any memory has been allocated). If you call
+this function when there IS memory on the Fortify list, it will issue an
+error, and Fortify will not be disabled.
+
+ Fortify_SetOutputFunc(Fortify_OutputFuncPtr Output) - Sets the function
+used to output all error and diagnostic messages by Fortify. The output
+function takes a single (const char *) argument, and must be able to handle
+newlines. The function returns the old pointer.
+ The default output func is a printf() to stdout. Unless you are using
+AUTOMATIC_LOG_FILE, where the default is to output to the log file.
+
+ Fortify_SetNewFailRate(int Percent) - Fortify will make a new attempt
+"fail" this "Percent" of the time, even if the memory IS available. Useful
+to "stress-test" an application. Returns the old value. The fail rate
+defaults to 0.
+
+
+
+ DIAGNOSTIC FUNCTIONS
+ --------------------
+ Fortify also provides some additional diagnostic functions which can be
+used to track down memory corruption and memory leaks. If Fortify is
+disabled, these functions do nothing. If calling these functions directly
+from a debugger, remember to add the "char *file" and "unsigned long line"
+paramters to each of the calls. (The ZFortify versions have a
+ZFortify_ prefix, of course).
+
+ Fortify_CheckPointer(void *uptr) - Returns true if the uptr points to a
+valid piece of Fortify'd memory. The memory must be on Fortify's list, and
+it's sentinals must be in tact. If anything is wrong, an error message is
+issued (Note - if Fortify is disabled, this function always returns true).
+
+ Fortify_CheckAllMemory() - Checks the sentinals of all malloc'd memory.
+Returns the number of blocks that failed. (If Fortify is disabled, this
+function always returns 0).
+
+ Fortify_OutputAllMemory() - Outputs the entire list of currently
+allocated memory. For each block is output it's Address, Size, the
+SourceFile and Line that allocated it, and the fortify scope from within it
+was allocated. If there is no memory on the list, this function outputs
+nothing. It returns the number of blocks on the list, unless Fortify has
+been disabled, in which case it always returns 0.
+
+ Fortify_DumpAllMemory(scope) - Just like Fortify_OutputAllMemory,
+except all memory inside the given scope is output, and a hex dump of each
+block is included in the output.
+
+ Fortify_EnterScope() - enters a level of fortify scope. Returns the
+new scope level.
+
+ Fortify_LeaveScope() - leaves a level of fortify scope, it also prints
+a dump of all memory allocated within the scope being left. This can be
+very useful in tracking down memory leaks in a part of a program. If you
+place a EnterScope/LeaveScope pair around a set of functions that should
+have no memory allocated when it's done, Fortify will let you know if this
+isn't the case.
+
+
+ PROBLEMS WITH THE new AND delete MACROS
+ ---------------------------------------
+ Due to limitations of the preprocessor, getting caller sourcecode
+information isn't as easy as it is for malloc() and free(). The macro for
+"new" which adds this information onto the new call causes syntax errors if
+you try to declare a custom new operator. The actual Fortifying works
+fine, it's just the macro expansion which causes problems.
+ If this happens, you will need to place #undef's and #define's around
+the offending code (sorry). Alternatively, you can not #include
+"zfortify.hpp" for the offending file. But remember that none of the
+allocation done in that file will have sourcecode information.
+
+eg.
+#undef new
+void *X::operator new(size_t) { return malloc(size_t); }
+#define new Fortify_New
+
+
+ Due to a limitation with delete, Fortify has limited information about
+where delete is being called called from, and so the the line and source
+information will often say "delete.0". If a delete is occuring from within
+another delete, Fortify will always endeavour to report the highest level
+delete as the caller.
+
+ It should be possible to replace the "new.0" and "delete.0" with the
+return address of the function, which would be useful when in a debugger,
+but this would be highly architecture dependant, so I leave that as an
+exercise for the students :-).
+
+
+
+ WHEN TO USE FORTIFY
+ -------------------
+ The simple answer to this is "All The Time". You should never be
+without Fortify when you're actually developing software. It will detect
+your bugs _as_you_write_them_, which makes them a lot easier to find. One
+programmer who recently started using Fortify when he had a very strange
+memory problem, spent at least 3 or 4 days tracking down _other_ memory
+corruption bugs that he wasn't even aware of before the program would stay
+up long enough to get to his original problem. If he'd been using Fortify
+from the beginning, this wouldn't have been a problem.
+ Leave fortify enabled until the final test and release of your
+software. You probably won't want some of the slower options, such as
+CHECK_ALL_MEMORY_ON_FREE, and PARANOID_FREE. With the exception of those
+options, Fortify doesn't have a great deal of overhead. If posing a great
+problem, this overhead can be greatly reduced by cutting down on the size
+of the fortifications, and turning off the pre/post fills, but each thing
+you turn off gives fortify less information to work with in tracking your
+bugs.
+
diff --git a/fourmess.c b/fourmess.c
index 27393a2d..08516cf7 100644
--- a/fourmess.c
+++ b/fourmess.c
@@ -393,7 +393,7 @@ static int FourMessStoreIntern(pSICSOBJ self, SConnection * pCon,
double fHkl[3], double fPosition[4], char *extra)
{
pFourMess priv = self->pPrivate;
- float fSum, fSigma, fTemp, fStep = .0, fPreset =.0;
+ float fSum, fSigma, fTemp, fMF, fStep = .0, fPreset =.0;
int i, iLF, iRet, iNP, ii;
long *lCounts = NULL;
pEVControl pEva = NULL;
@@ -411,6 +411,7 @@ static int FourMessStoreIntern(pSICSOBJ self, SConnection * pCon,
SCWrite(pCon, "ERROR: store: no files open", eLogError);
return 0;
}
+ priv->count++;
/* get necessary data */
fSum = 0.;
@@ -485,6 +486,23 @@ static int FourMessStoreIntern(pSICSOBJ self, SConnection * pCon,
iRet = EVCGetPos(pEva, pCon, &fTemp);
}
+ /* get mf */
+ fMF = -777.77;
+ pEva = (pEVControl) FindCommandData(pServ->pSics, "mf",
+ "Environment Controller");
+ if (pEva == NULL) {
+ pPtr = (pDummy) FindCommandData(pServ->pSics, "mf",
+ "RemObject");
+ if (pPtr != NULL) {
+ pDriv = pPtr->pDescriptor->GetInterface(pPtr, DRIVEID);
+ if (pDriv != NULL) {
+ fMF = pDriv->GetValue(pPtr, pCon);
+ }
+ }
+ } else {
+ iRet = EVCGetPos(pEva, pCon, &fMF);
+ }
+
/* write profile */
if (priv->profFile) {
/* collect data */
@@ -492,12 +510,16 @@ static int FourMessStoreIntern(pSICSOBJ self, SConnection * pCon,
GetScanVarStep(priv->pScanner, 0, &fStep);
fPreset = GetScanPreset(priv->pScanner);
prot = getProtonAverage(priv);
+ /*
+ They rather wanted fMF in place of the proton average which fell victim to the
+ monitor assignement chaos anyway.
+ */
if(extra == NULL){
fprintf(priv->profFile, "%3d %7.4f %9.0f %7.3f %12f %s\n", iNP, fStep,
- fPreset, fTemp, prot, pBueffel);
+ fPreset, fTemp, fMF, pBueffel);
} else {
fprintf(priv->profFile, "%3d %7.4f %9.0f %7.3f %12f %s %s\n", iNP, fStep,
- fPreset, fTemp, prot, extra, pBueffel);
+ fPreset, fTemp, fMF, pBueffel,extra);
}
for (i = 0; i < iNP; i++) {
for (ii = 0; ii < 10 && i < iNP; ii++) {
diff --git a/hipadaba.c b/hipadaba.c
index 1514374c..5eb6666d 100644
--- a/hipadaba.c
+++ b/hipadaba.c
@@ -138,6 +138,9 @@ void DeleteNodeData(pHdb node)
if (node->name != NULL) {
free(node->name);
}
+ if (node->path != NULL) {
+ free(node->path);
+ }
ReleaseHdbValue(&node->value);
node->magic = 000000;
@@ -601,7 +604,7 @@ int compareHdbValue(hdbValue v1, hdbValue v2)
}
break;
case HIPFLOAT:
- if (ABS(v1.v.doubleValue - v2.v.doubleValue) < .01) {
+ if (ABS(v1.v.doubleValue - v2.v.doubleValue) < .0001) { /* DFC */
return 1;
} else {
return 0;
@@ -895,6 +898,7 @@ char *GetHipadabaPath(pHdb node)
strcat(pPtr, "/");
strcat(pPtr, nodeStack[i]->name);
}
+ node->path = pPtr;
return pPtr;
}
@@ -1167,6 +1171,16 @@ void SetHdbProperty(pHdb node, char *key, char *value)
}
}
+/*---------------------------------------------------------------------------*/
+int HasHdbProperty(pHdb node, char *key)
+{
+ if (node != NULL && node->properties != NULL) {
+ return StringDictExists(node->properties, key);
+ } else {
+ return 0;
+ }
+}
+
/*---------------------------------------------------------------------------*/
int GetHdbProperty(pHdb node, char *key, char *value, int len)
{
@@ -1180,7 +1194,7 @@ int GetHdbProperty(pHdb node, char *key, char *value, int len)
/*---------------------------------------------------------------------------*/
char *GetHdbProp(pHdb node, char *key)
{
- if (node != NULL && node->properties != NULL) {
+ if (node != NULL && isHdbNodeValid(node) && node->properties != NULL) {
return StringDictGetShort(node->properties, key);
} else {
return NULL;
diff --git a/hipadaba.h b/hipadaba.h
index f4f86328..d07488c8 100644
--- a/hipadaba.h
+++ b/hipadaba.h
@@ -72,6 +72,7 @@ typedef struct __hipadaba {
struct __hipadaba *next;
struct __hdbcallback *callBackChain;
char *name;
+ char *path;
hdbValue value;
int iprotected;
pStringDict properties;
@@ -446,6 +447,13 @@ void SetHdbProperty(pHdb node, char *key, char *value);
* @param len The length of value
* @return 0 on failure, 1 on success
*/
+/**
+ * Check if a property exists
+ * @param node The node to get the property from
+ * @param key The properties key
+ * @return 1 or 0 for true or false
+ */
+int HasHdbProperty(pHdb node, char *key);
int GetHdbProperty(pHdb node, char *key, char *value, int len);
/**
* get the value of a property
diff --git a/ifile.c b/ifile.c
index a35fc862..25767f57 100644
--- a/ifile.c
+++ b/ifile.c
@@ -47,6 +47,7 @@
#include "ifile.h"
/*====================== Locals ============================================*/
+IPair *pSICSOptions = NULL;
static IPair *CreateNewEntry(char *name, char *val, IPair * pN)
{
diff --git a/interface.c b/interface.c
index d661b2c3..6f84a604 100644
--- a/interface.c
+++ b/interface.c
@@ -242,6 +242,9 @@ static int DriveTaskFunc(void *data)
ExeInterest(pServ->pExecutor,taskData->name, "finished with problem");
}
traceSys("drive","DriveTask %s finished with state %d", taskData->name,status);
+ if(taskData->pCon->transID > 100000) {
+ SCPrintf(taskData->pCon,eLog,"TASKEND %d", taskData->pCon->transID);
+ }
return 0;
}
/*--------------------------------------------------------------------------*/
@@ -272,6 +275,9 @@ long StartDriveTask(void *obj, SConnection *pCon, char *name, float fTarget)
ExeInterest(pServ->pExecutor,name,"started");
DevexecLog("START",name);
InvokeNewTarget(pServ->pExecutor,name,fTarget);
+ if(pCon->transID > 100000) {
+ SCPrintf(pCon,eLog,"TASKSTART %d", pCon->transID);
+ }
taskData->id = DRIVEID;
taskData->obj = obj;
@@ -393,6 +399,9 @@ static int CountTaskFunc(void *data)
ExeInterest(pServ->pExecutor,taskData->name, "finished with problem");
}
traceSys("count","CountTask %s finished with state %d", taskData->name,status);
+ if(taskData->pCon->transID > 100000) {
+ SCPrintf(taskData->pCon,eLog,"TASKEND %d", taskData->pCon->transID);
+ }
return 0;
}
/*--------------------------------------------------------------------------*/
@@ -418,6 +427,9 @@ long StartCountTask(void *obj, SConnection *pCon, char *name)
}
ExeInterest(pServ->pExecutor,name,"started");
DevexecLog("START",name);
+ if(pCon->transID > 100000) {
+ SCPrintf(pCon,eLog,"TASKSTART %d", pCon->transID);
+ }
taskData->id = COUNTID;
taskData->obj = obj;
diff --git a/interface.h b/interface.h
index c5ac569e..6753084e 100644
--- a/interface.h
+++ b/interface.h
@@ -109,6 +109,8 @@
void *pUserData, KillFuncIT pKill);
int RemoveCallback(pICallBack pInterface, long iID);
int RemoveCallback2(pICallBack pInterface, void *pUserData);
+ int RemoveCallbackUsr(pICallBack self, SICSCallBack pFunc,
+ int (*userfunc)(const void* pContext, const void* pUserData), void *pCtx);
int RemoveCallbackCon(pICallBack pInterface, SConnection *pCon);
int CallbackScript(SConnection *pCon, SicsInterp *pSics, void *pData,
diff --git a/lld.c b/lld.c
index c5e7f3dd..53fc61d5 100644
--- a/lld.c
+++ b/lld.c
@@ -89,6 +89,7 @@ static int ListInit(int List, int ItemSize)
*/
Tmp = NODE_MALLOC(List);
if (NULL == Tmp) {
+ Tmp = ListControl[List].first;
NODE_FREE(Tmp); /* no need to cause memory leaks */
ListControl[List].first = NULL; /* or other errors */
return ERR_MEMORY; /* even if we're in trouble ... */
diff --git a/logger.c b/logger.c
index 37d0d631..bcf7c682 100644
--- a/logger.c
+++ b/logger.c
@@ -397,6 +397,9 @@ static int LoggerMakeDir(char *path)
return 0; /* mkdir failed */
snprintf(buffer, sizeof buffer, "%s", path);
lpath = strlen(buffer);
+ /* Discard any trailing slash characters */
+ while (lpath > 0 && buffer[lpath - 1] == '/')
+ buffer[--lpath] = '\0';
do {
slash = strrchr(buffer, '/');
if (!slash)
diff --git a/logreader.c b/logreader.c
index f5cd6f03..f3258fae 100644
--- a/logreader.c
+++ b/logreader.c
@@ -218,9 +218,7 @@ static int LogReader(SConnection * pCon, SicsInterp * pSics, void *pData,
int argc, char *argv[])
{
/* Usage:
- graph [ none ] np [ ...]
- graph text
- graph [ ...]
+ graph [ none | text | np ] [ ...]
and are seconds since epoch (unix time) or, if the value
is below one day, a time relative to the actual time
@@ -230,15 +228,13 @@ static int LogReader(SConnection * pCon, SicsInterp * pSics, void *pData,
is the maximal number of points to be returned. If more values
are present and the values are numeric, the data is reduced. If the data is not
numeric, the last values may be skipped in order to avoid overflow.
+ If np is not given, it is assumed to be very high.
is the name of a variable (several vaiables may be given).
Note that slashes are converted to dots, and that the first slash is ignored.
- The seconds variant is for text values, which can not be reduced. In any case, all values
- are returned.
-
- The third variant is old style and can be replaced by the first variant, where
- = ( - ) / + 2
+ "text" means that the values are not numeric, in this case np is not needed
+ and by default infinitely high
Output format:
@@ -308,15 +304,16 @@ Statistics *old;
to += now;
}
iarg = 3;
+ type0 = NUMERIC;
while (1) {
if (iarg >= argc)
goto illarg;
if (strcasecmp(argv[iarg], "text") == 0) { /* non-numeric values */
iarg++;
step = 1;
- type0 = TEXT;
np = to - from + 2;
- break;
+ type0 = TEXT;
+ /* break; */
} else if (strcasecmp(argv[iarg], "none") == 0) { /* none */
iarg++;
if (iarg >= argc)
@@ -327,7 +324,6 @@ Statistics *old;
iarg++;
if (iarg >= argc)
goto illarg;
- type0 = NUMERIC;
np = strtol(argv[iarg], &p, 0);
if (p == argv[iarg])
goto illarg;
@@ -341,14 +337,9 @@ Statistics *old;
}
break;
} else {
- step = strtol(argv[iarg], &p, 0);
- if (p == argv[iarg])
- goto illarg;
- iarg++;
- if (step <= 0)
- step = 1;
- type0 = NUMERIC;
- np = (from - to) / step + 2;
+ np = to - from + 2;
+ step = 1;
+ break;
}
}
if (step <= 0)
diff --git a/logsetup.c b/logsetup.c
index a8394b97..aceb023e 100644
--- a/logsetup.c
+++ b/logsetup.c
@@ -118,6 +118,11 @@ static int LogSetup(SConnection * pCon, SicsInterp * pSics, void *pData,
LoggerChange(logger, period, name);
} else {
logger = LoggerMake(name, period, !numeric);
+ /* If that failed, we cannot continue - it crashes in the callback */
+ if (logger == NULL) {
+ SCPrintf(pCon, eError, "ERROR: logger %s not created", argv[1]);
+ return 0;
+ }
LoggerSetNumeric(logger, numeric);
SetHdbProperty(node, "logger_name", name);
cb = MakeHipadabaCallback(LoggerUpdateCallback, logger,
diff --git a/macro.c b/macro.c
index a61e58c5..d7b39ad1 100644
--- a/macro.c
+++ b/macro.c
@@ -68,6 +68,7 @@
#include "ifile.h"
#include "Dbg.h"
#include "servlog.h"
+#include "sicsglobal.h"
#include "stringdict.h"
#include "exeman.h"
#include "nxcopy.h"
@@ -493,6 +494,7 @@ int MacroFileEval(SConnection * pCon, SicsInterp * pInter, void *pData,
iRun = 0;
}
if (iChar == (int) '\n') {
+ iLine++;
pBueffel[i] = (char) iChar;
pBueffel[i + 1] = '\0';
Tcl_DStringAppend(&command, pBueffel, -1);
@@ -500,13 +502,12 @@ int MacroFileEval(SConnection * pCon, SicsInterp * pInter, void *pData,
if (Tcl_CommandComplete(pCom)) {
FirstWord(pCom, pBueffel);
if (FindCommand(pInter, pBueffel) != NULL) {
- snprintf(pBueffel,sizeof(pBueffel)-1, "%s:%d>> %s", pFile, iLine, pCom);
+ snprintf(pBueffel,sizeof(pBueffel)-1, "%s:%d>> %s", argv[1], iLine, pCom);
SCWrite(pCon, pBueffel, eLog);
if (pWhere != NULL) {
free(pWhere);
}
pWhere = strdup(pBueffel);
- iLine++;
}
iRet = Tcl_Eval(pTcl, pCom);
if (iRet != TCL_OK) {
diff --git a/make_gen b/make_gen
index 1c875842..db644462 100644
--- a/make_gen
+++ b/make_gen
@@ -47,7 +47,7 @@ SOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \
histmemsec.o sansbc.o sicsutil.o strlutil.o genbinprot.o trace.o\
singlebinb.o taskobj.o sctcomtask.o tasmono.o multicountersec.o \
messagepipe.o sicsget.o remoteobject.o pmacprot.o charbychar.o binprot.o \
- cnvrt.o
+ cnvrt.o tclClock.o tclDate.o tclUnixTime.o
MOTOROBJ = motor.o simdriv.o
COUNTEROBJ = countdriv.o simcter.o counter.o
diff --git a/matrix/DEMO.DAT b/matrix/DEMO.DAT
index b566d75c..1aea9364 100644
--- a/matrix/DEMO.DAT
+++ b/matrix/DEMO.DAT
@@ -1,20 +1,20 @@
-1.1 2 3 4 5 6 7 8 9 9
-2 3.1 4 5 6 7 8 9 9 8
-3 4 5.1 6 7 8 9 9 8 7
-3 4 5 6.1 7 8 9 9 8 7
-1 2 3 4 5 7.2 7 8 9 9
-1 2 3 4 1 6 7 8.3 9 9
-6 2 3 4 5 6 7 8 9.8 9
-1 2 3 3 5 6 7 8 9 9.8
-1 2 3 4 6 6 7 8 9 9
-6 2 3 4 5 4 7 8 9 9
-3
-1
-4
-1
-2
-3
-1
-1
-2
-6
+1.1 2 3 4 5 6 7 8 9 9
+2 3.1 4 5 6 7 8 9 9 8
+3 4 5.1 6 7 8 9 9 8 7
+3 4 5 6.1 7 8 9 9 8 7
+1 2 3 4 5 7.2 7 8 9 9
+1 2 3 4 1 6 7 8.3 9 9
+6 2 3 4 5 6 7 8 9.8 9
+1 2 3 3 5 6 7 8 9 9.8
+1 2 3 4 6 6 7 8 9 9
+6 2 3 4 5 4 7 8 9 9
+3
+1
+4
+1
+2
+3
+1
+1
+2
+6
diff --git a/matrix/MAKEFILE.MSC b/matrix/MAKEFILE.MSC
index 107df78b..ed495420 100644
--- a/matrix/MAKEFILE.MSC
+++ b/matrix/MAKEFILE.MSC
@@ -1,62 +1,62 @@
-#------------------------------------------------------------------------------
-# file: makefile.msc
-# desc: makefile for matrix library package under Microsoft C/C++ v7.0
-# by: patrick ko
-# date: 16 Apr 94
-#
-# note: a slim matrix library matrix.lib will be generated
-#------------------------------------------------------------------------------
-
-OBJS = matcreat.obj matdump.obj materr.obj matadd.obj matsub.obj \
-matmul.obj matinv.obj matsolve.obj matdet.obj mattran.obj matdurbn.obj \
-mattoepz.obj matsubx.obj
-
-CC = cl -c
-C = cl
-
-demo.exe: demo.c matrix.lib
- $(C) demo.c matrix.lib
-
-matrix.lib: $(OBJS)
- lib matrix +matcreat +matdump +materr +matadd.obj ,,
- lib matrix +matsub +matmul +matinv +matsolve +matdet.obj ,,
- lib matrix +mattran +matdurbn +mattoepz +matsubx ,,
-
-matcreat.obj: matcreat.c matrix.h
- $(CC) matcreat.c
-
-matdump.obj: matdump.c matrix.h
- $(CC) matdump.c
-
-materr.obj: materr.c matrix.h
- $(CC) materr.c
-
-matadd.obj: matadd.c matrix.h
- $(CC) matadd.c
-
-matsub.obj: matsub.c matrix.h
- $(CC) matsub.c
-
-matmul.obj: matmul.c matrix.h
- $(CC) matmul.c
-
-matinv.obj: matinv.c matrix.h
- $(CC) matinv.c
-
-matsolve.obj: matsolve.c matrix.h
- $(CC) matsolve.c
-
-mattran.obj: mattran.c matrix.h
- $(CC) mattran.c
-
-matdet.obj: matdet.c matrix.h
- $(CC) matdet.c
-
-matdurbn.obj: matdurbn.c matrix.h
- $(CC) matdurbn.c
-
-mattoepz.obj: mattoepz.c matrix.h
- $(CC) mattoepz.c
-
-matsubx.obj: matsubx.c matrix.h
- $(CC) matsubx.c
+#------------------------------------------------------------------------------
+# file: makefile.msc
+# desc: makefile for matrix library package under Microsoft C/C++ v7.0
+# by: patrick ko
+# date: 16 Apr 94
+#
+# note: a slim matrix library matrix.lib will be generated
+#------------------------------------------------------------------------------
+
+OBJS = matcreat.obj matdump.obj materr.obj matadd.obj matsub.obj \
+matmul.obj matinv.obj matsolve.obj matdet.obj mattran.obj matdurbn.obj \
+mattoepz.obj matsubx.obj
+
+CC = cl -c
+C = cl
+
+demo.exe: demo.c matrix.lib
+ $(C) demo.c matrix.lib
+
+matrix.lib: $(OBJS)
+ lib matrix +matcreat +matdump +materr +matadd.obj ,,
+ lib matrix +matsub +matmul +matinv +matsolve +matdet.obj ,,
+ lib matrix +mattran +matdurbn +mattoepz +matsubx ,,
+
+matcreat.obj: matcreat.c matrix.h
+ $(CC) matcreat.c
+
+matdump.obj: matdump.c matrix.h
+ $(CC) matdump.c
+
+materr.obj: materr.c matrix.h
+ $(CC) materr.c
+
+matadd.obj: matadd.c matrix.h
+ $(CC) matadd.c
+
+matsub.obj: matsub.c matrix.h
+ $(CC) matsub.c
+
+matmul.obj: matmul.c matrix.h
+ $(CC) matmul.c
+
+matinv.obj: matinv.c matrix.h
+ $(CC) matinv.c
+
+matsolve.obj: matsolve.c matrix.h
+ $(CC) matsolve.c
+
+mattran.obj: mattran.c matrix.h
+ $(CC) mattran.c
+
+matdet.obj: matdet.c matrix.h
+ $(CC) matdet.c
+
+matdurbn.obj: matdurbn.c matrix.h
+ $(CC) matdurbn.c
+
+mattoepz.obj: mattoepz.c matrix.h
+ $(CC) mattoepz.c
+
+matsubx.obj: matsubx.c matrix.h
+ $(CC) matsubx.c
diff --git a/matrix/MAKEFILE.TC b/matrix/MAKEFILE.TC
index 0823ac6b..ddf87c3c 100644
--- a/matrix/MAKEFILE.TC
+++ b/matrix/MAKEFILE.TC
@@ -1,74 +1,74 @@
-#------------------------------------------------------------------------------
-# file: makefile.tc
-# desc: makefile for matrix library package under Turbo C v2.0
-# by: patrick ko
-# date: 16 Apr 94
-#
-# note: a slim matrix library matrix.lib will be generated
-#------------------------------------------------------------------------------
-DRIVE = C:
-
-OBJS = matcreat.obj matdump.obj materr.obj matadd.obj matsub.obj \
-matmul.obj matinv.obj matsolve.obj matdet.obj mattran.obj matdurbn.obj \
-mattoepz.obj matsubx.obj
-
-LIB = -L$(DRIVE)\TC\LIB
-INC = -I$(DRIVE)\TC\INCLUDE
-CC = tcc -c -mh $(INC)
-C = tcc -mh $(LIB) $(INC)
-LL = tlib matrix.lib
-
-demo.exe: demo.c $(OBJS)
- $(C) demo.c matrix.lib
-
-matcreat.obj: matcreat.c matrix.h
- $(CC) matcreat.c
- $(LL) -+matcreat.obj
-
-matdump.obj: matdump.c matrix.h
- $(CC) matdump.c
- $(LL) -+matdump.obj
-
-materr.obj: materr.c matrix.h
- $(CC) materr.c
- $(LL) -+materr.obj
-
-matadd.obj: matadd.c matrix.h
- $(CC) matadd.c
- $(LL) -+matadd.obj
-
-matsub.obj: matsub.c matrix.h
- $(CC) matsub.c
- $(LL) -+matsub.obj
-
-matmul.obj: matmul.c matrix.h
- $(CC) matmul.c
- $(LL) -+matmul.obj
-
-matinv.obj: matinv.c matrix.h
- $(CC) matinv.c
- $(LL) -+matinv.obj
-
-matsolve.obj: matsolve.c matrix.h
- $(CC) matsolve.c
- $(LL) -+matsolve.obj
-
-mattran.obj: mattran.c matrix.h
- $(CC) mattran.c
- $(LL) -+mattran.obj
-
-matdet.obj: matdet.c matrix.h
- $(CC) matdet.c
- $(LL) -+matdet.obj
-
-matdurbn.obj: matdurbn.c matrix.h
- $(CC) matdurbn.c
- $(LL) -+matdurbn.obj
-
-mattoepz.obj: mattoepz.c matrix.h
- $(CC) mattoepz.c
- $(LL) -+mattoepz.obj
-
-matsubx.obj: matsubx.c matrix.h
- $(CC) matsubx.c
- $(LL) -+matsubx.obj
+#------------------------------------------------------------------------------
+# file: makefile.tc
+# desc: makefile for matrix library package under Turbo C v2.0
+# by: patrick ko
+# date: 16 Apr 94
+#
+# note: a slim matrix library matrix.lib will be generated
+#------------------------------------------------------------------------------
+DRIVE = C:
+
+OBJS = matcreat.obj matdump.obj materr.obj matadd.obj matsub.obj \
+matmul.obj matinv.obj matsolve.obj matdet.obj mattran.obj matdurbn.obj \
+mattoepz.obj matsubx.obj
+
+LIB = -L$(DRIVE)\TC\LIB
+INC = -I$(DRIVE)\TC\INCLUDE
+CC = tcc -c -mh $(INC)
+C = tcc -mh $(LIB) $(INC)
+LL = tlib matrix.lib
+
+demo.exe: demo.c $(OBJS)
+ $(C) demo.c matrix.lib
+
+matcreat.obj: matcreat.c matrix.h
+ $(CC) matcreat.c
+ $(LL) -+matcreat.obj
+
+matdump.obj: matdump.c matrix.h
+ $(CC) matdump.c
+ $(LL) -+matdump.obj
+
+materr.obj: materr.c matrix.h
+ $(CC) materr.c
+ $(LL) -+materr.obj
+
+matadd.obj: matadd.c matrix.h
+ $(CC) matadd.c
+ $(LL) -+matadd.obj
+
+matsub.obj: matsub.c matrix.h
+ $(CC) matsub.c
+ $(LL) -+matsub.obj
+
+matmul.obj: matmul.c matrix.h
+ $(CC) matmul.c
+ $(LL) -+matmul.obj
+
+matinv.obj: matinv.c matrix.h
+ $(CC) matinv.c
+ $(LL) -+matinv.obj
+
+matsolve.obj: matsolve.c matrix.h
+ $(CC) matsolve.c
+ $(LL) -+matsolve.obj
+
+mattran.obj: mattran.c matrix.h
+ $(CC) mattran.c
+ $(LL) -+mattran.obj
+
+matdet.obj: matdet.c matrix.h
+ $(CC) matdet.c
+ $(LL) -+matdet.obj
+
+matdurbn.obj: matdurbn.c matrix.h
+ $(CC) matdurbn.c
+ $(LL) -+matdurbn.obj
+
+mattoepz.obj: mattoepz.c matrix.h
+ $(CC) mattoepz.c
+ $(LL) -+mattoepz.obj
+
+matsubx.obj: matsubx.c matrix.h
+ $(CC) matsubx.c
+ $(LL) -+matsubx.obj
diff --git a/matrix/MAKEFILE.UX b/matrix/MAKEFILE.UX
index a5f58ce5..de95f4d4 100644
--- a/matrix/MAKEFILE.UX
+++ b/matrix/MAKEFILE.UX
@@ -1,54 +1,54 @@
-#------------------------------------------------------------------------------
-# file: makefile.ux
-# desc: makefile for matrix library package under Unix
-# by: patrick ko
-# date: 16 Apr 94
-#------------------------------------------------------------------------------
-OBJS = matcreat.o matdump.o materr.o matadd.o matsub.o \
-matmul.o matinv.o matsolve.o mattran.o matdet.o mattoepz.o matdurbn.o
-
-CC = cc -c
-C = cc
-CO = -lm -g
-
-demo: demo.c $(OBJS)
- $(C) demo.c -o demo $(OBJS) $(CO)
-
-matcreat.o: matcreat.c matrix.h
- $(CC) matcreat.c
-
-matdump.o: matdump.c matrix.h
- $(CC) matdump.c
-
-materr.o: materr.c matrix.h
- $(CC) materr.c
-
-matadd.o: matadd.c matrix.h
- $(CC) matadd.c
-
-matsub.o: matsub.c matrix.h
- $(CC) matsub.c
-
-matmul.o: matmul.c matrix.h
- $(CC) matmul.c
-
-matinv.o: matinv.c matrix.h
- $(CC) matinv.c
-
-matsolve.o: matsolve.c matrix.h
- $(CC) matsolve.c
-
-mattran.o: mattran.c matrix.h
- $(CC) mattran.c
-
-matdet.o: matdet.c matrix.h
- $(CC) matdet.c
-
-mattoepz.o: mattoepz.c matrix.h
- $(CC) mattoepz.c
-
-matdurbn.o: matdurbn.c matrix.h
- $(CC) matdurbn.c
-
-matsubx.o: matsubx.c matrix.h
- $(CC) matsubx.c
+#------------------------------------------------------------------------------
+# file: makefile.ux
+# desc: makefile for matrix library package under Unix
+# by: patrick ko
+# date: 16 Apr 94
+#------------------------------------------------------------------------------
+OBJS = matcreat.o matdump.o materr.o matadd.o matsub.o \
+matmul.o matinv.o matsolve.o mattran.o matdet.o mattoepz.o matdurbn.o
+
+CC = cc -c
+C = cc
+CO = -lm -g
+
+demo: demo.c $(OBJS)
+ $(C) demo.c -o demo $(OBJS) $(CO)
+
+matcreat.o: matcreat.c matrix.h
+ $(CC) matcreat.c
+
+matdump.o: matdump.c matrix.h
+ $(CC) matdump.c
+
+materr.o: materr.c matrix.h
+ $(CC) materr.c
+
+matadd.o: matadd.c matrix.h
+ $(CC) matadd.c
+
+matsub.o: matsub.c matrix.h
+ $(CC) matsub.c
+
+matmul.o: matmul.c matrix.h
+ $(CC) matmul.c
+
+matinv.o: matinv.c matrix.h
+ $(CC) matinv.c
+
+matsolve.o: matsolve.c matrix.h
+ $(CC) matsolve.c
+
+mattran.o: mattran.c matrix.h
+ $(CC) mattran.c
+
+matdet.o: matdet.c matrix.h
+ $(CC) matdet.c
+
+mattoepz.o: mattoepz.c matrix.h
+ $(CC) mattoepz.c
+
+matdurbn.o: matdurbn.c matrix.h
+ $(CC) matdurbn.c
+
+matsubx.o: matsubx.c matrix.h
+ $(CC) matsubx.c
diff --git a/matrix/READ.ME b/matrix/READ.ME
index 89521113..933b7480 100644
--- a/matrix/READ.ME
+++ b/matrix/READ.ME
@@ -1,112 +1,112 @@
--------------------------------------------------------------------------------
- Small Matrix Toolbox for C programmers
- version 0.42
- (Support Unix and DOS)
-
- by Patrick KO Shu-pui
-
- Copyright (c) 1992, 1993, 1994 All Rights Reserved.
--------------------------------------------------------------------------------
-ADDRESS TO CONTACT:
-
- fidonet: 6:700/132 BiG Programming Club
- [852] 663-0223 19.2 kbps
- [852] 663-0236 16.8 kbps
-
- internet: pko@hk.super.net
-
- mailing: Patrick Ko
- G.P.O. Box 7468
- Hong Kong
--------------------------------------------------------------------------------
-MATRX042.ZIP contains
-
-READ .ME - this file
-DEMO .C - demo how to use this package
-DEMO .DAT - demo data
-DEMO .EXE - demo executable for DOS
-MAKEFILE.MSC - makefile for Microsoft C/C++ 7.0 on DOS
-MAKEFILE.TC - makefile for Turbo C 2.0 on DOS
-MAKEFILE.UX - makefile for Unix
-MATRIX .DOC - matrix toolbox interface document
-MATRIX .H - matrix header file (must include it)
-MATADD .C - matrix addition
-MATCREAT.C - matrix creation
-MATDET .C - find minor, cofactor, determinant
-MATDUMP .C - matrix dump
-MATERR .C - matrix error handling routine
-MATINV .C - matrix inversion
-MATMUL .C - matrix multiplication
-MATSOLVE.C - linear equations solver
-MATSUB .C - matrix substraction
-MATSUBX .C - submatrix operation
-MATTOEPZ.C - create symmetric Toeplitz matrix
-MATDURBN.C - Symmetrix Toeplitz matrix fast solving algorithm
-MATTRAN .C - matrix transpose
-
--------------------------------------------------------------------------------
-WHATS NEW in v0.1:
- - +, -, *, inverse matrix operations
-
-WHATS NEW in v0.2:
- - Linear equation solver
- - C-programmer-friendly C sources
-
-WHATS NEW in v0.3:
- - better data structure (more Object-Oriented)
- - finding minors, cofactors, determinants
- - Levinson-Durbin algorithm for symmetric Toeplitz matrix
-
-WHATS NEW in v0.4:
- - Revised method for minors, cofactors and determinants
- whose time complexity is T(n^3) instead of nearly T(n!).
- This is important when you want to find the determinant
- of a matrix whose size is over 10 x 10.
- - submatrix operator
- - matrix formmated dump function
- - brief matrix toolbox interface document included
-
-WHATS NEW in v0.41:
- - bug fix for unit matrix creation
-
-WHATS NEW in v0.42:
- - support Microsoft C/C++ 7.0
-
-HOW TO COMPILE:
-
- 1. All Unix environment - make -f makefile.ux
- 2. DOS (Turbo C v2.0) - make -fmakefile.tc
- 3. DOS (Microsoft C/C++ v7.0) - nmake -fmakefile.msc
-
-REFERENCES
-
- [1] Mary L.Boas, "Mathematical Methods in the Physical Sciene,"
- John Wiley & Sons, 2nd Ed., 1983. Chap 3.
-
- [2] Kendall E.Atkinson, "An Introduction to Numberical Analysis,"
- John Wiley & Sons, 1978.
-
- [3] Shuzo Saito, Kazuo Nakata, "Fundamentals of Speech Signal
- Processing," Academic Press, 1985.
-
- [4] Alfred V.Aho, John E.Hopcroft, Jeffrey D.Ullman, "The Design
- and Analysis of Computer Algorithms," 1974.
-
-AUTHOR
-All the sources are written by Patrick KO Shu Pui
-BiG Programming Club (6:700/132, fidonet)
-
-===============================================================================
-AUTHORIZATION NOTICE
-
-This C source package MATRX042.ZIP is FREE for ACADEMIC purpose only.
-
-For COMMERCIAL use, authorization is required from the author.
-Please send US$25 for registration.
-===============================================================================
-DISCLAIMER (I hate this but I have to do that)
-
-You are on your own risk - the author is not responsible for any lost due
-to the use of this toolbox.
-===============================================================================
-
+-------------------------------------------------------------------------------
+ Small Matrix Toolbox for C programmers
+ version 0.42
+ (Support Unix and DOS)
+
+ by Patrick KO Shu-pui
+
+ Copyright (c) 1992, 1993, 1994 All Rights Reserved.
+-------------------------------------------------------------------------------
+ADDRESS TO CONTACT:
+
+ fidonet: 6:700/132 BiG Programming Club
+ [852] 663-0223 19.2 kbps
+ [852] 663-0236 16.8 kbps
+
+ internet: pko@hk.super.net
+
+ mailing: Patrick Ko
+ G.P.O. Box 7468
+ Hong Kong
+-------------------------------------------------------------------------------
+MATRX042.ZIP contains
+
+READ .ME - this file
+DEMO .C - demo how to use this package
+DEMO .DAT - demo data
+DEMO .EXE - demo executable for DOS
+MAKEFILE.MSC - makefile for Microsoft C/C++ 7.0 on DOS
+MAKEFILE.TC - makefile for Turbo C 2.0 on DOS
+MAKEFILE.UX - makefile for Unix
+MATRIX .DOC - matrix toolbox interface document
+MATRIX .H - matrix header file (must include it)
+MATADD .C - matrix addition
+MATCREAT.C - matrix creation
+MATDET .C - find minor, cofactor, determinant
+MATDUMP .C - matrix dump
+MATERR .C - matrix error handling routine
+MATINV .C - matrix inversion
+MATMUL .C - matrix multiplication
+MATSOLVE.C - linear equations solver
+MATSUB .C - matrix substraction
+MATSUBX .C - submatrix operation
+MATTOEPZ.C - create symmetric Toeplitz matrix
+MATDURBN.C - Symmetrix Toeplitz matrix fast solving algorithm
+MATTRAN .C - matrix transpose
+
+-------------------------------------------------------------------------------
+WHATS NEW in v0.1:
+ - +, -, *, inverse matrix operations
+
+WHATS NEW in v0.2:
+ - Linear equation solver
+ - C-programmer-friendly C sources
+
+WHATS NEW in v0.3:
+ - better data structure (more Object-Oriented)
+ - finding minors, cofactors, determinants
+ - Levinson-Durbin algorithm for symmetric Toeplitz matrix
+
+WHATS NEW in v0.4:
+ - Revised method for minors, cofactors and determinants
+ whose time complexity is T(n^3) instead of nearly T(n!).
+ This is important when you want to find the determinant
+ of a matrix whose size is over 10 x 10.
+ - submatrix operator
+ - matrix formmated dump function
+ - brief matrix toolbox interface document included
+
+WHATS NEW in v0.41:
+ - bug fix for unit matrix creation
+
+WHATS NEW in v0.42:
+ - support Microsoft C/C++ 7.0
+
+HOW TO COMPILE:
+
+ 1. All Unix environment - make -f makefile.ux
+ 2. DOS (Turbo C v2.0) - make -fmakefile.tc
+ 3. DOS (Microsoft C/C++ v7.0) - nmake -fmakefile.msc
+
+REFERENCES
+
+ [1] Mary L.Boas, "Mathematical Methods in the Physical Sciene,"
+ John Wiley & Sons, 2nd Ed., 1983. Chap 3.
+
+ [2] Kendall E.Atkinson, "An Introduction to Numberical Analysis,"
+ John Wiley & Sons, 1978.
+
+ [3] Shuzo Saito, Kazuo Nakata, "Fundamentals of Speech Signal
+ Processing," Academic Press, 1985.
+
+ [4] Alfred V.Aho, John E.Hopcroft, Jeffrey D.Ullman, "The Design
+ and Analysis of Computer Algorithms," 1974.
+
+AUTHOR
+All the sources are written by Patrick KO Shu Pui
+BiG Programming Club (6:700/132, fidonet)
+
+===============================================================================
+AUTHORIZATION NOTICE
+
+This C source package MATRX042.ZIP is FREE for ACADEMIC purpose only.
+
+For COMMERCIAL use, authorization is required from the author.
+Please send US$25 for registration.
+===============================================================================
+DISCLAIMER (I hate this but I have to do that)
+
+You are on your own risk - the author is not responsible for any lost due
+to the use of this toolbox.
+===============================================================================
+
diff --git a/matrix/demo.c b/matrix/demo.c
index 8658ce01..171b9ec8 100644
--- a/matrix/demo.c
+++ b/matrix/demo.c
@@ -1,92 +1,92 @@
-/*
-*-----------------------------------------------------------------------------
-* file: demo.c
-* desc: demostrate how to use Patrick's matrix toolbox
-* by: ko shu pui, patrick
-* date: 24 nov 91 v0.1
-* revi: 14 may 92 v0.2
-* 24 may 92 v0.4
-* ref:
-* [1] Mary L.Boas, "Mathematical Methods in the Physical Sciene,"
-* John Wiley & Sons, 2nd Ed., 1983. Chap 3.
-*
-* [2] Kendall E.Atkinson, "An Introduction to Numberical Analysis,"
-* John Wiley & Sons, 1978.
-*
-* [3] Alfred V.Aho, John E.Hopcroft, Jeffrey D.Ullman, "The Design
-* and Analysis of Computer Algorithms," 1974.
-*
-*-----------------------------------------------------------------------------
-*/
-#include
-#include
-#include "matrix.h"
-
-int main()
-{
- MATRIX A, B, X, M;
- FILE *fp;
- double result;
- time_t t1, t2;
- int tinv, tdet, tmul;
-
- A = mat_creat( 10, 10, UNDEFINED );
- B = mat_creat( 10, 1, UNDEFINED );
-
- if ((fp = fopen( "demo.dat", "r" )) == NULL)
- {
- fprintf( stderr, "file cannot be opened\n" );
- exit (0);
- }
-
- fgetmat( A, fp );
- printf( "|- Matrix A -|\n");
- mat_dumpf( A, "%+06.1f " );
-
- t1 = time(&t1);
- result = mat_det(A);
- t2 = time(&t2);
- tdet = t2 - t1;
- printf( "\n\nDet(A) = %f\n", result );
-
- printf( "|- Inv A -|\n");
- t1 = time(&t1);
- X = mat_inv( A );
- t2 = time(&t2);
- tinv = t2 - t1;
-
- if (X == NULL)
- printf( "A is a singular matrix\n" );
- else
- {
- mat_dumpf(X, "%+06.1f ");
-
- printf( "|- A x Inv A -|\n");
- t1 = time(&t1);
- M = mat_mul( X, A );
- t2 = time(&t2);
- tmul = t2 - t1;
- mat_dumpf( M, "%+06.1f " );
-
- mat_free(M);
- mat_free(X);
- }
-
- fgetmat( B, fp );
- printf( "|- Matrix B -|\n");
- mat_dumpf( B, "%+06.1f " );
-
- printf( "|- A x B -|\n");
- mat_free(mat_dumpf(mat_mul(A, B), "%+06.1f "));
-
- printf( "time for finding 10 x 10 matrix inversion is less than %d secs\n", tinv );
- printf( "time for finding 10 x 10 matrix determinant is less than %d secs\n", tdet );
- printf( "time for finding 10 x 10 matrix multiplication is less than %d secs\n", tmul );
-
- mat_free( A );
- mat_free( B );
-
- fclose(fp);
-
-}
+/*
+*-----------------------------------------------------------------------------
+* file: demo.c
+* desc: demostrate how to use Patrick's matrix toolbox
+* by: ko shu pui, patrick
+* date: 24 nov 91 v0.1
+* revi: 14 may 92 v0.2
+* 24 may 92 v0.4
+* ref:
+* [1] Mary L.Boas, "Mathematical Methods in the Physical Sciene,"
+* John Wiley & Sons, 2nd Ed., 1983. Chap 3.
+*
+* [2] Kendall E.Atkinson, "An Introduction to Numberical Analysis,"
+* John Wiley & Sons, 1978.
+*
+* [3] Alfred V.Aho, John E.Hopcroft, Jeffrey D.Ullman, "The Design
+* and Analysis of Computer Algorithms," 1974.
+*
+*-----------------------------------------------------------------------------
+*/
+#include
+#include
+#include "matrix.h"
+
+int main()
+{
+ MATRIX A, B, X, M;
+ FILE *fp;
+ double result;
+ time_t t1, t2;
+ int tinv, tdet, tmul;
+
+ A = mat_creat( 10, 10, UNDEFINED );
+ B = mat_creat( 10, 1, UNDEFINED );
+
+ if ((fp = fopen( "demo.dat", "r" )) == NULL)
+ {
+ fprintf( stderr, "file cannot be opened\n" );
+ exit (0);
+ }
+
+ fgetmat( A, fp );
+ printf( "|- Matrix A -|\n");
+ mat_dumpf( A, "%+06.1f " );
+
+ t1 = time(&t1);
+ result = mat_det(A);
+ t2 = time(&t2);
+ tdet = t2 - t1;
+ printf( "\n\nDet(A) = %f\n", result );
+
+ printf( "|- Inv A -|\n");
+ t1 = time(&t1);
+ X = mat_inv( A );
+ t2 = time(&t2);
+ tinv = t2 - t1;
+
+ if (X == NULL)
+ printf( "A is a singular matrix\n" );
+ else
+ {
+ mat_dumpf(X, "%+06.1f ");
+
+ printf( "|- A x Inv A -|\n");
+ t1 = time(&t1);
+ M = mat_mul( X, A );
+ t2 = time(&t2);
+ tmul = t2 - t1;
+ mat_dumpf( M, "%+06.1f " );
+
+ mat_free(M);
+ mat_free(X);
+ }
+
+ fgetmat( B, fp );
+ printf( "|- Matrix B -|\n");
+ mat_dumpf( B, "%+06.1f " );
+
+ printf( "|- A x B -|\n");
+ mat_free(mat_dumpf(mat_mul(A, B), "%+06.1f "));
+
+ printf( "time for finding 10 x 10 matrix inversion is less than %d secs\n", tinv );
+ printf( "time for finding 10 x 10 matrix determinant is less than %d secs\n", tdet );
+ printf( "time for finding 10 x 10 matrix multiplication is less than %d secs\n", tmul );
+
+ mat_free( A );
+ mat_free( B );
+
+ fclose(fp);
+
+}
\ No newline at end of file
diff --git a/mcstas/dmc/MKMonitor.comp b/mcstas/dmc/MKMonitor.comp
index 79eb2803..8927f61c 100644
--- a/mcstas/dmc/MKMonitor.comp
+++ b/mcstas/dmc/MKMonitor.comp
@@ -10,7 +10,7 @@
* %I
* Written by: Kim Lefmann
* Date: October 4, 1997
-* Version: $Revision: 1.19 $
+* Version: $Revision$
* Origin: Risoe
* Release: McStas 1.6
* Modified to write monitor file for SICS: Mark Koennecke, June 2005
diff --git a/mcstas/dmc/PSD_monitor.comp b/mcstas/dmc/PSD_monitor.comp
index 8ae68401..0103aaa8 100644
--- a/mcstas/dmc/PSD_monitor.comp
+++ b/mcstas/dmc/PSD_monitor.comp
@@ -10,7 +10,7 @@
* %I
* Written by: Kim Lefmann
* Date: Feb 3, 1998
-* Version: $Revision: 1.2 $
+* Version: $Revision$
* Origin: Risoe
* Release: McStas 1.6
*
diff --git a/mcstas/dmc/dmcafter.c b/mcstas/dmc/dmcafter.c
index 3c5183d3..9579fd0b 100644
--- a/mcstas/dmc/dmcafter.c
+++ b/mcstas/dmc/dmcafter.c
@@ -30,9 +30,9 @@
*
* Usage: Automatically embbeded in the c code.
*
-* $Id: dmcafter.c,v 1.1 2007/01/30 03:19:43 koennecke Exp $
+* $Id$
*
-* $Log: dmcafter.c,v $
+* $Log$
* Revision 1.1 2007/01/30 03:19:43 koennecke
* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh!
*
@@ -87,7 +87,7 @@
*******************************************************************************/
#ifndef MCSTAS_R_H
-#define MCSTAS_R_H "$Revision: 1.1 $"
+#define MCSTAS_R_H "$Revision$"
#include
#include
@@ -569,9 +569,9 @@ void mcsiminfo_close(void);
*
* Usage: Automatically embbeded in the c code whenever required.
*
-* $Id: dmcafter.c,v 1.1 2007/01/30 03:19:43 koennecke Exp $
+* $Id$
*
-* $Log: dmcafter.c,v $
+* $Log$
* Revision 1.1 2007/01/30 03:19:43 koennecke
* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh!
*
@@ -4356,9 +4356,9 @@ MCNUM mccDet9_zmax;
* %include "read_table-lib"
*
*
-* $Id: dmcafter.c,v 1.1 2007/01/30 03:19:43 koennecke Exp $
+* $Id$
*
-* $Log: dmcafter.c,v $
+* $Log$
* Revision 1.1 2007/01/30 03:19:43 koennecke
* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh!
*
@@ -4433,9 +4433,9 @@ static void Table_Stat(t_Table *mc_rt_Table);
* Usage: within SHARE
* %include "read_table-lib"
*
-* $Id: dmcafter.c,v 1.1 2007/01/30 03:19:43 koennecke Exp $
+* $Id$
*
-* $Log: dmcafter.c,v $
+* $Log$
* Revision 1.1 2007/01/30 03:19:43 koennecke
* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh!
*
@@ -5284,9 +5284,9 @@ long Virtual_input_Read_Input(char *aFile, char *aType, t_Table *aTable, long *a
* Usage: within SHARE
* %include "monitor_nd-lib"
*
-* $Id: dmcafter.c,v 1.1 2007/01/30 03:19:43 koennecke Exp $
+* $Id$
*
-* $Log: dmcafter.c,v $
+* $Log$
* Revision 1.1 2007/01/30 03:19:43 koennecke
* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh!
*
@@ -5460,9 +5460,9 @@ void Monitor_nD_McDisplay(MonitornD_Defines_type *,
* Usage: within SHARE
* %include "monitor_nd-lib"
*
-* $Id: dmcafter.c,v 1.1 2007/01/30 03:19:43 koennecke Exp $
+* $Id$
*
-* $Log: dmcafter.c,v $
+* $Log$
* Revision 1.1 2007/01/30 03:19:43 koennecke
* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh!
*
diff --git a/modriv.h b/modriv.h
index f84d0df8..d6f01dd5 100644
--- a/modriv.h
+++ b/modriv.h
@@ -14,6 +14,7 @@
#define MOTREDO -1
#define MOTFAIL 0
#define MOTOK 1
+#define TEXTPARLEN 1024
typedef struct __AbstractMoDriv {
/* general motor driver interface
@@ -33,6 +34,7 @@ typedef struct __AbstractMoDriv {
char *name, float newValue);
void (*ListDriverPar) (void *self, char *motorName, SConnection * pCon);
void (*KillPrivate) (void *self);
+ int (*GetDriverTextPar) (void *self, char *name, char *textPar);
} MotorDriver;
/* the first fields above HAVE to be IDENTICAL to those below */
@@ -56,6 +58,7 @@ typedef struct ___MoSDriv {
char *name, float newValue);
void (*ListDriverPar) (void *self, char *motorName, SConnection * pCon);
void (*KillPrivate) (void *self);
+ int (*GetDriverTextPar) (void *self, char *name, char *textPar);
/* Simulation specific fields */
float fFailure; /* percent random failures */
diff --git a/motor.c b/motor.c
index 6347bdf6..458614f0 100644
--- a/motor.c
+++ b/motor.c
@@ -81,6 +81,8 @@
#define IGNOREFAULT 10
#define MOVECOUNT 11
+extern double DoubleTime(void);
+
/*-------------------------------------------------------------------------*/
static void SetMotorError(pMotor self, char *text)
{
@@ -351,8 +353,9 @@ static int checkPosition(pMotor self, SConnection * pCon)
SCWrite(pCon, pBueffel, eWarning);
return HWFault;
}
- snprintf(pBueffel, 131, "WARNING: %s off position by %f",
- self->name, absf(fHard - self->fTarget));
+ snprintf(pBueffel, 131, "WARNING: %s off position by %f%s",
+ self->name, absf(fHard - self->fTarget),
+ self->fTarget > fHard ? "-" : "");
SCWrite(pCon, pBueffel, eLog);
status = statusRunTo(self, pCon);
return status;
@@ -361,16 +364,19 @@ static int checkPosition(pMotor self, SConnection * pCon)
}
/*--------------------------------------------------------------------*/
-static void finishDriving(pMotor self, SConnection * pCon)
+void finishDriving(pMotor self, SConnection * pCon)
{
MotCallback sCall;
MotorGetSoftPosition(self, pCon, &sCall.fVal);
sCall.pName = self->name;
self->fPosition = sCall.fVal;
self->fPosition = sCall.fVal;
- InvokeCallBack(self->pCall, MOTDRIVE, &sCall); /* send also very last position */
- InvokeCallBack(self->pCall, MOTEND, &sCall);
- tracePar(self->name,"%f",sCall.fVal);
+ if (self->moving) {
+ InvokeCallBack(self->pCall, MOTDRIVE, &sCall); /* send also very last position */
+ InvokeCallBack(self->pCall, MOTEND, &sCall);
+ tracePar(self->name,"%f",sCall.fVal);
+ }
+ self->moving = 0;
self->running = 0;
}
@@ -474,13 +480,15 @@ static void handleMoveCallback(pMotor self, SConnection * pCon)
{
MotCallback sCall;
- self->posCount++;
- if (self->posCount >= ObVal(self->ParArray, MOVECOUNT)) {
+ double current_time, skip_time;
+ current_time = DoubleTime();
+ skip_time = 0.001 * ObVal(self->ParArray,MOVECOUNT);
+ if(self->last_report_time + skip_time <= current_time) {
MotorGetSoftPosition(self, pCon, &sCall.fVal);
sCall.pName = self->name;
InvokeCallBack(self->pCall, MOTDRIVE, &sCall);
tracePar(self->name,"%f",sCall.fVal);
- self->posCount = 0;
+ self->last_report_time = current_time;
}
}
@@ -749,8 +757,10 @@ static long MotorRunImpl(void *sulf, SConnection * pCon, float fNew)
self->posFaultCount = 0;
self->retryCount = 0;
self->stopped = 0;
+ self->moving = 1;
self->fTarget = fHard;
InvokeCallBack(self->pCall, HDBVAL, self);
+ self->last_report_time = 0.0;
self->posCount = 0;
iRet = self->pDriver->RunTo(self->pDriver, fHard);
if (iRet != OKOK) { /* try three times to fix it */
@@ -824,7 +834,7 @@ pMotor MotorInit(char *drivername, char *name, MotorDriver * pDriv)
ObParInit(pM->ParArray, ECOUNT, "failafter", 3.0, usMugger);
ObParInit(pM->ParArray, POSCOUNT, "maxretry", 3.0, usMugger);
ObParInit(pM->ParArray, IGNOREFAULT, "ignorefault", 0.0, usMugger);
- ObParInit(pM->ParArray, MOVECOUNT, "movecount", 10.0, usMugger);
+ ObParInit(pM->ParArray, MOVECOUNT, "movecount", 500.0, usMugger);
pDriv->GetPosition(pDriv, &(pM->fPosition));
pM->fTarget = pM->fPosition;
pM->endScriptID = 0;
@@ -1210,6 +1220,20 @@ static int EndScriptCallback(int iEvent, void *pEvent, void *pUser)
return iRet;
}
+/*
+ * Context test function for callback removal
+ */
+ int CheckMotiMatch(const void* context, const void* pUserData)
+{
+ pMotInfo pMoti = (pMotInfo) pUserData;
+ SConnection *pCon = (SConnection*) context;
+ if (VerifyConnection(pCon) && VerifyConnection(pMoti->pCon)) {
+ if (pMoti->pCon->ident == pCon->ident)
+ return 0;
+ }
+ return 1;
+}
+
/*----------------------------------------------------------------------------
The wrapper function for a motor. Commands currently supported are:
@@ -1305,13 +1329,14 @@ int MotorAction(SConnection * pCon, SicsInterp * pSics, void *pData,
}
pMoti->lastValue = fValue;
+ RemoveCallbackUsr(self->pCall, InterestCallback, CheckMotiMatch, pCon); /* only this one */
lID = RegisterCallback(self->pCall, MOTDRIVE, InterestCallback,
pMoti, KillInfo);
DeleteTokenList(pList);
SCSendOK(pCon);
return 1;
} else if (strcmp(pCurrent->text, "uninterest") == 0) {
- RemoveCallbackCon(self->pCall, pCon);
+ RemoveCallbackUsr(self->pCall, InterestCallback, CheckMotiMatch, pCon); /* only this one */
SCSendOK(pCon);
DeleteTokenList(pList);
return 1;
diff --git a/motor.h b/motor.h
index 72e32d50..c5908674 100644
--- a/motor.h
+++ b/motor.h
@@ -41,6 +41,9 @@ typedef struct __Motor {
int stopReported;
int errorCount;
int running;
+ int moving;
+ double last_report_time;
+ ObjectFunc pActionRoutine;
ObPar *ParArray;
void *pPrivate;
void (*KillPrivate) (void *);
diff --git a/motorsec.c b/motorsec.c
index 6f0ed33a..d33e7076 100644
--- a/motorsec.c
+++ b/motorsec.c
@@ -298,7 +298,7 @@ static int SecMotorStatus(void *sulf, SConnection * pCon)
int status;
pHdb node = NULL;
hdbValue v;
- float interrupt;
+ float interrupt = 0.;
char error[132];
assert(sulf);
@@ -350,7 +350,7 @@ static int SecMotorStatus(void *sulf, SConnection * pCon)
case HWFault:
self->posCount = 10000;
handleMoveCallback(self, pCon);
- SecMotorGetPar(self, "interrupt", &interrupt);
+ SecMotorGetPar(self, "interruptmode", &interrupt);
if (SCGetInterrupt(pCon) < (int) interrupt) {
SCSetInterrupt(pCon, (int) interrupt);
}
@@ -527,7 +527,7 @@ static hdbCallbackReturn SecMotorCallback(pHdb node, void *userData,
SCSetInterrupt(pCon, eAbortBatch);
self->pDrivInt->iErrorCount = 0;
child = GetHipadabaNode(self->pDescriptor->parNode, "status");
- UpdateHipadabaPar(child, MakeHdbText("run"), pCon);
+ UpdateHipadabaPar(child, MakeHdbText("error"), pCon);
return hdbAbort;
}
diff --git a/multicounter.c b/multicounter.c
index a5c0647a..2e190df9 100644
--- a/multicounter.c
+++ b/multicounter.c
@@ -31,6 +31,14 @@
#define MAXSLAVE 16
#define NOCOUNTERS -2727
+
+/*
+ checkSlaves codes
+*/
+#define WAITMASTER 100
+#define WAITSLAVE 200
+#define FINISHED 300
+
/*=============== code for the driver ======================================*/
typedef struct {
void *slaveData[MAXSLAVE];
@@ -107,7 +115,7 @@ static int MMCCStart(void *pData, SConnection * pCon)
pCount->isUpToDate = 0;
pCount->tStart = time(NULL);
InvokeCallBack(pCount->pCall, COUNTSTART, pCon);
- self->checkSlaves = 0;
+ self->checkSlaves = WAITMASTER;
return OKOK;
}
@@ -115,7 +123,7 @@ static int MMCCStart(void *pData, SConnection * pCon)
static int MMCCStatus(void *pData, SConnection * pCon)
{
- int status, i;
+ int status = HWIdle, i;
pCounter pCount = NULL, pMaster = NULL;;
pMultiCounter self = NULL;
pDummy pDum = NULL;
@@ -132,7 +140,7 @@ static int MMCCStatus(void *pData, SConnection * pCon)
return HWFault;
}
- if(self->checkSlaves == 0) {
+ if(self->checkSlaves == WAITMASTER) {
status = self->slaves[0]->CheckCountStatus(self->slaveData[0], pCon);
if (status == HWIdle || status == HWFault) {
/*
@@ -141,11 +149,11 @@ static int MMCCStatus(void *pData, SConnection * pCon)
*/
MMCCHalt(pData);
ReleaseCountLock(pCount->pCountInt);
- self->checkSlaves = 1;
+ self->checkSlaves = WAITSLAVE;
status = HWBusy;
}
pCount->pDriv->fLastCurrent = GetControlValue(self->slaveData[0]);
- } else {
+ } else if(self->checkSlaves == WAITSLAVE) {
/*
* wait for the detectors to report finish too. Otherwise, with the second
* generation HM data may not be fully transferred.
@@ -172,7 +180,9 @@ static int MMCCStatus(void *pData, SConnection * pCon)
}
status = HWIdle;
InvokeCallBack(pCount->pCall, COUNTEND, pCon);
- self->checkSlaves = 0;
+ self->checkSlaves = FINISHED;
+ } else if(self->checkSlaves == FINISHED){
+ status = HWIdle;
}
return status;
}
@@ -530,6 +540,7 @@ int MakeMultiCounter(SConnection * pCon, SicsInterp * pSics,
self->slaveData[self->nSlaves] = pCom->pData;
self->nSlaves++;
}
+ self->checkSlaves = FINISHED;
/*
now install our action command and we are done
diff --git a/mumo.c b/mumo.c
index 96b2d9cc..c3ab138e 100644
--- a/mumo.c
+++ b/mumo.c
@@ -396,7 +396,7 @@ static int ParseAlias(psParser pParse, SConnection * pCon, pMulMot self)
return 1;
break;
default:
- snprintf(pBueffel,1024, "ERROR: Unexpected symbol %s", pParse->Token);
+ snprintf(pBueffel,sizeof(pBueffel), "ERROR: Unexpected symbol %s", pParse->Token);
SCWrite(pCon, pBueffel, eError);
return 0;
}
diff --git a/napiu.c b/napiu.c
index 63ad8c22..2a8d9de8 100644
--- a/napiu.c
+++ b/napiu.c
@@ -21,10 +21,10 @@
For further information, see
- $Id: napiu.c,v 1.4 2012/03/29 08:41:06 koennecke Exp $
+ $Id$
----------------------------------------------------------------------------*/
-static const char* rscid = "$Id: napiu.c,v 1.4 2012/03/29 08:41:06 koennecke Exp $"; /* Revision interted by CVS */
+static const char* rscid = "$Id$"; /* Revision interted by CVS */
#include
#include
diff --git a/napiu.h b/napiu.h
index 1d67b66f..3f9c3adb 100644
--- a/napiu.h
+++ b/napiu.h
@@ -21,7 +21,7 @@
For further information, see
- $Id: napiu.h,v 1.3 2009/02/13 09:00:20 koennecke Exp $
+ $Id$
----------------------------------------------------------------------------*/
diff --git a/network.c b/network.c
index f6b7d709..944ecacf 100644
--- a/network.c
+++ b/network.c
@@ -188,7 +188,7 @@ mkChannel *NETAccept(mkChannel * self, long timeout)
return NULL; /* eof */
}
iRet =
- uselect((self->sockid + 1), (fd_set *) & lMask, NULL, NULL, &tmo);
+ uselect((self->sockid + 1), &lMask, NULL, NULL, &tmo);
if (iRet <= 0) {
/* failure, or no request */
return NULL;
@@ -327,7 +327,7 @@ int NETConnectFinished(mkChannel * self)
return -1;
}
oldopts = fcntl(self->sockid, F_GETFL, 0);
- if (!(oldopts | O_NONBLOCK)) {
+ if (!(oldopts & O_NONBLOCK)) { /* DCL was | */
/* assume success when in blocking mode */
return 1;
}
@@ -360,7 +360,7 @@ int NETConnectFinished(mkChannel * self)
}
/* reset to blocking mode */
olderrno = errno;
- fcntl(self->sockid, F_SETFL, oldopts | ~O_NONBLOCK);
+ fcntl(self->sockid, F_SETFL, oldopts & ~O_NONBLOCK); /* DCL was | */
errno = olderrno;
return iret;
}
@@ -679,7 +679,7 @@ int NETReadTillTerm(mkChannel * self, long timeout,
status = NETAvailable(self, timeout - dif);
};
};
- return status;
+ return bufPtr;
}
/*-------------------------------------------------------------------------*/
@@ -753,14 +753,20 @@ int NETReconnectWithFlags(mkChannel * self, int flags)
if (self->sockid != 0) {
close(self->sockid);
}
- /* Reopen and try to get it on the olf fd */
+ /* Reopen and try to get it on the old fd */
sock = socket(AF_INET, SOCK_STREAM, 0);
+ /* If this isn't the same fd, try to move it over */
if (self->sockid != sock) {
+ /* Duplicate the new socket with the old fd if we can */
iRet = fcntl(sock, F_DUPFD, self->sockid);
- if (iRet != sock)
+ if (iRet != self->sockid) {
+ /* If we didn't get the one we want, use original and close new */
self->sockid = sock;
- else
+ close(iRet);
+ } else {
+ /* If we did get the one we want, close original and use old */
close(sock);
+ }
sock = self->sockid;
}
/* restore the old flags */
@@ -798,7 +804,7 @@ int NETReconnectWithFlags(mkChannel * self, int flags)
if (iRet <= 0)
iRet = -1; /* failure */
}
- if (FD_ISSET(self->sockid, &wmask)) {
+ else if (FD_ISSET(self->sockid, &wmask)) {
iRet = send(self->sockid, NULL, 0, 0);
if (iRet < 0)
iRet = -1; /* failure */
@@ -920,7 +926,7 @@ mkChannel *UDPConnect(char *name, int port)
/*--------------------------------------------------------------------------*/
long UDPRead(mkChannel * self, char *buffer, long lLen, int timeout)
{
- long lMask = 0L;
+ fd_set lMask;
struct timeval tmo = { 0, 1 };
long iRet;
socklen_t iLang;
@@ -934,9 +940,10 @@ long UDPRead(mkChannel * self, char *buffer, long lLen, int timeout)
/* setup for select first */
tmo.tv_usec = (timeout % 1000) * 1000;
tmo.tv_sec = timeout / 1000;
- lMask = (1 << self->sockid);
+ FD_ZERO(&lMask);
+ FD_SET(self->sockid, &lMask);
iRet =
- uselect((self->sockid + 1), (fd_set *) & lMask, NULL, NULL, &tmo);
+ uselect((self->sockid + 1), &lMask, NULL, NULL, &tmo);
if (iRet <= 0) {
/* failure, or no data */
return 0;
diff --git a/nread.c b/nread.c
index 75310ce6..5ccf8e43 100644
--- a/nread.c
+++ b/nread.c
@@ -42,6 +42,8 @@
#include "commandlog.h"
#include "uselect.h"
#include "trace.h"
+#include "protocol.h"
+
extern pServer pServ;
extern int VerifyChannel(mkChannel * self); /* defined in network.c */
@@ -294,15 +296,25 @@ static int NetReadRead(pNetRead self, pNetItem pItem)
*pEnd = '\0';
/* do we have something in hold ? */
if (strlen(pItem->pHold) > 0) {
- strlcat(pItem->pHold, pPtr,511);
- iStat = CostaTop(pItem->pCon->pStack, pItem->pHold);
+ strlcat(pItem->pHold, pPtr, 511);
+ /* DFC locking for protocol zero only */
+ if (pItem->pCon->iProtocolID == PROTSICS &&
+ CostaLocked(pItem->pCon->pStack))
+ iStat = 0;
+ else
+ iStat = CostaTop(pItem->pCon->pStack, pItem->pHold);
if (!iStat) {
SCWrite(pItem->pCon, "ERROR: Busy", eError);
}
pItem->pHold[0] = '\0';
} else {
/* no, normal command */
- iStat = CostaTop(pItem->pCon->pStack, pPtr);
+ /* DFC locking for protocol zero only */
+ if (pItem->pCon->iProtocolID == PROTSICS &&
+ CostaLocked(pItem->pCon->pStack))
+ iStat = 0;
+ else
+ iStat = CostaTop(pItem->pCon->pStack, pPtr);
if (!iStat) {
SCWrite(pItem->pCon, "ERROR: Busy", eError);
}
@@ -487,7 +499,12 @@ static int TelnetRead(pNetRead self, pNetItem pItem)
break;
case '\r':
case '\n':
- iStat = CostaTop(pItem->pCon->pStack, pItem->pHold);
+ /* DFC locking for protocol zero only */
+ if (pItem->pCon->iProtocolID == PROTSICS &&
+ CostaLocked(pItem->pCon->pStack))
+ iStat = 0;
+ else
+ iStat = CostaTop(pItem->pCon->pStack, pItem->pHold);
/* printf("%s\n",pItem->pHold); */
if (!iStat) {
SCWrite(pItem->pCon, "ERROR: Busy", eError);
@@ -1027,6 +1044,7 @@ static int testAndInvokeInterrupt(pCommandCBData self, int handle)
snprintf(buffer, 512, "INTERRUPT %d issued on sock %d",
iInt, handle);
WriteToCommandLog("SYS>", buffer);
+ SICSLogWrite(buffer, eInternal);
if (iInt == eEndServer) {
TaskStop(pServ->pTasker);
}
@@ -1060,8 +1078,10 @@ static int CommandDataCB(int handle, void *userData)
if (pPtr[i] == '\r' || pPtr[i] == '\n') {
self->state = SKIPTERM;
if (!testAndInvokeInterrupt(self, handle)) {
- status =
- CostaTop(self->pCon->pStack, GetCharArray(self->command));
+ if (self->pCon->iProtocolID == PROTSICS && CostaLocked(self->pCon->pStack))
+ status = 0;
+ else
+ status = CostaTop(self->pCon->pStack, GetCharArray(self->command));
if (!status) {
SCWrite(self->pCon, "ERROR: Busy", eError);
}
@@ -1162,8 +1182,10 @@ static int ANETTelnetProcess(int handle, void *usData)
case '\r':
case '\n':
if (!testAndInvokeInterrupt(self, handle)) {
- status =
- CostaTop(self->pCon->pStack, GetCharArray(self->command));
+ if (self->pCon->iProtocolID == PROTSICS && CostaLocked(self->pCon->pStack))
+ status = 0;
+ else
+ status = CostaTop(self->pCon->pStack, GetCharArray(self->command));
if (!status) {
SCWrite(self->pCon, "ERROR: Busy", eError);
}
@@ -1333,8 +1355,8 @@ static int TelnetAcceptCB(int handle, void *userData)
/*------------------------------------------------------------------------------------*/
static void NREADlog(int level, char *txt, void *userData)
{
- traceSys("anet",txt);
- puts(txt);
+ traceSys("anet","%s",txt);
+ SICSLogWrite(txt, (level == ANETERROR) ? eLogError : eLog);
}
/*------------------------------------------------------------------------------------*/
diff --git a/nserver.c b/nserver.c
index 5f5fb02a..06ed9087 100644
--- a/nserver.c
+++ b/nserver.c
@@ -40,12 +40,16 @@
#include "commandlog.h"
-extern void StopExit(void); /* in SICSmain.c */
-
extern int openDevexecLog(); /* in devexec.c */
-extern void NetWatchInit(void); /* in nwatch.c */
-/* ========================= Less dreadful file statics =================== */
+extern int NetWatchTask(void *pData); /* in nwatch.c */
+/*------------------------------------------------------------------------*/
+static void StopExit(void)
+{
+ if (pServ) {
+ StopServer(pServ);
+ }
+}
#define DEFAULTINIFILE "servo.tcl"
#define DEFAULTSTATUSFILE "sicsstat.tcl"
@@ -116,7 +120,7 @@ int InitServer(char *file, pServer * pServ)
pSICSOptions = IFAddOption(pSICSOptions, "ConMask", "0");
/* initialize the network watcher */
- NetWatchInit();
+ TaskRegister(self->pTasker, NetWatchTask, NULL, NULL, NULL, 1);
/* initialise the server from script */
SetWriteHistory(0);
@@ -507,3 +511,23 @@ int ServerIsStarting(pServer self)
{
return self->pReader == NULL;
}
+
+pServer pServ = NULL;
+
+/*--------------------------------------------------------------------------*/
+SicsInterp *GetInterpreter(void)
+{
+ return pServ->pSics;
+}
+
+/*--------------------------------------------------------------------------*/
+pExeList GetExecutor(void)
+{
+ return pServ->pExecutor;
+}
+
+/*-------------------------------------------------------------------------*/
+pTaskMan GetTasker(void)
+{
+ return pServ->pTasker;
+}
diff --git a/nwatch.c b/nwatch.c
index 43e862eb..9a59ea77 100644
--- a/nwatch.c
+++ b/nwatch.c
@@ -18,8 +18,8 @@
#include
#include "fortify.h"
#include "nwatch.h"
-#include "sics.h"
#include "uselect.h"
+#include "sics.h"
#define NWMAGIC 51966
@@ -36,14 +36,12 @@ typedef struct __netwatcher_s {
/* Singleton pattern */
static pNetWatch instance = NULL;
-static int NetWatchTask(void *pData);
-
/**
* \brief Initialises the Net Watcher singleton and starts the task
*
* \return 1=success, 0=failure
*/
-int NetWatchInit(void)
+static pNetWatch NetWatchGetInstance(void)
{
/*
* If the singleton has not yet been created, do so now
@@ -54,9 +52,8 @@ int NetWatchInit(void)
return 0;
memset(instance, 0, sizeof(NetWatch));
instance->lMagic = NWMAGIC;
- TaskRegisterN(pServ->pTasker,"nwatch", NetWatchTask, NULL, NULL, NULL, 1);
}
- return 1;
+ return instance;
}
/*
@@ -67,10 +64,13 @@ typedef struct __netwatchtimer {
struct timeval tv; /* time when event is due */
pNWCallback func; /* function to call */
void *cntx; /* abstract context to pass to callback */
- long int tick; /* millisecond repeat rate */
+ int msec; /* millisecond delay time */
+ int tick; /* millisecond repeat rate */
long int vrfy; /* integrity check */
} NWTimer;
+static pNWTimer activeTimer = NULL;
+
/*
* \brief private function to insert an entry into the sorted timer queue.
*
@@ -83,6 +83,7 @@ static int NetWatchTimerInsQue(pNetWatch self, pNWTimer handle)
if (self->tq_head == NULL) {
self->tq_head = self->tq_tail = handle;
handle->next = NULL;
+ handle->vrfy = NWMAGIC;
return 1;
}
/* if new one is not earlier than latest one, insert after latest */
@@ -92,6 +93,7 @@ static int NetWatchTimerInsQue(pNetWatch self, pNWTimer handle)
self->tq_tail->next = handle;
self->tq_tail = handle;
handle->next = NULL;
+ handle->vrfy = NWMAGIC;
return 1;
}
/* if new one is not later than earliest one, insert before earliest */
@@ -100,6 +102,7 @@ static int NetWatchTimerInsQue(pNetWatch self, pNWTimer handle)
handle->tv.tv_usec <= self->tq_head->tv.tv_usec)) {
handle->next = self->tq_head;
self->tq_head = handle;
+ handle->vrfy = NWMAGIC;
return 1;
} else {
/* must be in between two so start at the first entry */
@@ -113,8 +116,10 @@ static int NetWatchTimerInsQue(pNetWatch self, pNWTimer handle)
/* slip new one in between this one and the next one */
handle->next = pNxt->next;
pNxt->next = handle;
+ handle->vrfy = NWMAGIC;
+ return 1;
}
- return 1;
+ return 0;
}
/*
@@ -125,6 +130,7 @@ static int NetWatchTimerInsQue(pNetWatch self, pNWTimer handle)
*/
static int NetWatchTimerRemQue(pNetWatch self, pNWTimer handle)
{
+ assert(handle->vrfy == NWMAGIC);
/* handle the case of first and possibly only */
if (handle == self->tq_head) {
self->tq_head = self->tq_head->next; /* may be NULL */
@@ -145,13 +151,15 @@ static int NetWatchTimerRemQue(pNetWatch self, pNWTimer handle)
if (handle == self->tq_tail)
self->tq_tail = pNxt;
}
+ handle->vrfy = 0;
return 1;
}
int NetWatchRegisterTimer(pNWTimer * handle, int mSec,
pNWCallback callback, void *context)
{
- pNetWatch self = instance;
+ assert(callback);
+ pNetWatch self = NetWatchGetInstance();
if (!self || self->lMagic != NWMAGIC)
return 0;
pNWTimer pNew = (pNWTimer) malloc(sizeof(NWTimer));
@@ -165,10 +173,10 @@ int NetWatchRegisterTimer(pNWTimer * handle, int mSec,
pNew->tv.tv_sec++;
pNew->tv.tv_usec -= 1000000;
}
+ pNew->msec = mSec;
pNew->tick = 0;
pNew->func = callback;
pNew->cntx = context;
- pNew->vrfy = NWMAGIC;
NetWatchTimerInsQue(self, pNew);
*handle = pNew;
return 1;
@@ -178,6 +186,7 @@ int NetWatchRegisterTimerPeriodic(pNWTimer * handle, int mSecInitial,
int mSecPeriod, pNWCallback callback,
void *context)
{
+ assert(callback);
if (NetWatchRegisterTimer(handle, mSecInitial, callback, context)) {
pNWTimer pNew = *handle;
if (pNew == NULL)
@@ -189,16 +198,36 @@ int NetWatchRegisterTimerPeriodic(pNWTimer * handle, int mSecInitial,
return 0;
}
+pNWTimer NetWatchGetActiveTimer(void)
+{
+ return activeTimer;
+}
+
+int NetWatchGetTimerInitial(pNWTimer handle)
+{
+ if (handle == NULL
+ || (handle->vrfy != NWMAGIC && handle->vrfy != ~NWMAGIC))
+ return 0;
+ return handle->msec;
+}
+
+int NetWatchGetTimerDelay(pNWTimer handle)
+{
+ return NetWatchGetTimerInitial(handle);
+}
+
int NetWatchGetTimerPeriod(pNWTimer handle)
{
- if (handle == NULL || handle->vrfy != NWMAGIC)
+ if (handle == NULL
+ || (handle->vrfy != NWMAGIC && handle->vrfy != ~NWMAGIC))
return 0;
return handle->tick;
}
int NetWatchSetTimerPeriod(pNWTimer handle, int mSecPeriod)
{
- if (handle == NULL || handle->vrfy != NWMAGIC)
+ if (handle == NULL
+ || (handle->vrfy != NWMAGIC && handle->vrfy != ~NWMAGIC))
return 0;
handle->tick = mSecPeriod;
return 1;
@@ -206,9 +235,11 @@ int NetWatchSetTimerPeriod(pNWTimer handle, int mSecPeriod)
int NetWatchRemoveTimer(pNWTimer handle)
{
- pNetWatch self = instance;
+ pNetWatch self = NetWatchGetInstance();
if (!self || self->lMagic != NWMAGIC)
return 0;
+ if (handle == NULL || handle->vrfy != NWMAGIC)
+ return 0;
NetWatchTimerRemQue(self, handle);
handle->vrfy = 0;
free(handle);
@@ -239,35 +270,10 @@ static int NetWatchContextInsQue(pNetWatch self, pNWContext handle)
self->cq_tail->next = handle;
self->cq_tail = handle;
}
+ handle->vrfy = NWMAGIC;
return 1;
}
-/**
- * \brief private function to remove entry from unsorted queue
- *
- * \param self singleton
- * \param handle entry to insert
- */
-static void NetWatchContextRemQue(pNetWatch self, pNWContext handle)
-{
- if (handle == self->cq_head) { /* if first */
- self->cq_head = self->cq_head->next;
- if (handle == self->cq_tail) /* if also last */
- self->cq_tail = NULL;
- } else {
- pNWContext pNxt = self->cq_head;
- while (pNxt) {
- if (handle == pNxt->next) {
- pNxt->next = pNxt->next->next;
- break;
- }
- pNxt = pNxt->next;
- }
- if (handle == self->cq_tail) /* if last */
- self->cq_tail = pNxt;
- }
- return;
-}
/**
* \brief private function to purge invalid entries
@@ -286,19 +292,19 @@ static void NetWatchContextPrgQue(pNetWatch self)
free(tmp);
}
pNxt = self->cq_head;
- while (pNxt) {
- if (pNxt->next && pNxt->next->sock < 0) {
+ while (pNxt && pNxt->next) {
+ if (pNxt->next->sock < 0) {
pNWContext tmp = NULL;
tmp = pNxt->next;
pNxt->next = pNxt->next->next;
tmp->vrfy = 0;
free(tmp);
+ continue;
}
pNxt = pNxt->next;
}
- /* if the queue is empty clear the tail */
- if (self->cq_head == NULL)
- self->cq_tail = pNxt;
+ /* if the queue is empty then pNxt is NULL else pNxt points to the tail */
+ self->cq_tail = pNxt;
self->nInvalid = 0;
return;
}
@@ -306,8 +312,9 @@ static void NetWatchContextPrgQue(pNetWatch self)
int NetWatchRegisterCallback(pNWContext * handle, int iSocket,
pNWCallback callback, void *context)
{
+ assert(callback);
pNWContext pNew = NULL;
- pNetWatch self = instance;
+ pNetWatch self = NetWatchGetInstance();
if (!self || self->lMagic != NWMAGIC)
return 0;
if (iSocket < 0 || iSocket > 65535)
@@ -320,7 +327,6 @@ int NetWatchRegisterCallback(pNWContext * handle, int iSocket,
pNew->mode = nwatch_read;
pNew->func = callback;
pNew->cntx = context;
- pNew->vrfy = NWMAGIC;
*handle = pNew;
NetWatchContextInsQue(self, pNew);
return 1;
@@ -328,13 +334,16 @@ int NetWatchRegisterCallback(pNWContext * handle, int iSocket,
int NetWatchRemoveCallback(pNWContext handle)
{
- pNetWatch self = instance;
+ pNetWatch self = NetWatchGetInstance();
if (handle == NULL || handle->vrfy != NWMAGIC)
return 0;
if (!self || self->lMagic != NWMAGIC)
return 0;
+ /* mark as invalid */
handle->sock = -1;
+ /* increment count of invalid */
self->nInvalid++;
+ /* leave for garbage collection */
return 1;
}
@@ -354,7 +363,8 @@ int NetWatchSetMode(pNWContext handle, int mode)
}
/**
- * \brief the registered SICS Task to drive all this
+ * \brief the task to drive all this
+ * Should be called periodically
*/
int NetWatchTask(void *pData)
{
@@ -367,7 +377,7 @@ int NetWatchTask(void *pData)
int iCount;
/* Check the singleton */
- self = (pNetWatch) instance;
+ self = NetWatchGetInstance();
if (!self || self->lMagic != NWMAGIC)
return 0;
@@ -375,7 +385,7 @@ int NetWatchTask(void *pData)
if (self->nInvalid > 0)
NetWatchContextPrgQue(self);
- /* build the select mask */
+ /* build the select mask */
FD_ZERO(&rMask);
FD_ZERO(&wMask);
pNWC = self->cq_head;
@@ -430,7 +440,11 @@ int NetWatchTask(void *pData)
break;
}
NetWatchTimerRemQue(self, pNew);
+ activeTimer = pNew;
+ activeTimer->vrfy = ~NWMAGIC;
iStatus = pNew->func(pNew->cntx, 0);
+ activeTimer->vrfy = 0;
+ activeTimer = NULL;
/*
* If this is a recurrent timer and the function
* indicates to keep it going, put it back in
diff --git a/nwatch.h b/nwatch.h
index 03f41fb4..1df2b656 100644
--- a/nwatch.h
+++ b/nwatch.h
@@ -1,5 +1,5 @@
/*
- * N E T W A T C H E R
+ * N E T W A T C H
*
* This module watches network connections for sockets becoming readable or
* writeable and invokes callbacks. It also provides a timer mechanism.
@@ -7,8 +7,8 @@
* Douglas Clowes, February 2007
*
*/
-#ifndef SICSNETWATCHER
-#define SICSNETWATCHER
+#ifndef NETWATCH_H
+#define NETWATCH_H
#define nwatch_read 1
#define nwatch_write 2
@@ -53,6 +53,10 @@ int NetWatchRegisterTimerPeriodic(pNWTimer * handle, int mSecInitial,
int mSecPeriod, pNWCallback callback,
void *context);
+pNWTimer NetWatchGetActiveTimer(void);
+
+int NetWatchGetTimerDelay(pNWTimer handle);
+int NetWatchGetTimerInitial(pNWTimer handle);
int NetWatchGetTimerPeriod(pNWTimer handle);
int NetWatchSetTimerPeriod(pNWTimer handle, int mSecPeriod);
/**
@@ -103,4 +107,4 @@ int NetWatchGetMode(pNWContext handle);
*/
int NetWatchSetMode(pNWContext handle, int mode);
-#endif /* SICSNETWATCHER */
+#endif /* NETWATCH_H */
diff --git a/nxdict.c b/nxdict.c
index 23ec3629..34846919 100644
--- a/nxdict.c
+++ b/nxdict.c
@@ -173,9 +173,9 @@ static void NXDIParse(char *pBuffer, pStringDict pDict)
char *pPtr;
int iToken;
int iMode;
- char pAlias[132];
- char pDefinition[1024]; /* this is > 10 lines of definition */
- char pWord[132];
+ char pAlias[1024];
+ char pDefinition[8192]; /* this is > 10 lines of definition */
+ char pWord[1024];
assert(pBuffer);
assert(pDict);
@@ -1490,6 +1490,126 @@ NXstatus NXDinfoalias(NXhandle hFil, NXdict dict, char *pAlias, int *rank,
return iRet;
}
+/*------------------------------------------------------------------------*/
+NXstatus NXDdefnamedlink(NXhandle hFil, NXdict dict,
+ char *pTarget, char *pVictim, char *newname)
+{
+ NXdict pDict;
+ ParDat pParseT, pParseV;
+ int iRet, i, iStat;
+ NXlink sLink;
+
+ pDict = NXDIAssert(dict);
+
+
+#ifdef DEFDEBUG
+ printf("Linking: %s\n", pVictim);
+ printf("To: %s\n", pTarget);
+#endif
+
+
+ /* parse Victim */
+ pParseV.iMayCreate = 0;
+ pParseV.pPtr = pVictim;
+ pParseV.iDepth = 0;
+ iRet = NXDIDefParse(hFil, pDict, &pParseV);
+ if (iRet == NX_ERROR) {
+ /* unwind and throw up */
+ NXDIUnwind(hFil, pParseV.iDepth);
+ return NX_ERROR;
+ }
+ /* get link data */
+ if (pParseV.iTerminal == TERMSDS) {
+ NXgetdataID(hFil, &sLink);
+ iRet = NXclosedata(hFil);
+ if (iRet != NX_OK) {
+ /* unwind and throw up */
+ NXDIUnwind(hFil, pParseV.iDepth);
+ return NX_ERROR;
+ }
+ } else if (pParseV.iTerminal == TERMVG) {
+ NXgetgroupID(hFil, &sLink);
+ } else {
+ assert(0); /* serious programming error */
+ }
+ /* Unwind */
+ iRet = NXDIUnwind(hFil, pParseV.iDepth);
+ if (iRet != NX_OK) {
+ return NX_ERROR;
+ }
+
+ /* parse Target */
+ pParseT.iMayCreate = 1;
+ pParseT.pPtr = pTarget;
+ pParseT.iDepth = 0;
+ iRet = NXDIDefParse(hFil, pDict, &pParseT);
+ if (iRet == NX_ERROR) {
+ /* unwind and throw up */
+ NXDIUnwind(hFil, pParseT.iDepth);
+ return NX_ERROR;
+ }
+ /* check it being a vGroup! */
+ if (pParseT.iTerminal != TERMVG) {
+ NXReportError("ERROR: can link only into a vGroup");
+ NXDIUnwind(hFil, pParseT.iDepth);
+ return NX_ERROR;
+ }
+
+ /* link, finally */
+ iRet = NXmakenamedlink(hFil, newname, &sLink);
+ /* Unwind anyway */
+ iStat = NXDIUnwind(hFil, pParseT.iDepth);
+ if (iStat != NX_OK) {
+ return NX_ERROR;
+ }
+ return iStat;
+}
+
+/*--------------------------------------------------------------------------*/
+NXstatus NXDaliasnamedlink(NXhandle hFil, NXdict dict,
+ char *pTarget, char *pVictim, char *newname)
+{
+ char pTargetDef[2048], pVictimDef[2048];
+ int iRet;
+ NXdict pDict;
+ pDynString pRep1 = NULL, pRep2 = NULL;
+
+ pDict = NXDIAssert(dict);
+
+ /* get Target Definition String */
+ iRet = NXDget(pDict, pTarget, pTargetDef, 2047);
+ if (iRet != NX_OK) {
+ sprintf(pTargetDef, "ERROR: alias %s not recognized", pTarget);
+ NXReportError(pTargetDef);
+ return NX_ERROR;
+ }
+
+ /* get Victim definition string */
+ iRet = NXDget(pDict, pVictim, pVictimDef, 2047);
+ if (iRet != NX_OK) {
+ sprintf(pTargetDef, "ERROR: alias %s not recognized", pTarget);
+ NXReportError(pTargetDef);
+ return NX_ERROR;
+ }
+
+ /* do replacements */
+ pRep1 = NXDItextreplace(dict, pTargetDef);
+ pRep2 = NXDItextreplace(dict, pVictimDef);
+ if ((!pRep1) || (!pRep2)) {
+ if (pRep1)
+ DeleteDynString(pRep1);
+ if (pRep2)
+ DeleteDynString(pRep2);
+ return NX_ERROR;
+ }
+
+ /* call NXdeflin */
+ iRet = NXDdefnamedlink(hFil, pDict, GetCharArray(pRep1), GetCharArray(pRep2), newname);
+ DeleteDynString(pRep1);
+ DeleteDynString(pRep2);
+ return iRet;
+}
+
/*------------------------------------------------------------------------*/
NXstatus NXDdeflink(NXhandle hFil, NXdict dict,
char *pTarget, char *pVictim)
diff --git a/nxscript.c b/nxscript.c
index ef31f14f..242ca32d 100644
--- a/nxscript.c
+++ b/nxscript.c
@@ -462,6 +462,7 @@ static void putHdb(SConnection * pCon, SicsInterp * pSics, pNXScript self,
hdbValue v;
float fVal, *floatAr = NULL;
int i;
+ int start[5], size[5];
if (argc < 3) {
SCWrite(pCon, "ERROR: putHdb needs at least node name", eLogError);
@@ -484,6 +485,23 @@ static void putHdb(SConnection * pCon, SicsInterp * pSics, pNXScript self,
}
}
GetHipadabaPar(node, &v, pCon);
+ if (argc > 3 && strcmp(argv[3], "point") == 0) {
+ NXDopenalias(self->fileHandle, self->dictHandle, alias);
+ start[0] = atoi(argv[4]);
+ size[0] = 1;
+ switch (v.dataType) {
+ case HIPINT:
+ NXputslab(self->fileHandle, &v.v.intValue, start, size);
+ break;
+ case HIPFLOAT:
+ fVal = v.v.doubleValue;
+ NXputslab(self->fileHandle, &fVal, start, size);
+ break;
+ }
+ ReleaseHdbValue(&v);
+ NXopenpath(self->fileHandle, "/");
+ return;
+ }
switch (v.dataType) {
case HIPNONE:
return;
@@ -922,6 +940,7 @@ static void putHistogramMemoryChunked(SConnection * pCon,
}
/*----------------------------------------------------------------------*/
+#define HANUM 3
static void putSlab(SConnection * pCon, SicsInterp * pSics, pNXScript self,
int argc, char *argv[])
{
@@ -1986,6 +2005,35 @@ static void makeLink(SConnection * pCon, SicsInterp * pSics,
SCSendOK(pCon);
}
+/*------------------------------------------------------------------------*/
+extern NXstatus NXDaliasnamedlink(NXhandle hFil, NXdict dict,
+ char *pTarget, char *pVictim, char *newname);
+
+/*----------------------------------------------------------------------*/
+static void makeNamedLink(SConnection * pCon, SicsInterp * pSics,
+ pNXScript self, int argc, char *argv[])
+{
+ int status;
+ char pBueffel[256];
+
+ if (argc < 5) {
+ SCWrite(pCon, "ERROR: insufficient number of arguments to makenamedlink",
+ eLogError);
+ return;
+ }
+
+ status = NXDaliasnamedlink(self->fileHandle, self->dictHandle,
+ argv[2], argv[3], argv[4]);
+ if (status != NX_OK) {
+ snprintf(pBueffel, 255, "ERROR: linking %s against %s as %s failed",
+ argv[2], argv[3], argv[4]);
+ SCWrite(pCon, pBueffel, eLogError);
+ return;
+ }
+
+ SCSendOK(pCon);
+}
+
/*----------------------------------------------------------------------*/
static void updateDictVar(SConnection * pCon, pNXScript self, int argc,
char *argv[])
@@ -2095,6 +2143,10 @@ int NXScriptAction(SConnection * pCon, SicsInterp * pSics, void *pData,
makeLink(pCon, pSics, self, argc, argv);
return 1;
}
+ if (strcmp(argv[1], "makenamedlink") == 0) {
+ makeNamedLink(pCon, pSics, self, argc, argv);
+ return 1;
+ }
return 1;
}
diff --git a/obpar.c b/obpar.c
index c89ef725..d9e40ccb 100644
--- a/obpar.c
+++ b/obpar.c
@@ -176,7 +176,9 @@ int ObParSet(ObPar * self, char *obname, char *name, float fVal,
/* are we running? */
if(DevExecLevelRunning(pServ->pExecutor, RUNDRIVE)){
- snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: Cannot change parameter while running");
+ snprintf(pBueffel,sizeof(pBueffel)-1,
+ "ERROR: Cannot change %s.%s parameter while running",
+ obname, name);
SCWrite(pCon, pBueffel, eError);
return 0;
}
diff --git a/ofac.c b/ofac.c
index e7a9ffd2..ad08da80 100644
--- a/ofac.c
+++ b/ofac.c
@@ -16,8 +16,11 @@
#include "statusfile.h"
#include "site.h"
#include "sicshipadaba.h"
+#include "sicsglobal.h"
static unsigned int killStartupCommands = 1;
+int isDuringInitialization;
+
extern void DevExecInit(void); /* devexec.c */
/*--------------------------------------------------------------------------*/
@@ -93,6 +96,8 @@ static void InitIniCommands(SicsInterp * pInter)
PCMD("InternEval", InternalFileEval);
PCMD("kill_command", SICSKill);
PCMD("list", SicsList);
+ PCMD("MakeAsyncProtocol", AsyncProtocolFactory);
+ PCMD("MakeAsyncQueue", AsyncQueueFactory);
PCMD("MakeMcStasController", McStasControllerFactory);
PCMD("MakeMulti", MakeMulti);
PCMD("MakeOptimise", MakeOptimiser);
@@ -107,6 +112,7 @@ static void InitIniCommands(SicsInterp * pInter)
PCMD("sicscron", MakeCron);
PCMD("sicsdatafactory", SICSDataFactory);
PCMD("sicsdescriptor", SICSDescriptor);
+ PCMD("SICSLog", LogOutput);
PCMD("sicsprompt", SicsPrompt);
PCMD("SICSStatus", SICSStatus);
PCMD("sicstime", SICSTime);
@@ -126,8 +132,6 @@ static void InitIniCommands(SicsInterp * pInter)
SCMD("AntiCollisionInstall", AntiColliderFactory);
SCMD("ChopperAdapter", CHAdapterFactory);
SCMD("InstallSinfox", InstallSinfox);
- SCMD("MakeAsyncProtocol", AsyncProtocolFactory);
- SCMD("MakeAsyncQueue", AsyncQueueFactory);
SCMD("MakeBatchManager", MakeExeManager);
SCMD("MakeChopper", ChocoFactory);
SCMD("MakeCone", MakeCone);
@@ -240,6 +244,7 @@ int InitObjectCommands(pServer pServ, char *file)
if(killStartupCommands){
RemoveStartupCommands();
}
+ isDuringInitialization = 0;
return 1;
}
/*---------------------------------------------------------------------------------*/
diff --git a/protocol.c b/protocol.c
index 9891913d..168768e4 100644
--- a/protocol.c
+++ b/protocol.c
@@ -32,6 +32,10 @@ typedef struct __Protocol {
int isDefaultSet;
char *pProList[PROLISTLEN]; /* list of valid protocols? */
} Protocol;
+/*================================================================================================
+ WARNING: These two char arrays may replicate things defined elsewhere. They may be out of
+ sync with the rest of SIS. Keep in mind.....
+ ==================================================================================================*/
char *pEventType[] = {
"VALUECHANGE", /* 0 */
@@ -145,12 +149,11 @@ void DeleteProtocol(void *self)
if (pOld->version) {
free(pOld->version);
}
- if (pOld->pProList) {
i = 0;
while (NULL != pOld->pProList[i]) {
free(pOld->pProList[i]);
i++;
- }
+
}
free(pOld);
}
@@ -171,7 +174,7 @@ static int ContextDo(SConnection * pCon, SicsInterp * pSics, void *pData,
comCon = SCCopyConnection(pCon);
if (comCon == NULL) {
- SCWrite(pCon, "EROOR: out of memory in contextdo", eError);
+ SCWrite(pCon, "ERROR: out of memory in contextdo", eError);
return 0;
}
status = Tcl_GetInt(pSics->pTcl, argv[1], &comCon->transID);
@@ -188,7 +191,13 @@ static int ContextDo(SConnection * pCon, SicsInterp * pSics, void *pData,
SCWrite(pCon, "ERROR: no more memory", eError);
return 0;
}
+ if(comCon->transID > 100000) {
+ SCPrintf(comCon,eLog,"COMSTART %d", comCon->transID);
+ }
status = InterpExecute(pSics, comCon, command);
+ if(comCon->transID > 100000) {
+ SCPrintf(comCon,eLog,"COMEND %d", comCon->transID);
+ }
if (command != buffer)
free(command);
SCDeleteConnection(comCon);
@@ -271,29 +280,29 @@ static int ProtocolSet(SConnection * pCon, Protocol * pPro, char *pProName)
return 0;
break;
- case 1: /* normal (connection start default) */
+ case PROTNORM: /* normal (connection start default) */
SCSetWriteFunc(pMaster, SCNormalWrite);
SCSetWriteFunc(pCon, SCNormalWrite);
break;
- case 2: /* outcodes */
+ case PROTCODE: /* outcodes */
SCSetWriteFunc(pMaster, SCWriteWithOutcode);
SCSetWriteFunc(pCon, SCWriteWithOutcode);
break;
- case 3: /* json */
+ case PROTJSON: /* json */
SCSetWriteFunc(pCon, SCWriteJSON_String);
SCSetWriteFunc(pMaster, SCWriteJSON_String);
break;
- case 4: /* ACT */
+ case PROTACT: /* ACT */
SCSetWriteFunc(pMaster, SCACTWrite);
SCSetWriteFunc(pCon, SCACTWrite);
break;
- case 5:
+ case PROTALL:
SCSetWriteFunc(pMaster, SCAllWrite);
SCSetWriteFunc(pCon, SCAllWrite);
break;
- case 0: /* default = psi_sics */
+ case PROTSICS: /* default = psi_sics */
default:
SCSetWriteFunc(pMaster, pPro->defaultWriter);
SCSetWriteFunc(pCon, pPro->defaultWriter);
@@ -332,11 +341,11 @@ int ProtocolGet(SConnection * pCon, void *pData, char *pProName, int len)
/* check list of protocols for valid name */
switch (Index) {
- case 0: /* default = psi_sics */
- case 1: /* normal (connection start default) */
- case 2: /* outcodes */
- case 3: /* json */
- case 4: /* act */
+ case PROTSICS: /* default = psi_sics */
+ case PROTNORM: /* normal (connection start default) */
+ case PROTCODE: /* outcodes */
+ case PROTJSON: /* json */
+ case PROTACT: /* act */
pProName = pPro->pProList[Index];
return 1;
break;
@@ -441,7 +450,7 @@ static int InitDefaultProtocol(SConnection * pCon, Protocol * pPro)
if (0 == pPro->isDefaultSet) {
pPro->defaultWriter = SCGetWriteFunc(pCon);
pPro->isDefaultSet = 1;
- pCon->iProtocolID = 0;
+ pCon->iProtocolID = PROTSICS;
}
return pPro->isDefaultSet;
}
@@ -628,10 +637,11 @@ char *GetProtocolName(SConnection * pCon)
/* check list of protocols for valid name */
switch (pCon->iProtocolID) {
- case 0: /* default = psi_sics */
- case 1: /* normal (connection start default) */
- case 2: /* outcodes */
- case 3: /* json */
+ case PROTSICS: /* default = psi_sics */
+ case PROTNORM: /* normal (connection start default) */
+ case PROTCODE: /* outcodes */
+ case PROTJSON: /* json */
+ case PROTACT: /* act */
return strdup(pPro->pProList[pCon->iProtocolID]);
break;
default:
@@ -654,13 +664,13 @@ writeFunc GetProtocolWriteFunc(SConnection * pCon)
{
if (pCon != NULL) {
switch (pCon->iProtocolID) {
- case 2: /* outcodes */
+ case PROTCODE: /* outcodes */
return SCWriteWithOutcode;
break;
- case 3: /* json */
+ case PROTJSON: /* json */
return SCWriteJSON_String;
break;
- case 4:
+ case PROTACT:
return SCACTWrite;
break;
default:
diff --git a/protocol.h b/protocol.h
index 4151b4f7..67316967 100644
--- a/protocol.h
+++ b/protocol.h
@@ -15,6 +15,14 @@ static char *pProTags[3] = {
#define esStart -1
#define esFinish -2
+/*---------------------- protocol defines -------------------------------*/
+#define PROTSICS 0
+#define PROTNORM 1
+#define PROTCODE 2
+#define PROTJSON 3
+#define PROTACT 4
+#define PROTALL 5
+
/*--------------------- lifecycle -------------------------------------- */
int InstallProtocol(SConnection * pCon, SicsInterp * pSics, void *pData,
int argc, char *argv[]);
diff --git a/remob.c b/remob.c
index ce4cf1d9..15744131 100644
--- a/remob.c
+++ b/remob.c
@@ -313,7 +313,8 @@ static int RemTransact(RemServer * remserver, int nChan,
int try;
int argMask;
RemChannel *rc = &remserver->rc[nChan];
-
+ pDynString errorResult;
+
try = 3;
if (rc->timeout) { /* eat old responses */
while (RemRead(rc, 0) > 0) {
@@ -339,6 +340,20 @@ tryagain:
if (iRet <= 0)
goto close;
while (!StartsWith(rc->line, "TRANSACTIONFINISHED")) {
+ if (StartsWith(rc->line, "ERROR:")) {
+ errorResult = CreateDynString(60, 64);
+ do {
+ RemHandle(remserver);
+ DynStringConcat(errorResult, rc->line);
+ DynStringConcat(errorResult, "\n");
+ iRet = RemRead(rc, 2000);
+ } while (!StartsWith(rc->line, "TRANSACTIONFINISHED"));
+ if (pCon != NULL) {
+ SCPrintf(pCon, eError, "ERROR: in %s:\n%s", remserver->name, GetCharArray(errorResult));
+ }
+ DeleteDynString(errorResult);
+ return -2;
+ }
RemHandle(remserver);
va_start(ap, cmd);
arg = va_arg(ap, char *);
@@ -448,15 +463,17 @@ static float RemobGetValue(void *pData, SConnection * pCon)
SCDeleteConnection(remserver->conn);
}
remserver->conn = SCCopyConnection(pCon);
- none = -1.25e6;
+ none = -1.234e32;
value = none;
snprintf(buf, sizeof(buf), "<%s", remob->name);
/* get value needs only spy priviledge */
iRet =
RemTransact(remserver, 0, pCon, remob->name, buf, &value, ">", NULL);
+ /*
if (iRet <= 0) {
return 0.0;
}
+ */
if (value != none) {
return value;
}
@@ -654,7 +671,13 @@ int RemobAction(SConnection * pCon, SicsInterp * pSics, void *pData,
remserver->conn = SCCopyConnection(pCon);
}
if (argc == 1) {
- iRet = RemTransact(remserver, nChan, pCon, remob->name, ">", NULL);
+ /* filter out time stamps !=== */
+ iRet = RemTransact(remserver, nChan, pCon, remob->name, "!===", ">", NULL);
+ if (iRet < 0) {
+ iRet = 0;
+ } else {
+ iRet = 1;
+ }
} else if (strcasecmp(argv[1], "interest") == 0) {
/* ignore interest commands, as they would not work properly */
iRet = 1;
@@ -666,7 +689,8 @@ int RemobAction(SConnection * pCon, SicsInterp * pSics, void *pData,
cmd = Arg2Tcl0(argc - 1, argv + 1, buf, sizeof buf, remob->name);
if (cmd) {
- RemTransact(remserver, nChan, pCon, cmd, ">", NULL);
+ /* filter out time stamps !=== */
+ RemTransact(remserver, nChan, pCon, cmd, "!===", ">", NULL);
if (cmd != buf)
free(cmd);
}
diff --git a/remoteobject.c b/remoteobject.c
index 5cad0a91..0e78a3a4 100644
--- a/remoteobject.c
+++ b/remoteobject.c
@@ -2,7 +2,7 @@
* Remote objects in sicsobj. This means accessing remote objects in a different
* SICS server from a master SICS server.
*
- * Reading is implementd according to this scheme:
+ * Reading is implemented according to this scheme:
*
* * When a read connection is made between a local node and a remote node in slave, then a
* callback is installed on remote node in slave.
@@ -17,7 +17,7 @@
*
* COPRYRIGHT: see file COPYRIGHT
*
- * Mark Koennecke, February 2015
+ * Mark Koennecke, February-May 2015
**/
#include
#include
@@ -31,22 +31,29 @@
#include
#include
#include
+#include
#define OOM -5001 /* out of memory */
#define TO -5002 /* timeout */
+#define READACT 7654
+#define POCHACT 8437
+
static char *login = {"RemoteMaster 3ed4c656a15f0aa45e02fd5ec429225bb93b762e7eb06cc81a0b4f6c35c76184\r\n"};
extern char *trim(char *txt);
+
+static int transactionID = 100000;
/*---------------------- our very private data structure -------------------*/
typedef struct {
char *host;
int port;
- int readHandle;
- int writeHandle;
- int writeInUse;
+ int handle;
+ int transactHandle;
int readList;
+ int writeList;
unsigned int connected;
time_t nextHeartbeat;
+ struct json_tokener *jtok;
} RemoteOBJ, *pRemoteOBJ;
/*----------------------------------------------------------------------------*/
typedef struct {
@@ -59,6 +66,12 @@ typedef struct {
char *remotePath;
} UpdateCallback, *pUpdateCallback;
/*----------------------------------------------------------------------------*/
+typedef struct {
+ int transID;
+ SConnection *pCon;
+ int waitTask;
+}writeData, *pWriteData;
+/*----------------------------------------------------------------------------*/
void KillRemoteOBJ(void *data)
{
char roTaskName[132];
@@ -68,66 +81,13 @@ void KillRemoteOBJ(void *data)
snprintf(roTaskName,sizeof(roTaskName),"ro-%s-%d", self->host, self->port);
StopTask(pServ->pTasker,roTaskName);
free(self->host);
- ANETclose(self->readHandle);
- ANETclose(self->writeHandle);
+ ANETclose(self->handle);
+ ANETclose(self->transactHandle);
LLDdeleteBlob(self->readList);
+ LLDdeleteBlob(self->writeList);
+ json_tokener_free(self->jtok);
}
}
-/*========================= reading related code ================================*/
-static int RemoteReadCallback(int handle, void *userData)
-{
- int length;
- char *pPtr, *pStart, *pEnd;
-
- pPtr = ANETreadPtr(handle,&length);
-
- /*
- * deal with command results
- */
- pStart = strstr(pPtr, "TRANSACTIONSTART");
- pEnd = strstr(pPtr,"TRANSACTIONEND");
- if(pStart != NULL && pEnd != NULL){
- pStart = pStart + strlen("TRANSACTIONSTART");
- *pEnd = '\0';
- traceIO("RO","Received command - reply: %s", pStart);
- pEnd += strlen("TRANSACTIONEND");
- ANETreadConsume(handle,pEnd - pPtr);
- }
-
- /*
- * deal with update messages
- */
- pStart = strstr(pPtr, "SROC:");
- pEnd = strstr(pPtr,":EROC\r\n");
- if(pStart != NULL && pEnd != NULL){
- pStart += strlen("SROC:");
- *pEnd = '\0';
- InterpExecute(pServ->pSics, pServ->dummyCon,pStart);
- traceIO("RO", "Received %s from remote", pStart);
- pEnd += strlen("EROC\r\n");
- ANETreadConsume(handle,pEnd - pPtr);
- }
-
- /*
- * deal with heartbeats
- */
- if((pStart = strstr(pPtr,"Poch")) != NULL){
- ANETreadConsume(handle,(pStart+4) - pPtr);
- }
-
- /*
- If there is more stuff to process: recurse
- */
- pPtr = ANETreadPtr(handle,&length);
- if(length > 0 &&
- ( strstr(pPtr,":EROC\r\n") != NULL ||
- strstr(pPtr,"TRANSACTIONEND") != NULL
- || strstr(pPtr,"Poch") != NULL ) ) {
- RemoteReadCallback(handle,userData);
- }
-
- return 1;
-}
/*-----------------------------------------------------------------------------*/
static int transactCommand(int handle, char *command, char *reply, int replyLen)
{
@@ -135,7 +95,7 @@ static int transactCommand(int handle, char *command, char *reply, int replyLen)
char *prefix = {"transact "};
int status, length, type;
time_t start;
- char *pPtr;
+ char *pPtr, *pEnd;
/*
* read possible dirt of the line
@@ -144,12 +104,15 @@ static int transactCommand(int handle, char *command, char *reply, int replyLen)
ANETreadConsume(handle,length);
- toSend = malloc(strlen(command) + strlen(prefix) + 1);
+ toSend = malloc(strlen(command) + strlen(prefix) + 10);
if(toSend == NULL){
return OOM;
}
strcpy(toSend, prefix);
strcat(toSend, command);
+ if(strstr(command,"\n") == NULL){
+ strcat(toSend,"\r\n");
+ }
status = ANETwrite(handle,toSend,strlen(toSend));
free(toSend);
if(status != 1){
@@ -163,7 +126,8 @@ static int transactCommand(int handle, char *command, char *reply, int replyLen)
while(time(NULL) < start + 2.0){
ANETprocess();
pPtr = ANETreadPtr(handle,&length);
- if(length > 0 && strstr(pPtr,"TRANSACTIONFINISHED") != NULL){
+ if(length > 0 && (pEnd = strstr(pPtr,"TRANSACTIONFINISHED")) != NULL){
+ *pEnd = '\0';
strncpy(reply,pPtr,replyLen);
ANETreadConsume(handle,length);
return 1;
@@ -191,9 +155,9 @@ static void ConnectRemoteObject(pRemoteOBJ self)
return;
}
- self->readHandle = ANETconnect(self->host, self->port);
- self->writeHandle = ANETconnect(self->host, self->port);
- if(self->readHandle < 0 || self->writeHandle < 0){
+ self->handle = ANETconnect(self->host, self->port);
+ self->transactHandle = ANETconnect(self->host, self->port);
+ if(self->handle < 0 || self->transactHandle < 0){
self->connected = 0;
traceIO("RO","Failed to connect to remote objects at %s, port %d",
self->host, self->port);
@@ -206,23 +170,20 @@ static void ConnectRemoteObject(pRemoteOBJ self)
Default login with hard coded manager login. Defined in
nserver.c
*/
- ANETwrite(self->readHandle,login,strlen(login));
- ANETwrite(self->writeHandle,login,strlen(login));
+ ANETwrite(self->handle,login,strlen(login));
+ ANETwrite(self->transactHandle,login,strlen(login));
usleep(500);
ANETprocess();
/*
eat the login responses
*/
- pPtr = ANETreadPtr(self->readHandle, &length);
- ANETreadConsume(self->readHandle,length);
- pPtr = ANETreadPtr(self->writeHandle, &length);
- ANETreadConsume(self->writeHandle,length);
+ pPtr = ANETreadPtr(self->handle, &length);
+ ANETreadConsume(self->handle,length);
+ pPtr = ANETreadPtr(self->transactHandle, &length);
+ ANETreadConsume(self->transactHandle,length);
+ transactCommand(self->handle,"protocol set json\r\n", command,sizeof(command)-1);
- /*
- * install the read callback
- */
- ANETsetReadCallback(self->readHandle,RemoteReadCallback, NULL, NULL);
/*
* Remove geterror on read nodes and reinstall callbacks for reconnects
@@ -233,17 +194,15 @@ static void ConnectRemoteObject(pRemoteOBJ self)
node = FindHdbNode(NULL,rd.localNode,NULL);
if(node != NULL){
SetHdbProperty(node,"geterror",NULL);
- snprintf(command,sizeof(command),"fulltransact addremotecb %s %s \r\n",
- rd.remoteNode, rd.localNode);
- ANETwrite(self->readHandle,command,strlen(command));
+ snprintf(command,sizeof(command),"contextdo %d addremotecb %s %s \r\n",
+ READACT, rd.remoteNode, rd.localNode);
+ ANETwrite(self->handle,command,strlen(command));
}
status = LLDnodePtr2Next(self->readList);
}
- transactCommand(self->writeHandle,"protocol set withcode\r\n", command,sizeof(command));
-
+
self->connected = 1;
- self->writeInUse = 0;
}
/*-----------------------------------------------------------------------------*/
static void MarkDisconnected(pRemoteOBJ self)
@@ -270,8 +229,8 @@ static hdbCallbackReturn ROUpdateCallback(pHdb currentNode, void *userData,
pUpdateCallback uppi = (pUpdateCallback)userData;
hdbDataMessage *mm = NULL;
pDynString text;
- char *prefix = {"SROC:hupdate "};
- char *postfix= {":EROC\r\n"};
+ char *prefix = {"hupdate "};
+ char *postfix= {" \r\n"};
char *txt = NULL;
int length;
pHdbPropertyChange propChange = NULL;
@@ -309,7 +268,7 @@ static hdbCallbackReturn ROUpdateCallback(pHdb currentNode, void *userData,
if(!SCisConnected(uppi->sendCon)){
return hdbKill;
}
- length = strlen("SROC:hdelprop ") + strlen(uppi->remotePath) +
+ length = strlen("hdelprop ") + strlen(uppi->remotePath) +
strlen(propChange->key) + 10;
if(propChange->value != NULL){
length += strlen(propChange->value);
@@ -319,10 +278,10 @@ static hdbCallbackReturn ROUpdateCallback(pHdb currentNode, void *userData,
return hdbContinue;
}
if(propChange->value == NULL){
- snprintf(txt,length,"SROC:hdelprop %s %s %s", uppi->remotePath,
+ snprintf(txt,length,"hdelprop %s %s %s", uppi->remotePath,
propChange->key,postfix);
} else {
- snprintf(txt,length,"SROC:hsetprop %s %s %s %s", uppi->remotePath,
+ snprintf(txt,length,"hsetprop %s %s %s %s", uppi->remotePath,
propChange->key,propChange->value, postfix);
}
SCWrite(uppi->sendCon,txt,eValue);
@@ -397,7 +356,7 @@ static int ConnectRead(pRemoteOBJ self, SConnection * pCon, ReadData rd)
* Get information about the remote node and check compatability
*/
snprintf(command,sizeof(command),"hinfo %s\r\n", rd.remoteNode);
- status = transactCommand(self->writeHandle,command,reply,sizeof(reply));
+ status = transactCommand(self->transactHandle,command,reply,sizeof(reply));
if(status != 1){
/*
* try a reconnect,
@@ -407,7 +366,7 @@ static int ConnectRead(pRemoteOBJ self, SConnection * pCon, ReadData rd)
*/
self->connected = 0;
ConnectRemoteObject(self);
- status = transactCommand(self->writeHandle,command,reply,sizeof(reply));
+ status = transactCommand(self->transactHandle,command,reply,sizeof(reply));
if(status != 1){
SCPrintf(pCon,eWarning,"WARNING: cannot yet reach slave %s, but continuing...",
self->host);
@@ -448,9 +407,9 @@ static int ConnectRead(pRemoteOBJ self, SConnection * pCon, ReadData rd)
* Install a callback on the remote node to update the master. The remote should
* then immediatly send an update which will be processed by the read callback.
*/
- snprintf(command,sizeof(command),"fulltransact addremotecb %s %s \r\n",
- rd.remoteNode, rd.localNode);
- ANETwrite(self->readHandle,command,strlen(command));
+ snprintf(command,sizeof(command),"contextdo %d addremotecb %s %s \r\n",
+ READACT, rd.remoteNode, rd.localNode);
+ ANETwrite(self->handle,command,strlen(command));
return 1;
}
@@ -492,10 +451,10 @@ static int HeartbeatTask(void *pData)
{
pRemoteOBJ self = (pRemoteOBJ)pData;
int status;
- char command[] = {"Poch\r\n"};
+ char command[] = {"contextdo 8437 Poch\r\n"};
if (time(NULL) > self->nextHeartbeat){
- status = ANETwrite(self->readHandle,command, strlen(command));
+ status = ANETwrite(self->handle,command, strlen(command));
if(status != 1){
traceIO("RO","Trying a reconnect to %s, %d", self->host, self->port);
self->connected = 0;
@@ -509,11 +468,13 @@ static int HeartbeatTask(void *pData)
return 1;
}
/*============================= writing related code ===========================
- The logic here is to use the standard writeHandle when available. I expect most
- communication to be short and to happen through the writeHandle. If that one is
- in use, a new connection will be built.
- ---------------------------------------------------------------------------------
- suppress all superfluous OK from the slave
+ This works by sending the command via contextdo with a ID > 10^6. This causes
+ the remote SICS to send the termination messages. The transaction IDs together
+ with the connection responsible for it are kept in a list.
+
+ This list is used by the write task to forward messages properly and for handling
+ termination.
+
-----------------------------------------------------------------------------------*/
#include
static OutCode findOutCode(char *txt)
@@ -527,93 +488,153 @@ static OutCode findOutCode(char *txt)
}
return eValue;
}
-/*--------------------------------------------------------------------------------*/
-static void printSICS(char *answer, SConnection *pCon)
+/*-----------------------------------------------------------------------------------*/
+static void CheckWriteList(int writeList,int transID, OutCode eOut, char *pText)
{
- char line[1024], *pPtr, *pCode;
- OutCode eCode;
+ int status;
+ writeData WD;
- pPtr = answer;
- while(pPtr != NULL){
- memset(line,0,sizeof(line));
- pPtr = stptok(pPtr,line,sizeof(line),"\n");
- if(strstr(line,"OK") == NULL){
- pCode = strstr(line,"@@");
- if(pCode != NULL){
- *pCode = '\0';
- pCode += 2;
- eCode = findOutCode(trim(pCode));
-
+ status = LLDnodePtr2First(writeList);
+ while(status == 1){
+ LLDblobData(writeList,&WD);
+ if(WD.transID == transID){
+ if(strstr(pText,"COMSTART") != NULL){
+ /* skip */
+ } else if(strstr(pText,"COMEND") != NULL && WD.waitTask == 0) {
+ SCDeleteConnection(WD.pCon);
+ LLDblobDelete(writeList);
+ return;
+ } else if(strstr(pText,"COMEND") != NULL && WD.waitTask == 1) {
+ /* skip */
+ return;
+ } else if(strstr(pText,"TASKSTART") != NULL){
+ WD.waitTask = 1 ;
+ LLDblobDelete(writeList);
+ LLDblobAppend(writeList,&WD, sizeof(writeData));
+ return;
+ } else if(strstr(pText,"TASKEND") != NULL && WD.waitTask == 1){
+ SCDeleteConnection(WD.pCon);
+ LLDblobDelete(writeList);
+ return;
} else {
- eCode = eValue;
+ if(strstr(pText,"OK") == NULL){
+ SCWrite(WD.pCon,pText,eOut);
+ }
+ return;
}
- SCWrite(pCon,line,eCode);
}
+ status = LLDnodePtr2Next(writeList);
}
-}
-/*---------------------------------------------------------------------------------*/
-static int PrepareWriteHandle(pRemoteOBJ self, SConnection *pCon, int *newHandle)
+ }
+/*-----------------------------------------------------------------------------------*/
+static int WriteResponseTask(void *pData)
{
- int handle, length;
- char *answer = NULL;
- char command[80];
+ pRemoteOBJ self = (pRemoteOBJ)pData;
+ int status, length = 0, transID;
+ char *pText, *outTxt;
+ json_object *message = NULL, *data = NULL;
+ enum json_tokener_error tokerr;
+ OutCode eOut;
+ writeData WD;
- if(self->writeInUse) {
- handle = ANETconnect(self->host,self->port);
- if(handle < 0){
- traceIO("RO","Failed to connect to %s at %d", self->host, self->port);
- if(pCon != NULL){
- SCPrintf(pCon,eError,"ERROR: Failed to connect to %s %d", self->host, self->port);
- }
- return handle;
- }
- ANETwrite(handle,login,strlen(login));
- usleep(500);
- ANETprocess();
- /*
- eat the login responses
- */
- answer = ANETreadPtr(handle, &length);
- ANETreadConsume(handle,length);
- *newHandle = 1;
-
- transactCommand(handle,"protocol set withcode\r\n", command,sizeof(command));
-
- } else {
- self->writeInUse = 1;
- handle = self->writeHandle;
- /*
- eat dirt from the line
- */
- answer = ANETreadPtr(handle, &length);
- ANETreadConsume(handle,length);
+ if(!ANETvalidHandle(self->handle)) {
+ return 1;
}
- return handle;
+
+ pText = ANETreadPtr(self->handle,&length);
+ while(length > 0){
+ json_tokener_reset(self->jtok);
+ message = json_tokener_parse_ex(self->jtok,pText,length);
+ tokerr = self->jtok->err;
+ if(tokerr == json_tokener_continue){
+ return 1;
+ } else if(tokerr != json_tokener_success) {
+ traceIO("RO","JSON parsing error %s on %s from %s %d",
+ json_tokener_errors[tokerr], pText, self->host, self->jtok->char_offset);
+ ANETreadConsume(self->handle,length);
+ return 1;
+ }
+ if(json_object_get_type(message) != json_type_object) {
+ traceIO("RO","Received JSON of bad type in %s from %s",pText,self->host);
+ ANETreadConsume(self->handle,length);
+ return 1;
+ }
+ /*
+ we need to consume here what has been parsed.
+ The char_offset in the tokenizer structure might tell us that...
+ */
+ ANETreadConsume(self->handle,self->jtok->char_offset);
+
+
+ /*
+ Received a valid message, process
+ */
+ data = json_object_object_get(message,"trans");
+ if(data == NULL){
+ traceIO("RO","No transaction ID found in %s from %s", pText,self->host);
+ return 1;
+ }
+ transID = json_object_get_int(data);
+
+ data = json_object_object_get(message,"flag");
+ if(data == NULL){
+ traceIO("RO","No flag found in %s from %s", pText,self->host);
+ return 1;
+ }
+ outTxt = (char *)json_object_get_string(data);
+ eOut = findOutCode(outTxt);
+
+ data = json_object_object_get(message,"data");
+ if(data == NULL){
+ traceIO("RO","No data found in %s from %s", pText,self->host);
+ return 1;
+ }
+ pText = (char *)json_object_get_string(data);
+
+ traceIO("RO","Received:%s:%d:%d:%s",self->host,transID,eOut,pText);
+
+ /*
+ do nothing on Poch
+ */
+ if(transID == POCHACT){
+ pText = ANETreadPtr(self->handle,&length);
+ json_object_put(message);
+ continue;
+ }
+
+ /*
+ process update messages
+ */
+ if(transID == READACT){
+ if(strstr(pText,"hupdate") != NULL || strstr(pText,"prop") != NULL){
+ InterpExecute(pServ->pSics,pServ->dummyCon,pText);
+ }
+ traceIO("RO","Received %s from remote",pText);
+ pText = ANETreadPtr(self->handle,&length);
+ json_object_put(message);
+ continue;
+ }
+
+ /*
+ check write List
+ */
+ CheckWriteList(self->writeList,transID,eOut,pText);
+ json_object_put(message);
+
+ pText = ANETreadPtr(self->handle,&length);
+ }
+
+ return 1;
}
+
/*---------------------------------------------------------------------------------*/
-static void ProcessWriteResponse(pRemoteOBJ self, int handle, SConnection *pCon)
+static int IncrementTransactionID()
{
- char *answer = NULL, *pEnd, *command = NULL;
- int length;
-
- while(1){
- TaskYield(pServ->pTasker);
- if(!ANETvalidHandle(handle)){
- SCPrintf(pCon,eError,"ERROR: Disconnected from %s", self->host);
- break;
- }
- answer = ANETreadPtr(handle,&length);
- if(length > 0 && (pEnd = strstr(answer,"TRANSACTIONFINISHED")) != NULL){
- if(pCon != NULL){
- *pEnd = '\0';
- printSICS(answer,pCon);
- }
- traceIO("RO","%s:%d: Received %s", self->host, self->port,answer);
- ANETreadConsume(handle,length);
- break;
- }
+ transactionID++;
+ if(transactionID >= 200000){
+ transactionID = 100000;
}
-
+ return transactionID;
}
/*---------------------------------------------------------------------------------*/
static hdbCallbackReturn ROWriteCallback(pHdb currentNode, void *userData,
@@ -626,14 +647,10 @@ static hdbCallbackReturn ROWriteCallback(pHdb currentNode, void *userData,
pDynString data;
char *remoteNode;
char *command, *answer, *pEnd;
-
+ writeData WD;
if((mm = GetHdbSetMessage(mes)) != NULL){
pCon = (SConnection *)mm->callData;
- handle = PrepareWriteHandle(self,pCon,&newHandle);
- if(handle < 0){
- return hdbAbort;
- }
/*
build the command to send
@@ -648,53 +665,32 @@ static hdbCallbackReturn ROWriteCallback(pHdb currentNode, void *userData,
}
return hdbAbort;
}
- snprintf(command,length,"transact hset %s %s\r\n",remoteNode, GetCharArray(data));
+ WD.pCon = SCCopyConnection(pCon);
+ WD.waitTask = 0;
+ WD.transID = IncrementTransactionID();
+ snprintf(command,length,"contextdo %d hset %s %s\r\n",
+ WD.transID, remoteNode, GetCharArray(data));
/*
write
*/
traceIO("RO","%s:%d: Sending %s ", self->host, self->port, command);
- status = ANETwrite(handle,command,strlen(command));
+ LLDblobAppend(self->writeList,&WD,sizeof(writeData));
+ status = ANETwrite(self->handle,command,strlen(command));
free(command);
DeleteDynString(data);
if(status < 0){
- if(pCon != NULL){
- SCPrintf(pCon,eError,"ERROR: remote %s on %s disconnected", remoteNode, self->host);
- }
- return hdbAbort;
- }
-
- /*
- wait for a response: TRANSACTIONFINISHED
- */
- ProcessWriteResponse(self,handle,pCon);
-
- /*
- Is there a termination script?
- */
- command = GetHdbProp(currentNode,"termscript");
- if(command != NULL){
- while(1) {
- TaskYield(pServ->pTasker);
- Tcl_Eval(InterpGetTcl(pServ->pSics),command);
- answer = (char *)Tcl_GetStringResult(InterpGetTcl(pServ->pSics));
- if(strstr(answer,"idle") != NULL){
- answer = ANETreadPtr(handle,&length);
- printSICS(answer,pCon);
- traceIO("RO","%s:%d:Received %s", self->host,self->port,answer);
- ANETreadConsume(handle,length);
- break;
- }
+ self->connected = 0;
+ ConnectRemoteObject(self);
+ if(self->connected == 0){
+ if(pCon != NULL){
+ SCPrintf(pCon,eError,"ERROR: remote %s on %s disconnected",
+ remoteNode, self->host);
+ }
+ return hdbAbort;
}
}
-
-
- if(newHandle){
- ANETclose(handle);
- } else {
- self->writeInUse = 0;
- }
-
+ return hdbContinue;
}
return hdbContinue;
@@ -722,12 +718,17 @@ static int ConnectWrite(pRemoteOBJ self, SConnection *pCon, ReadData rd)
SetHdbProperty(localNode,"remotewrite",rd.remoteNode);
AppendHipadabaCallback(localNode, MakeHipadabaCallback(ROWriteCallback,
self,NULL));
+ /*
+ TODO: The connected write nodes should be held in a list in order to be able to
+ remove the write callbacks when deleting the remote object. As removing remote
+ objects usually only happens when SICS shuts down this is not so important.
+ */
/*
* Get information about the remote node and check compatability
*/
snprintf(command,sizeof(command),"hinfo %s\r\n", rd.remoteNode);
- status = transactCommand(self->writeHandle,command,reply,sizeof(reply));
+ status = transactCommand(self->transactHandle,command,reply,sizeof(reply));
if(status != 1){
SCPrintf(pCon,eWarning,"WARNING: cannot yet reach slave %s, but continuing...",
self->host);
@@ -784,61 +785,21 @@ static int ConnectwriteCmd(pSICSOBJ ccmd, SConnection * pCon,
/*============================ remote execute =================================*/
static int RemoteExecute(pRemoteOBJ self, SConnection *pCon, char *command)
{
- int status, handle, newHandle = 0, length;
- char *answer, *pEnd;
-
- handle = PrepareWriteHandle(self,pCon,&newHandle);
- if(handle < 0){
- return 0;
- }
+ int status;
+ char answer[65536];
/*
write, thereby taking care to prefix with transact and for proper termination
*/
- if(strstr(command,"transact") == NULL){
- ANETwrite(handle,"transact ", sizeof("transact "));
- }
- status = ANETwrite(handle,command,strlen(command));
- if(strstr(command,"\n") == NULL){
- ANETwrite(handle,"\r\n",2);
- }
- if(status < 0){
- traceIO("RO","Disconnect from %s while executing %s", self->host, command);
- if(pCon != NULL){
- SCPrintf(pCon,eError,"ERROR: Disconnected from %s %d", self->host, self->port);
- }
- return 0;
- }
-
- /*
- wait for response
- */
- while(1){
- TaskYield(pServ->pTasker);
- if(!ANETvalidHandle(handle)){
- if(pCon != NULL){
- SCPrintf(pCon,eError,"ERROR: Disconnected from %s %d", self->host, self->port);
- }
- break;
- }
- answer = ANETreadPtr(handle,&length);
- if(length > 0 && (pEnd = strstr(answer,"TRANSACTIONFINISHED")) != NULL){
- if(pCon != NULL){
- *pEnd = '\0';
- SCPrintf(pCon,eValue,answer);
- }
- ANETreadConsume(handle,length);
- break;
- }
- }
-
- if(newHandle){
- ANETclose(handle);
+ memset(answer,0,sizeof(answer)-1);
+ status = transactCommand(self->transactHandle,command,answer,sizeof(answer));
+ if(status == 1){
+ SCWrite(pCon,answer,eValue);
} else {
- self->writeInUse = 0;
+ SCPrintf(pCon,eError,"ERROR: Disconnected from %s %d", self->host, self->port);
}
- return 1;
+ return status;
}
/*------------------------------------------------------------------------------*/
static int RemoteExecuteCmd(pSICSOBJ ccmd, SConnection * pCon,
@@ -920,6 +881,8 @@ static int MakeRemoteObject(SConnection *pCon, SicsInterp *pSics, void *pData,
self->host = strdup(argv[2]);
self->port = atoi(argv[3]);
self->readList = LLDblobCreate();
+ self->writeList = LLDblobCreate();
+ self->jtok = json_tokener_new();
ConnectRemoteObject(self);
cmd = AddSICSHdbPar(pNew->objectNode,
@@ -949,6 +912,8 @@ static int MakeRemoteObject(SConnection *pCon, SicsInterp *pSics, void *pData,
snprintf(roTaskName,sizeof(roTaskName),"ro-%s-%d", self->host, self->port);
TaskRegisterN(pServ->pTasker, roTaskName, HeartbeatTask, NULL,NULL,self,1);
+ snprintf(roTaskName,sizeof(roTaskName),"rocom-%s-%d", self->host, self->port);
+ TaskRegisterN(pServ->pTasker, roTaskName, WriteResponseTask, NULL,NULL,self,1);
return status;
}
diff --git a/sansbc.c b/sansbc.c
index 69cdcfa7..05b710ea 100644
--- a/sansbc.c
+++ b/sansbc.c
@@ -201,7 +201,7 @@ static int StatCmd(pSICSOBJ self, SConnection *pCon, pHdb commandNode,
}
average = (double)sum/(double)length;
- SCPrintf(pCon,eValue,"Stat:sum,max,min,av = %d,%d,%d,%f", sum,max,min,average);
+ SCPrintf(pCon,eValue,"Stat:sum,max,min,av = %ld,%ld,%ld,%f", sum,max,min,average);
return 1;
}
diff --git a/scan.c b/scan.c
index 87449183..4e989264 100644
--- a/scan.c
+++ b/scan.c
@@ -152,6 +152,8 @@ pScanData CreateScanObject(char *pRecover, char *pHeader, pCounter pCount,
/* assign various things */
if (pRecover) {
strlcpy(pNew->pRecover, pRecover,1024);
+ } else {
+ memset(pNew->pRecover,0,sizeof(pNew->pRecover));
}
if (pHeader) {
strlcpy(pNew->pHeaderFile, pHeader,1024);
@@ -597,7 +599,7 @@ static int ScanLoop(pScanData self)
InvokeCallBack(self->pCall, SCANPOINT, self);
self->WriteScanPoints(self, i);
- if (self->pRecover) {
+ if (strlen(self->pRecover) > 10) {
WriteRecover(self);
}
}
diff --git a/scriptcontext.c b/scriptcontext.c
index faa347c4..2a803c7b 100644
--- a/scriptcontext.c
+++ b/scriptcontext.c
@@ -404,6 +404,7 @@ static char *SctActionHandler(void *actionData, char *lastReply,
char *errorScript = NULL;
char *send = NULL;
int i;
+ int j;
SConnection *con;
char eprop[80];
char msg[1024];
@@ -431,23 +432,24 @@ static char *SctActionHandler(void *actionData, char *lastReply,
* property result to the data from the device. Read this now and
* print it if diagnostics is required.
*/
- SetProp(node, controller->node, "result", lastReply);
script = NULL;
- if (!commError && controller->verbose && lastReply != NULL
- && *lastReply != '\0') {
- SCPrintf(con, eLog, "%6.3f reply : %s\n", secondsOfMinute(), lastReply);
+ if (lastReply != NULL) {
+ SetProp(node, controller->node, "result", lastReply);
+ if (*lastReply != '\0') {
+ if (!commError && controller->verbose) {
+ SCPrintf(con, eLog, "%6.3f reply : %s\n", secondsOfMinute(), lastReply);
+ }
+ if(!commError && controller->fd != NULL) {
+ fprintf(controller->fd, "%6.3f reply : %s\n", secondsOfMinute(), lastReply);
+ }
+ if(data != NULL && data->controller != NULL) {
+ traceIO(data->controller->node->name, "reply:%s", lastReply);
+ } else {
+ traceIO("sctunknown", "reply:%s", lastReply);
+ }
+ }
}
- if(!commError && controller->fd != NULL && lastReply != NULL && *lastReply != '\0'){
- fprintf(controller->fd, "%6.3f reply : %s\n", secondsOfMinute(), lastReply);
- }
- if(lastReply != NULL && *lastReply != '\0'){
- if(data != NULL && data->controller != NULL){
- traceIO(data->controller->node->name, "reply:%s", lastReply);
- } else {
- traceIO("sctunknown", "reply:%s", lastReply);
- }
- }
-
+
/*
* Make sure that the state property is set to the name of the property
* which holds the name of the script to run at this stage.
@@ -521,7 +523,7 @@ static char *SctActionHandler(void *actionData, char *lastReply,
traceIO(data->controller->node->name, "ERROR: action {%s} in {%s} node %s:\nERROR: %s",
data->name, origScript, path, result);
} else {
- traceIO("sctunknown", "reply:%s", "ERROR: action {%s} in {%s} node %s:\nERROR: %s",
+ traceIO("sctunknown", "ERROR: action {%s} in {%s} node %s:\nERROR: %s",
data->name, origScript, path, result);
}
}
@@ -529,6 +531,21 @@ static char *SctActionHandler(void *actionData, char *lastReply,
if (strcasecmp(data->name, "read") == 0) {
SetHdbProperty(node, "geterror", result);
}
+ /* Sanitize the text to reduce TCL problems with unbalanced and substituted items */
+ for (j = 0; msg[j]; ++j) {
+ switch (msg[j]) {
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case '<':
+ case '>':
+ case '\'':
+ case '"':
+ case '$':
+ msg[j] = '_';
+ }
+ }
SetHdbProperty(node, eprop, msg);
blank = strchr(origScript, ' ');
if (blank != NULL) {
@@ -544,6 +561,21 @@ static char *SctActionHandler(void *actionData, char *lastReply,
}
cnt++;
snprintf(msg, sizeof msg, "%dx {%s}: %s", cnt, origScript, result);
+ /* Sanitize the text to reduce TCL problems with unbalanced and substituted items */
+ for (j = 0; msg[j]; ++j) {
+ switch (msg[j]) {
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case '<':
+ case '>':
+ case '\'':
+ case '"':
+ case '$':
+ msg[j] = '_';
+ }
+ }
SetHdbProperty(node, eprop, msg);
send = NULL;
free(script);
@@ -951,12 +983,15 @@ int SctAddPollNode(SctController * controller, Hdb * node, double interval,
{
SctData *data;
hdbCallback *cb;
-
+ char nodePath[512], info[1024];
+
if (!FindHdbCallbackData(node, controller)) {
cb = MakeHipadabaCallback(SctMainCallback, controller, NULL);
assert(cb);
AppendHipadabaCallback(node, cb);
- SetHdbProperty(node, "geterror", "Not read yet");
+ GetHdbPath(node, nodePath, sizeof nodePath);
+ snprintf(info, 1023, "%s: Not read yet", nodePath);
+ SetHdbProperty(node, "geterror", info);
}
data = calloc(1, sizeof(*data));
@@ -1585,8 +1620,19 @@ static int SctProcessCmd(pSICSOBJ ccmd, SConnection * con,
startTime = time(NULL);
DevQueue(c->devser, data, WritePRIO,
SctWriteHandler, SctTransactMatch, NULL, SctDataInfo);
- while (data->busy == 1 && time(NULL) < startTime + 20) {
+
+ while (data->busy == 1){
TaskYield(pServ->pTasker);
+ if(time(NULL) >= startTime + 20) {
+ /*
+ if data would still come after such a long timeout, it
+ might end up in the next action: see comment in devser.c
+ */
+ SCPrintf(con,eError,"ERROR: timeout processing node %s", par[0]->value.v.text);
+ DevRemoveAction(c->devser,data);
+ SctKillData(data);
+ return 0;
+ }
}
SctKillData(data);
diff --git a/sctcomtask.c b/sctcomtask.c
index df83bd27..74deec1c 100644
--- a/sctcomtask.c
+++ b/sctcomtask.c
@@ -203,7 +203,7 @@ static int StartComTaskCmd(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
char *priority, *sendData;
- int lID;
+ long lID;
ComTaskManager *manni = NULL;
if(nPar < 2) {
@@ -294,7 +294,7 @@ const char *GetComTaskReply(ComTaskManager *manager,
static int GetComTaskReplyCmd(pSICSOBJ ccmd, SConnection * con,
Hdb * cmdNode, Hdb * par[], int nPar)
{
- int lID;
+ long lID;
int length;
ComTaskManager *manni = NULL;
const char *reply;
@@ -311,7 +311,7 @@ static int GetComTaskReplyCmd(pSICSOBJ ccmd, SConnection * con,
SCPrintf(con,eError,"No reply for %ld found", lID);
return 0;
} else {
- SCPrintf(con,eValue,"%d = %s", lID, reply);
+ SCPrintf(con,eValue,"%ld = %s", lID, reply);
}
return 1;
diff --git a/servlog.c b/servlog.c
index 29faad49..90ab59a8 100644
--- a/servlog.c
+++ b/servlog.c
@@ -47,16 +47,18 @@
- NETWrites log message (if enabled) before attempt to write to file
- uses OpenVerifyLogFile helper function (removed duplicate code)
-----------------------------------------------------------------------------*/
+#include
#include "fortify.h"
#include
#include
#include
#include
#include
+#include
+#include
#include
#include "ifile.h"
-#include "conman.h"
#include "servlog.h"
#include "network.h"
@@ -69,24 +71,142 @@
this the following code is necessary.
*/
typedef struct __LogLog {
- SConnection *pCon;
- OutCode iOut;
- int iAllFlag;
+ pSICSLogHook pFunc;
+ void *pData;
+ unsigned int code_bits;
struct __LogLog *pNext;
struct __LogLog *pPrevious;
} CaptureEntry, *pCaptureEntry;
static pCaptureEntry pCapture = NULL;
/*------------------------------------------------------------------------*/
+#include "outcode.c" /* for pCode */
-int KillCapture(SConnection * pCon)
+const char* OutCodeToTxt(OutCode eOut)
+{
+ switch (eOut) {
+ case eInternal: return "Int"; /* internal */
+ case eCommand: return "Cmd"; /* reserved, not used */
+ case eHWError: return "ErH"; /* reserved, used only for SICSLog */
+ case eInError: return "ErI"; /* reserved, used as a mark in the handling of output codes */
+ case eStatus: return "Sta"; /* reserved, deprecated */
+ case eValue: return "Val"; /* value reponse: copied into Tcl */
+ case eStart: return "Beg"; /* start message */
+ case eFinish: return "End"; /* finish message */
+ case eEvent: return "Evt"; /* some callback messages */
+ case eWarning: return "Wrn"; /* warnings */
+ case eError: return "Err"; /* error: copied into Tcl */
+ case eHdbValue: return "HVa"; /* hipadaba value chnage */
+ case eHdbEvent: return "HEv"; /* Hipadaba event */
+ case eLog: return "Log"; /* log message: is always written to client */
+ case eLogError: return "ErL"; /* error message to log: is always written to client */
+ }
+ return "???";
+}
+
+const char* OutCodeToText(OutCode eOut)
+{
+ switch (eOut) {
+ case eInternal: return "Internal";
+ case eCommand: return "Command";
+ case eHWError: return "HWError";
+ case eInError: return "InError";
+ case eStatus: return "Status";
+ case eValue: return "Value";
+ case eStart: return "Start";
+ case eFinish: return "Finish";
+ case eEvent: return "Event";
+ case eWarning: return "Warning";
+ case eError: return "Error";
+ case eHdbValue: return "HdbValue";
+ case eHdbEvent: return "HdbEvent";
+ case eLog: return "Log";
+ case eLogError: return "LogError";
+ }
+ return "Unknown";
+}
+
+int OutCodeFromText(const char *text, OutCode *outcode)
+{
+ int i;
+ for (i = 0; i < iNoCodes; ++i) {
+ if (strcasecmp(text, pCode[i]) == 0) {
+ if (outcode)
+ *outcode = i;
+ return i;
+ }
+ }
+ return -1;
+}
+
+static unsigned int find_code_bits(const char *p1, const char *p2) {
+ /* may be outcode, try find it */
+ int i;
+ const char *pShort;
+ const char *pLong;
+ size_t len = p2 - p1;
+ if (len == 3 && strncasecmp(p1, "all", 3) == 0)
+ return (1 << iNoCodes) - 1;
+ for (i = 0; i < iNoCodes; ++i) {
+ pShort = OutCodeToTxt(i);
+ if (pShort && strncasecmp(p1, pShort, len) == 0)
+ return 1 << i;
+ pLong = OutCodeToText(i);
+ if (pLong && strlen(pLong) == len) {
+ if (strncasecmp(p1, pLong, len) == 0) {
+ return 1 << i;
+ }
+ }
+ }
+ return 0;
+}
+
+char *AddSICSLogHook(pSICSLogHook func, const char *pCodes, void *pData)
+{
+ unsigned int code_bits = 0;
+ if (strcasecmp("all", pCodes) == 0)
+ code_bits = (1 << iNoCodes) - 1;
+ else {
+ const char *p1, *p2;
+ p1 = pCodes;
+ while (NULL != (p2 = strchr(p1, ','))) {
+ /* TODO [p1:p2) */
+ code_bits |= find_code_bits(p1, p2);
+ p1 = p2 + 1;
+ }
+ /* p1 points at the last or only code */
+ /* TODO p1 */
+ code_bits |= find_code_bits(p1, p1 + strlen(p1));
+ }
+ if (code_bits != 0) {
+ pCaptureEntry pNew;
+ pNew = (pCaptureEntry) malloc(sizeof(CaptureEntry));
+ if (!pNew) {
+ SICSLogWrite("Out of memory in LogCapture", eInternal);
+ return NULL;
+ }
+ if (pCapture) {
+ pCapture->pPrevious = pNew;
+ }
+ pNew->pPrevious = NULL;
+ pNew->pNext = pCapture;
+ pCapture = pNew;
+ pNew->code_bits = code_bits;
+ pNew->pFunc = func;
+ pNew->pData = pData;
+ }
+ return NULL;
+}
+
+/* Remove any and all hooks with this pData */
+char *RemSICSLogHook(void *pData)
{
pCaptureEntry pCurrent, pTemp;
/* find first */
pCurrent = pCapture;
while (pCurrent != NULL) {
- if (pCon == pCurrent->pCon) {
+ if (pData == pCurrent->pData) {
/* relink */
if (pCurrent->pPrevious) {
pCurrent->pPrevious->pNext = pCurrent->pNext;
@@ -104,88 +224,27 @@ int KillCapture(SConnection * pCon)
pCurrent = pCurrent->pNext;
}
}
- return 1;
+ return NULL;
}
-
-/* ------------------------------------------------------------------------
- the command function:
- Syntax:
- Kill kills all logging
- Log OutCode starts loggin OutCode events
- All starts logging all events
--------------------------------------------------------------------------- */
-#include "outcode.c" /* for pCode */
-
-int LogCapture(SConnection * pCon, SicsInterp * pSics, void *pData,
- int argc, char *argv[])
+/* Return bitmask of any and all hooks with this pData */
+unsigned int GetSICSLogHook(void *pData)
{
- pCaptureEntry pNew = NULL;
- char pBueffel[512];
- int i;
+ pCaptureEntry pCurrent, pTemp;
+ unsigned int code_bits = 0;
- /* check no af args */
- if (argc < 2) {
- snprintf(pBueffel,sizeof(pBueffel)-1, "Insufficient number of argumenst to %s", argv[0]);
- SCWrite(pCon, pBueffel, eError);
- return 0;
+ /* find first */
+ pCurrent = pCapture;
+ while (pCurrent != NULL) {
+ if (pData == pCurrent->pData) {
+ code_bits |= pCurrent->code_bits;
+ }
+ pCurrent = pCurrent->pNext;
}
- argtolower(argc, argv);
-
- /* Branch according to argv[1] */
- if (strcmp(argv[1], "kill") == 0) {
- KillCapture(pCon);
- return 1;
- } else if (strcmp(argv[1], "all") == 0) {
- pNew = (pCaptureEntry) malloc(sizeof(CaptureEntry));
- if (!pNew) {
- SICSLogWrite("Out of memory in LogCapture", eInternal);
- return 0;
- }
- if (pCapture) {
- pCapture->pPrevious = pNew;
- }
- pNew->pPrevious = NULL;
- pNew->pNext = pCapture;
- pCapture = pNew;
- pNew->iAllFlag = 1;
- pNew->pCon = pCon;
- return 1;
- } else {
- /* must be outcode, try find it */
- i = 0;
- while (pCode[i] != NULL) {
- if (strcmp(argv[1], pCode[i]) == 0) {
- break;
- }
- i++;
- }
- if (i > iNoCodes) {
- snprintf(pBueffel,sizeof(pBueffel)-1, "OutPutCode %s not recognized in %s", argv[1],
- argv[0]);
- SCWrite(pCon, pBueffel, eError);
- return 0;
- }
- /* create a new one */
- pNew = (pCaptureEntry) malloc(sizeof(CaptureEntry));
- if (!pNew) {
- SICSLogWrite("Out of memory in LogCapture", eInternal);
- return 0;
- }
- if (pCapture) {
- pCapture->pPrevious = pNew;
- }
- pNew->pPrevious = NULL;
- pNew->pNext = pCapture;
- pCapture = pNew;
- pNew->iAllFlag = 0;
- pNew->pCon = pCon;
- pNew->iOut = i;
- return 1;
- }
- return 0;
+ return code_bits;
}
+
/*--------------------------------------------------------------------------*/
static int HasLineFeed(char *pText)
{
@@ -203,28 +262,67 @@ static int HasLineFeed(char *pText)
}
/*---------------------------------------------------------------------------*/
-#define MAXLOG 10000
-#define MAXFILES 20
+static const char* timestamp(struct timeval *tp, char delim) {
+ static char ts[80];
+ int year, month, day;
+ int hour, min, sec, usec;
+ struct timeval tv;
+ struct tm *time;
+ if (tp)
+ tv = *tp;
+ else
+ gettimeofday(&tv, NULL);
+ time = localtime(&tv.tv_sec);
+ year = 1900 + time->tm_year;
+ month = time->tm_mon + 1;
+ day = time->tm_mday;
+ hour = time->tm_hour;
+ min = time->tm_min;
+ sec = time->tm_sec;
+ usec = (int) tv.tv_usec;
+ snprintf(ts, 80, "%04d-%02d-%02dT%02d%c%02d%c%02d.%06d",
+ year, month, day, hour, delim, min, delim, sec, usec);
+ return ts;
+}
+
+/*---------------------------------------------------------------------------*/
+#define MAXLOG 100000
+#define MAXFILES 100
static FILE *fLogFile = NULL;
static int iFile = 0;
static int iLineCount = 0;
static int iLogUsable = 1;
+static char filnam[1024];
+static char prevfilnam[1024];
/*---------------------------------------------------------------------------*/
int OpenVerifyLogFile()
{
char pFile[256];
- char filnam[512];
char *pChar = NULL;
+ char fPath[1024];
+
+ pChar = getenv("SICS_INIT_LOGPATH");
+ if (!pChar) {
+ snprintf(fPath, sizeof(fPath) - 1, "%s/", "../log");
+ } else {
+ snprintf(fPath, sizeof(fPath) - 1, "%s/", pChar);
+ }
pChar = IFindOption(pSICSOptions, "LogFileBaseName");
if (!pChar) { /* Try to write to file "server" in */
strcpy(pFile, "server");
} else {
- strlcpy(pFile, pChar, 255);
+ strlcpy(pFile, pChar, sizeof(pFile) - 1);
}
- snprintf(filnam, 511, "%s%2.2d.log", pFile, iFile);
+
+ snprintf(filnam, sizeof(filnam) - 1, "%s%s_%19.19s.%02d.log", fPath, pFile, timestamp(NULL, '-'), iFile);
+ iFile++;
+ if (iFile >= MAXFILES) {
+ iFile = 0;
+ }
+
fLogFile = fopen(filnam, "w");
if (!fLogFile) {
fprintf(stderr, "ERROR: Cannot open logfile %s for writing\n", pFile);
@@ -242,23 +340,21 @@ void SICSLogEnable(int flag)
}
/*---------------------------------------------------------------------------*/
-void SICSLogWrite(char *pText, OutCode eOut)
+static void SICSLogWriteFile(char *pText, OutCode eOut, struct timeval *tp)
{
- char pFile[256];
- char *pChar = NULL;
pCaptureEntry pCurrent;
- char pBueffel[256];
+ int text_len;
#ifdef NOLOG
return;
#endif
+ text_len = strlen(pText);
/* do all captured */
pCurrent = pCapture;
while (pCurrent) {
- if ((pCurrent->iOut == eOut) || (pCurrent->iAllFlag == 1)) {
- ANETwrite(pCurrent->pCon->sockHandle, pText, strlen(pText));
- ANETwrite(pCurrent->pCon->sockHandle, "\n", 1);
+ if ((pCurrent->code_bits & (1 << eOut))) {
+ pCurrent->pFunc(pText, eOut, pCurrent->pData);
}
pCurrent = pCurrent->pNext;
}
@@ -273,23 +369,242 @@ void SICSLogWrite(char *pText, OutCode eOut)
return;
}
iLogUsable = OpenVerifyLogFile();
+ iLineCount = 0;
}
/* switch file if too many lines */
if (iLineCount >= MAXLOG) {
- fclose(fLogFile);
- fLogFile = NULL;
- iFile++;
- iLineCount = 0;
- if (iFile >= MAXFILES) {
- iFile = 0;
- }
+ strcpy(prevfilnam, filnam);
+ FILE *prevlog = fLogFile;
+
iLogUsable = OpenVerifyLogFile();
+ iLineCount = 0;
+ fprintf(prevlog, "%s: <<>> to %s\n",
+ timestamp(NULL, ':'),
+ filnam);
+ fclose(prevlog);
+ if (1 == iLogUsable) {
+ fprintf(fLogFile, "%s: <<>> from %s\n",
+ timestamp(NULL, ':'),
+ prevfilnam);
+ iLineCount++;
+ }
}
if (1 == iLogUsable) {
- fprintf(fLogFile, "%s\n", pText);
+ if (iLineCount == 0)
+ fprintf(fLogFile, "%s: <<>>\n", timestamp(NULL, ':'));
+ fprintf(fLogFile, "%s:%s:", timestamp(tp, ':'), OutCodeToTxt(eOut));
+ fprintf(fLogFile, "%s", pText);
+ if (text_len < 1 || pText[text_len - 1] != '\n')
+ fprintf(fLogFile, "\n");
fflush(fLogFile);
iLineCount++;
}
}
+
+void SICSLogWriteTimeCode(char *pText, OutCode eOut, struct timeval *tp, char code)
+{
+ char buf[200];
+ const char *cp;
+ int idx = 0;
+ struct timeval tv;
+
+ if (tp == NULL) {
+ gettimeofday(&tv, NULL);
+ tp = &tv;
+ }
+ if (isalnum(code)) {
+ buf[idx++] = code;
+ buf[idx++] = ':';
+ }
+ cp = pText;
+ while (*cp) {
+ if (*cp == '\n') {
+ buf[idx++] = '\n';
+ buf[idx++] = '\0';
+ SICSLogWriteFile(buf, eOut, tp);
+ idx = 0;
+ buf[idx++] = '*';
+ buf[idx++] = ':';
+ } else if (*cp == '\r') {
+ buf[idx++] = '\\';
+ buf[idx++] = 'r';
+ } else if (*cp == '\t') {
+ buf[idx++] = '\\';
+ buf[idx++] = 't';
+ } else if (*cp < ' ' || *cp > '~') {
+ const char hex[] = "0123456789ABCDEF";
+ buf[idx++] = '<';
+ buf[idx++] = hex[(*cp >> 4) & 0xF];
+ buf[idx++] = hex[(*cp) & 0xF];
+ buf[idx++] = '>';
+ } else {
+ buf[idx++] = *cp;
+ }
+ cp++;
+ if (idx > 132) {
+ buf[idx++] = '\n';
+ buf[idx++] = '\0';
+ SICSLogWriteFile(buf, eOut, tp);
+ idx = 0;
+ buf[idx++] = '*';
+ buf[idx++] = ':';
+ }
+ }
+ /* Suppress the spurious blank lines caused by trailing newlines */
+ if (idx == 2 && buf[0] == '*' && buf[1] == ':')
+ return;
+ if (idx > 0) {
+ buf[idx++] = '\n';
+ buf[idx++] = '\0';
+ SICSLogWriteFile(buf, eOut, tp);
+ }
+}
+
+void SICSLogWriteCode(char *pText, OutCode eOut, char code)
+{
+ SICSLogWriteTimeCode(pText, eOut, NULL, code);
+}
+
+void SICSLogWriteTime(char *pText, OutCode eOut, struct timeval *tp)
+{
+ SICSLogWriteTimeCode(pText, eOut, tp, 'L');
+}
+
+void SICSLogWrite(char *pText, OutCode eOut)
+{
+ SICSLogWriteTime(pText, eOut, NULL);
+}
+
+void SICSLogWriteHexTime(const char *text, int count, OutCode eOut,
+ struct timeval *tp)
+{
+ const char hex[] = "0123456789ABCDEF";
+ char addr[20], left[80], right[80];
+ char *lp = left;
+ char *rp = right;
+ int i;
+ int duplicates;
+ /* Limit the output */
+ if (count > 1024)
+ count = 1024;
+ duplicates = 0;
+ for (i = 0; i < count; ++i) {
+ if ((i & 0xF) == 0) {
+ if (i > 0) {
+ char line[132];
+ snprintf(line, sizeof(line) - 1, "%-6s: %-49s | %-17s |", addr,
+ left, right);
+ SICSLogWriteTime(line, eOut, tp);
+ }
+ snprintf(addr, sizeof(addr) - 1, "0x%04X", i);
+ while (i >= 16 && i + 16 < count) {
+ if (memcmp(&text[i - 16], &text[i], 16) != 0)
+ break;
+ ++duplicates;
+ i += 16;
+ }
+ if (duplicates > 0) {
+ if (duplicates > 1) {
+ char line[132];
+ snprintf(line, sizeof(line) - 1, "%-6s: ... (%d duplicates)",
+ addr, duplicates);
+ SICSLogWriteTime(line, eOut, tp);
+ } else {
+ char line[132];
+ snprintf(line, sizeof(line) - 1, "%-6s: %-49s | %-17s |", addr,
+ left, right);
+ SICSLogWriteTime(line, eOut, tp);
+ }
+ duplicates = 0;
+ }
+ snprintf(addr, sizeof(addr) - 1, "0x%04X", i);
+ lp = left;
+ rp = right;
+ }
+ *lp++ = hex[(text[i] >> 4) & 0xF];
+ *lp++ = hex[(text[i]) & 0xF];
+ *lp++ = ' ';
+ if (text[i] >= ' ' && text[i] <= '~')
+ *rp++ = text[i];
+ else
+ *rp++ = '.';
+ /* if we just did slot 7, insert an extra space */
+ if ((i & 0xF) == 7) {
+ *lp++ = ' ';
+ *rp++ = ' ';
+ }
+ *lp = *rp = '\0';
+ }
+ if (i > 0) {
+ char line[132];
+ snprintf(line, sizeof(line) - 1, "%-6s: %-49s | %-17s |", addr, left,
+ right);
+ SICSLogWriteTime(line, eOut, tp);
+ }
+}
+
+void SICSLogWriteHex(const char *text, int count, OutCode eOut)
+{
+ SICSLogWriteHexTime(text, count, eOut, NULL);
+}
+
+void SICSLogTimePrintf(OutCode eOut, struct timeval *tp, const char *fmt,
+ ...)
+{
+ va_list ap;
+ char buf[256];
+ char *dyn;
+ unsigned int l;
+ int res;
+
+ va_start(ap, fmt);
+ l = vsnprintf(buf, sizeof buf, fmt, ap);
+ va_end(ap);
+ if (l >= sizeof buf) {
+ /* we have probably a C99 conforming snprintf and
+ need a larger buffer
+ */
+ dyn = malloc(l + 1);
+ if (dyn != NULL) {
+ va_start(ap, fmt);
+ vsnprintf(dyn, l + 1, fmt, ap);
+ va_end(ap);
+ SICSLogWriteTime(dyn, eOut, tp);
+ free(dyn);
+ return;
+ }
+ }
+ SICSLogWriteTime(buf, eOut, tp);
+ return;
+}
+
+void SICSLogPrintf(OutCode eOut, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+ char *dyn;
+ unsigned int l;
+ int res;
+
+ va_start(ap, fmt);
+ l = vsnprintf(buf, sizeof buf, fmt, ap);
+ va_end(ap);
+ if (l >= sizeof buf) {
+ /* we have probably a C99 conforming snprintf and
+ need a larger buffer
+ */
+ dyn = malloc(l + 1);
+ if (dyn != NULL) {
+ va_start(ap, fmt);
+ vsnprintf(dyn, l + 1, fmt, ap);
+ va_end(ap);
+ SICSLogWrite(dyn, eOut);
+ free(dyn);
+ return;
+ }
+ }
+ SICSLogWrite(buf, eOut);
+ return;
+}
diff --git a/servlog.h b/servlog.h
index c61615f5..99fafc0a 100644
--- a/servlog.h
+++ b/servlog.h
@@ -16,9 +16,25 @@
#define SICSLOG
#include "Scommon.h"
void SICSLogWrite(char *ptext, OutCode eOut);
-void SICSLogEnable(int flag);
-int KillCapture(SConnection * pCon);
-
-int LogCapture(SConnection * pCon, SicsInterp * pInter, void *pData,
- int argc, char *argv[]);
+void SICSLogWriteTime(char *ptext, OutCode eOut, struct timeval *tp);
+void SICSLogWriteCode(char *ptext, OutCode eOut, char code);
+void SICSLogWriteTimeCode(char *ptext, OutCode eOut, struct timeval *tp, char code);
+void SICSLogWriteHex(const char *text, int count, OutCode eOut);
+void SICSLogWriteHexTime(const char *text, int count, OutCode eOut,
+ struct timeval *tp);
+#if __GNUC__ > 2
+#define G_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#else
+#define G_GNUC_PRINTF( format_idx, arg_idx )
+#endif
+void SICSLogPrintf(OutCode eOut, const char *fmt, ...) G_GNUC_PRINTF (2, 3);
+void SICSLogTimePrintf(OutCode eOut, struct timeval *tp, const char *fmt,
+ ...) G_GNUC_PRINTF (3, 4);
+#undef G_GNUC_PRINTF
+void SICSLogEnable(int flag);
+typedef void (*pSICSLogHook)(const char *pText, OutCode eOut, void *pData);
+char *AddSICSLogHook(pSICSLogHook func, const char *pCodes, void *pData);
+char *RemSICSLogHook(void *pData);
+unsigned int GetSICSLogHook(void *pData);
#endif
diff --git a/sicsget.c b/sicsget.c
index 2924a2a6..f4feb803 100644
--- a/sicsget.c
+++ b/sicsget.c
@@ -90,7 +90,7 @@ static int SICSGetCommand(SConnection * pCon, SicsInterp * pSics, void *pData,
}
} else {
if(v.dataType == HIPTEXT && strstr(v.v.text,"ERROR") != NULL){
- SCPrintf(pCon,eError,v.v.text);
+ SCPrintf(pCon,eError,"%s",v.v.text);
} else {
SCPrintf(pCon,eError,"ERROR: value for %s not found", argv[1]);
}
@@ -153,6 +153,7 @@ static int InvokeSICSFunc(void *ms, void *userData)
SCsetMacro(pCon,0);
if(!status){
self->success = 0;
+ SCDeleteConnection(pCon);
return MPSTOP;
}
self->response = strdup(Tcl_GetStringResult(InterpGetTcl(pServ->pSics)));
diff --git a/sicsglobal.c b/sicsglobal.c
new file mode 100644
index 00000000..7956f6a6
--- /dev/null
+++ b/sicsglobal.c
@@ -0,0 +1,4 @@
+/***************************** Necessary Globals ****************************/
+
+const char *argv0 = (void*) 0;
+int isDuringInitialization = 1; /* gets set to zero after initialization */
diff --git a/sicsglobal.h b/sicsglobal.h
new file mode 100644
index 00000000..49d43352
--- /dev/null
+++ b/sicsglobal.h
@@ -0,0 +1,6 @@
+#ifndef SICSGLOBAL_H
+#define SICSGLOBAL_H
+
+extern const char *argv0;
+extern int isDuringInitialization;
+#endif
diff --git a/sicshipadaba.c b/sicshipadaba.c
index dd6fc203..c62ecade 100644
--- a/sicshipadaba.c
+++ b/sicshipadaba.c
@@ -435,12 +435,12 @@ typedef struct {
int internalID;
} HdbCBInfo;
-static Protocol isJSON(SConnection * pCon)
+static Protocol isJSON(SConnection * pCon, int notify)
{
char proName[128];
void *pData;
- if (SCinMacro(pCon)) {
+ if (notify == 0 && SCinMacro(pCon)) {
return normal_protocol;
}
pData = FindCommandData(pServ->pSics, "protocol", "Protocol");
@@ -659,7 +659,7 @@ static hdbCallbackReturn SICSNotifyCallback(pHdb node, void *userData,
GetHdbPath(node,updatePath,sizeof(updatePath));
result = CreateDynString(128, 128);
- if ((protocol = isJSON(cbInfo->pCon)) == 1)
+ if ((protocol = isJSON(cbInfo->pCon, 1)) == 1)
outCode = eHdbEvent;
else
outCode = eEvent;
@@ -688,8 +688,11 @@ static hdbCallbackReturn SICSNotifyCallback(pHdb node, void *userData,
}
formatNameValue(protocol, updatePath, GetCharArray(printedData), result,
mm->v->dataType);
- /* SCWrite(cbInfo->pCon, GetCharArray(result), outCode); */
+#ifdef SITE_ANSTO
+ SCWrite(cbInfo->pCon, GetCharArray(result), outCode);
+#else
SCPureSockWrite(cbInfo->pCon, GetCharArray(result), outCode);
+#endif
DeleteDynString(printedData);
} else {
formatNameValue(protocol, updatePath, "!!datachange!!", result, HIPTEXT);
@@ -786,7 +789,7 @@ static hdbCallbackReturn TreeChangeCallback(pHdb node, void *userData,
return hdbAbort;
}
path = GetHipadabaPath(node);
- if ((protocol = isJSON(cbInfo->pCon)) == 1)
+ if ((protocol = isJSON(cbInfo->pCon, 1)) == 1)
outCode = eHdbEvent;
else
outCode = eEvent;
@@ -1540,7 +1543,7 @@ pHdb FindHdbParent(char *rootpath, char *relpath, char **namePtr,
} else {
iret = snprintf(buffer, sizeof buffer, "%s/%s", rootpath, relpath);
}
- if (iret < 0 || iret >= sizeof(buffer)) {
+ if (iret < 0 || iret >= (int) sizeof(buffer)) {
SCWrite(pCon, "ERROR: path too long", eError);
return NULL;
}
@@ -1652,7 +1655,7 @@ int GetHdbPath(pHdb nodeArg, char *path, size_t pathlen)
len = 0;
for (node = nodeArg; node != NULL && node != root; node = node->mama) {
len += strlen(node->name) + 1;
- if (len >= pathlen)
+ if (len >= (int) pathlen)
return 0; /* buffer overflow (recursive path?) */
parent = node;
}
@@ -1669,7 +1672,7 @@ int GetHdbPath(pHdb nodeArg, char *path, size_t pathlen)
}
l = strlen(sics);
len += l;
- if (len > pathlen)
+ if (len > (int) pathlen)
return 0; /* buffer overflow */
strncpy(path, sics, l);
}
@@ -2336,6 +2339,71 @@ static int MakeHdbNode(SConnection * pCon, SicsInterp * pSics, void *pData,
return 1;
}
+/*--------------------------------------------------------------------------*/
+static int MakeHdbScriptNode(SConnection * pCon, SicsInterp * pSics,
+ void *pData, int argc, char *argv[])
+{
+ int type = 0, i, length = 0;
+ char *name = NULL;
+ pHdb parent = NULL;
+ pHdb child = NULL;
+ pHdb current = NULL;
+ char *urgv[] = { "5", NULL };
+ char driver[] = { "hdb" };
+ char buffer[512], buffer2[512];
+
+
+ if (!SCMatchRights(pCon, usMugger)) {
+ return 0;
+ }
+
+ if (argc < 5) {
+ SCWrite(pCon, "ERROR: not enough arguments to MakeHdbNode", eError);
+ return 0;
+ }
+
+ /*
+ * convert datatype
+ */
+ strtolower(argv[4]);
+ type = convertHdbType(argv[4]);
+ if (type >= 7) {
+ SCWrite(pCon,
+ "ERROR: invalid type requested: none, int, float, text, intar, floatar, intvarar, floatvarar supported",
+ eError);
+ return 0;
+ }
+ if (type > 2) {
+ if (argc < 6) {
+ SCWrite(pCon, "ERROR: array length missing for array data type",
+ eError);
+ return 0;
+ } else {
+ length = atoi(argv[5]);
+ }
+ }
+
+ parent = FindHdbParent(NULL, argv[1], &name, pCon);
+ if (parent == NULL) {
+ return 0; /* error messages written inside FindHdbParent */
+ }
+ child = MakeSICSScriptPar(name, argv[3], argv[2],
+ makeHdbValue(type, length));
+ if (child == NULL) {
+ SCWrite(pCon, "ERROR: out of memory creating node", eError);
+ return 0;
+ }
+
+ AddHipadabaChild(parent, child, pCon);
+ /*
+ * have it polled automatically
+ */
+ addPollObject(poller, pCon, GetHipadabaPath(child), driver, 1, urgv);
+
+ SCSendOK(pCon);
+ return 1;
+}
+
/*------------------------------------------------------------------------------*/
static int isNodeProtected(pHdb node)
{
@@ -2559,7 +2627,7 @@ static int ZipGetHdbNode(SConnection * pCon, SicsInterp * pSics,
SCWrite(pCon, "ERROR: out of memory formatting data", eError);
return 0;
}
- if ((protocol = isJSON(pCon)) == 1)
+ if ((protocol = isJSON(pCon, 0)) == 1)
outCode = eHdbEvent;
else
outCode = eValue;
@@ -2604,7 +2672,7 @@ static int ZipReadHdbNode(SConnection * pCon, SicsInterp * pSics,
SCWrite(pCon, "ERROR: out of memory formatting data", eError);
return 0;
}
- if ((protocol = isJSON(pCon)) == 1)
+ if ((protocol = isJSON(pCon,0)) == 1)
outCode = eHdbEvent;
else
outCode = eValue;
@@ -2715,13 +2783,17 @@ static int GetHdbNode(SConnection * pCon, SicsInterp * pSics, void *pData,
}
}
memset(&newValue, 0, sizeof(hdbValue));
- GetHipadabaPar(targetNode, &newValue, pCon);
+ /* ffr XXX I expect if status=0 then we don't have a valid value
+ Original code was causing a segfault for hdb text nodes
+ */
+ if (0 == GetHipadabaPar(targetNode, &newValue, pCon))
+ return 0;
parData = formatValue(newValue, targetNode);
if (parData == NULL) {
SCWrite(pCon, "ERROR: out of memory formatting data", eError);
return 0;
}
- if ((protocol = isJSON(pCon)) == 1)
+ if ((protocol = isJSON(pCon, 0)) == 1)
outCode = eHdbEvent;
else
outCode = eValue;
@@ -2759,13 +2831,14 @@ static int GetHdbVal(SConnection * pCon, SicsInterp * pSics, void *pData,
return 0;
}
memset(&newValue, 0, sizeof(hdbValue));
- GetHipadabaPar(targetNode, &newValue, pCon);
+ if (0 == GetHipadabaPar(targetNode, &newValue, pCon))
+ return 0;
parData = formatValue(newValue, targetNode);
if (parData == NULL) {
SCWrite(pCon, "ERROR: out of memory formatting data", eError);
return 0;
} else {
- if ((protocol = isJSON(pCon)) == 1)
+ if ((protocol = isJSON(pCon, 0)) == 1)
outCode = eHdbEvent;
else
outCode = eValue;
@@ -2777,6 +2850,37 @@ static int GetHdbVal(SConnection * pCon, SicsInterp * pSics, void *pData,
return 1;
}
+/*---------------------------------------------------------------------------*/
+static int GetHdbValIgnoreError(
+ SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[])
+{
+ pHdb targetNode = NULL;
+ pDynString parData = NULL;
+ int protocol, outCode;
+
+ if (argc != 2) {
+ SCWrite(pCon, "ERROR: syntax must be: hvali ", eError);
+ return 0;
+ }
+
+ targetNode = FindHdbNode(NULL, argv[1], pCon);
+ if (targetNode == NULL) {
+ return 0;
+ }
+ parData = formatValue(targetNode->value, targetNode);
+ if (parData == NULL) {
+ SCWrite(pCon, "ERROR: out of memory formatting data", eError);
+ return 0;
+ }
+ if ((protocol = isJSON(pCon, 0)) == 1)
+ outCode = eHdbEvent;
+ else
+ outCode = eValue;
+ SCWrite(pCon, GetCharArray(parData), outCode);
+ DeleteDynString(parData);
+ return 1;
+}
+
/*--------------------------------------------------------------------------*/
static int countChildren(pHdb node)
{
@@ -3071,7 +3175,7 @@ static int ListHdbNode(SConnection * pCon, SicsInterp * pSics, void *pData,
} else if (strcmp(argv[1], "-cli") == 0) {
listData = formatClientList(node);
} else {
- if ((protocol = isJSON(pCon)) == 1) {
+ if ((protocol = isJSON(pCon, 0)) == 1) {
listData = formatJSONList(node);
outCode = eHdbEvent;
} else {
@@ -3151,6 +3255,62 @@ static int RemoveHdbCallback(SConnection * pCon, SicsInterp * pSics,
return 1;
}
+/*---------------------------------------------------------------------------*/
+static int LinkHdbNode(SConnection * pCon, SicsInterp * pSics, void *pData,
+ int argc, char *argv[])
+{
+ pHdb node = NULL;
+ char buffer[256];
+ pObjectDescriptor pDes = NULL;
+
+ if (argc < 3) {
+ SCWrite(pCon, "ERROR: need path and object name to link", eError);
+ return 0;
+ }
+ if (!SCMatchRights(pCon, usMugger)) {
+ return 0;
+ }
+
+ node = GetHipadabaNode(root, argv[1]);
+ if (node == NULL) {
+ snprintf(buffer, 255, "ERROR: path %s NOT found!", argv[1]);
+ SCWrite(pCon, buffer, eError);
+ return 0;
+ }
+
+ pDes = FindCommandDescriptor(pSics, argv[2]);
+ if (pDes == NULL) {
+ snprintf(buffer, 255, "ERROR: failed to find object %s", argv[2]);
+ SCWrite(pCon, buffer, eError);
+ return 0;
+ }
+ if (pDes->parNode == NULL) {
+ snprintf(buffer, 255,
+ "ERROR: Object %s does not use Hipadaba natively and thus cannot be linked",
+ argv[2]);
+ SCWrite(pCon, buffer, eError);
+ return 0;
+ }
+
+ if (pDes->parNode->mama != NULL) {
+ snprintf(buffer, 255,
+ "ERROR: Object %s is already linked somewhere else", argv[2]);
+ SCWrite(pCon, buffer, eError);
+ return 0;
+ }
+
+ AddHipadabaChild(node, pDes->parNode, pCon);
+
+ if (argc > 3) {
+ if (pDes->parNode->name != NULL) {
+ free(pDes->parNode->name);
+ }
+ pDes->parNode->name = strdup(argv[3]);
+ }
+
+ SCSendOK(pCon);
+ return 1;
+}
/*-------------------------------------------------------------------------*/
static int isArrayNode(pHdb node)
{
@@ -3599,9 +3759,9 @@ static hdbCallbackReturn CommandSetCallback(pHdb node, void *userData,
}
current = current->next;
}
- SendHdbStatusMessage(node, "start");
+// SendHdbStatusMessage(node,"start");
status = HDBInvoke(pCon, pServ->pSics, GetCharArray(cmd));
- SendHdbStatusMessage(node, "stop");
+// SendHdbStatusMessage(node,"stop");
DeleteDynString(cmd);
if (status == 1) {
return hdbContinue;
@@ -3632,6 +3792,60 @@ static hdbCallbackReturn CommandGetCallback(pHdb node, void *userData,
return hdbContinue;
}
+/*--------------------------------------------------------------------------*/
+static int SicsCommandNode(SConnection * pCon, SicsInterp * pSics,
+ void *pData, int argc, char *argv[])
+{
+ char *name = NULL;
+ pHdbCallback kalle = NULL;
+ pHdb parent = NULL, node = NULL;
+
+ if (argc < 3) {
+ SCWrite(pCon, "ERROR: insufficent number of arguments to hcommand",
+ eError);
+ return 0;
+ }
+ if (!SCMatchRights(pCon, usMugger)) {
+ return 0;
+ }
+
+ parent = FindHdbParent(NULL, argv[1], &name, pCon);
+ if (parent == NULL) {
+ return 0; /* error message already written */
+ }
+ node = MakeHipadabaNode(name, HIPTEXT, 1);
+ if (node == NULL) {
+ SCWrite(pCon, "ERROR: out of memory in hcommand", eError);
+ return 0;
+ }
+ node->value.v.text = strdup(argv[2]);
+ node->value.arrayLength = strlen(argv[2]);
+ SetHdbProperty(node, "sicscommand", argv[2]);
+ // Set privillege of the node
+ if (argc > 3) {
+ SetHdbProperty(node, "priv", argv[3]);
+ }
+
+ kalle = MakeHipadabaCallback(CommandSetCallback, NULL, NULL);
+ if (kalle == NULL) {
+ SCWrite(pCon, "ERROR: out of memory in hcommand", eError);
+ return 0;
+ }
+ AppendHipadabaCallback(node, kalle);
+
+ kalle = MakeHipadabaCallback(CommandGetCallback, NULL, NULL);
+ if (kalle == NULL) {
+ SCWrite(pCon, "ERROR: out of memory in hcommand", eError);
+ return 0;
+ }
+ AppendHipadabaCallback(node, kalle);
+
+ AddHipadabaChild(parent, node, pCon);
+
+ SCSendOK(pCon);
+ return 1;
+}
+
/*======================= Property Functions ================================*/
static int SetSICSHdbProperty(SConnection * pCon, SicsInterp * pSics,
void *pData, int argc, char *argv[])
@@ -3681,6 +3895,30 @@ static int DelSICSHdbProperty(SConnection * pCon, SicsInterp * pSics,
return 1;
}
+/*--------------------------------------------------------------------------*/
+static int HasSICSHdbProperty(SConnection * pCon, SicsInterp * pSics,
+ void *pData, int argc, char *argv[])
+{
+ pHdb targetNode = NULL;
+
+ if (argc < 3) {
+ SCWrite(pCon, "ERROR: need path key as parameters", eError);
+ return 0;
+ }
+ targetNode = FindHdbNode(NULL, argv[1], pCon);
+ if (targetNode == NULL) {
+ SCWrite(pCon, "ERROR: node not found", eError);
+ return 0;
+ }
+ if (HasHdbProperty(targetNode, argv[2])) {
+ SCPrintf(pCon, eValue, "%s", "true");
+ return 1;
+ } else {
+ SCPrintf(pCon, eValue, "%s", "false");
+ return 1;
+ }
+}
+
/*--------------------------------------------------------------------------*/
static int GetSICSHdbProperty(SConnection * pCon, SicsInterp * pSics,
void *pData, int argc, char *argv[])
@@ -3741,10 +3979,18 @@ static int ListSICSHdbProperty(SConnection * pCon, SicsInterp * pSics,
char buffer[512];
const char *pKey = NULL;
pDynString data = NULL;
+ int genTclList = 0;
if (argc < 2) {
SCWrite(pCon, "ERROR: need path as parameter", eError);
return 0;
+ } else if (argc == 3) {
+ if (strncasecmp(argv[2], "tcl", 3) == 0)
+ genTclList = 1;
+ if (strncasecmp(argv[2], "tclesc", 6) == 0)
+ genTclList |= 2;
+ if (strncasecmp(argv[2], "tclnam", 6) == 0)
+ genTclList |= 4;
}
targetNode = FindHdbNode(NULL, argv[1], pCon);
if (targetNode == NULL) {
@@ -3759,9 +4005,24 @@ static int ListSICSHdbProperty(SConnection * pCon, SicsInterp * pSics,
InitHdbPropertySearch(targetNode);
while ((pKey = GetNextHdbProperty(targetNode, buffer, 511)) != NULL) {
DynStringConcat(data, (char *) pKey);
- DynStringConcat(data, "=");
- DynStringConcat(data, buffer);
- DynStringConcat(data, "\n");
+ if (genTclList) {
+ char *bp;
+ DynStringConcatChar(data, ' ');
+ if (genTclList & 4)
+ continue;
+ DynStringConcatChar(data, '{');
+ for (bp = buffer; *bp; ++bp) {
+ if (genTclList & 2 && (*bp == '{' || *bp == '}'))
+ DynStringConcatChar(data, '\\');
+ DynStringConcatChar(data, *bp);
+ }
+ DynStringConcatChar(data, '}');
+ DynStringConcatChar(data, ' ');
+ } else {
+ DynStringConcat(data, "=");
+ DynStringConcat(data, buffer);
+ DynStringConcat(data, "\n");
+ }
}
SCWrite(pCon, GetCharArray(data), eValue);
DeleteDynString(data);
@@ -3789,24 +4050,94 @@ static int CallNotify(SConnection * pCon, SicsInterp * pSics,
return 1;
}
-/*---------------------------------------------------------------------------*/
-static pHdb matchHdbProp(pHdb root, char *propname, char *buffer)
+static int ANSTO_ListSICSHdbProperty(SConnection * pCon,
+ SicsInterp * pSics, void *pData,
+ int argc, char *argv[])
{
- char value[1024];
+ pHdb targetNode = NULL;
+ char buffer[512], *globPtr = NULL;
+ int cmpSize = 0;
+ const char *pKey = NULL;
+ pDynString data = NULL;
+
+ if (argc < 3) {
+ SCWrite(pCon, "ERROR: need path and search string as parameters",
+ eError);
+ return 0;
+ }
+ targetNode = FindHdbNode(NULL, argv[1], pCon);
+ if (targetNode == NULL) {
+ SCWrite(pCon, "ERROR: node not found", eError);
+ return 0;
+ }
+ data = CreateDynString(64, 64);
+ if (data == NULL) {
+ SCWrite(pCon, "ERROR: out of memory in ListSICSHdbProperty", eError);
+ return 0;
+ }
+ InitHdbPropertySearch(targetNode);
+
+ /* Allow simple glob matches with '*' as a suffix
+ Eg hfindprop /hpath @*
+ */
+ if ((globPtr = index(argv[2], '*')) != NULL) {
+ *globPtr = '\0';
+ }
+ cmpSize = strlen(argv[2]);
+ while ((pKey = GetNextHdbProperty(targetNode, buffer, 511)) != NULL) {
+ if (strncasecmp(argv[2], pKey, cmpSize) == 0) {
+ DynStringConcat(data, (char *) pKey);
+ DynStringConcat(data, " ");
+ DynStringConcat(data, buffer);
+ DynStringConcat(data, "\n");
+ }
+ }
+ SCWrite(pCon, GetCharArray(data), eValue);
+ DeleteDynString(data);
+ return 1;
+}
+
+/*---------------------------------------------------------------------------*/
+static pHdb matchHdbProp(pHdb root, char *propname, char *buffer,
+ pDynString result, int invertmatch)
+{
+ char value[1024], *path = NULL;
pHdb current = NULL, search;
memset(value, 0, 1024);
- if (GetHdbProperty(root, propname, value, 1023) == 1) {
+ if (strcmp(buffer, "*") == 0) {
+ if (GetHdbProperty(root, propname, value, 1023) == 1) {
+ if (!invertmatch) {
+ path = GetHipadabaPath(root);
+ DynStringConcat(result, path);
+ DynStringConcat(result, "\n");
+ free(path);
+ }
+ } else if (invertmatch) {
+ path = GetHipadabaPath(root);
+ DynStringConcat(result, path);
+ DynStringConcat(result, "\n");
+ free(path);
+ }
+ } else if (GetHdbProperty(root, propname, value, 1023) == 1) {
if (strstr(buffer, value) != NULL) {
- return root;
+ if (!invertmatch) {
+ path = GetHipadabaPath(root);
+ DynStringConcat(result, path);
+ DynStringConcat(result, "\n");
+ free(path);
+ }
+ } else if (invertmatch) {
+ path = GetHipadabaPath(root);
+ DynStringConcat(result, path);
+ DynStringConcat(result, "\n");
+ free(path);
}
}
current = root->child;
while (current != NULL) {
- search = matchHdbProp(current, propname, buffer);
- if (search != NULL) {
- return search;
- }
+ search =
+ matchHdbProp(current, propname, buffer, result, invertmatch);
current = current->next;
}
@@ -3819,6 +4150,8 @@ static int MatchHdbProperty(SConnection * pCon, SicsInterp * pSics,
pHdb root = NULL;
pHdb foundNode = NULL;
char buffer[1024], *path = NULL;
+ int node = 1, prop = 2, propval = 3, invertmatch = 0;
+ pDynString matchList = NULL;
if (argc < 4) {
SCWrite(pCon,
@@ -3826,25 +4159,29 @@ static int MatchHdbProperty(SConnection * pCon, SicsInterp * pSics,
eError);
return 0;
}
+ if (argc >= 5) {
+ if (strcasecmp(argv[1], "invert") == 0) {
+ invertmatch = 1;
+ node++;
+ prop++;
+ propval++;
+ }
+ }
memset(buffer, 0, 1024);
- Arg2Text(argc - 3, &argv[3], buffer, 1023);
- root = GetHipadabaNode(GetHipadabaRoot(), argv[1]);
+ Arg2Text(argc - propval, &argv[propval], buffer, 1023);
+ root = GetHipadabaNode(GetHipadabaRoot(), argv[node]);
if (root == NULL) {
SCWrite(pCon, "ERROR: start node for search not found", eError);
return 0;
}
- strtolower(argv[2]);
+ strtolower(argv[prop]);
strtolower(buffer);
- foundNode = matchHdbProp(root, argv[2], buffer);
-
- if (foundNode == NULL) {
- SCWrite(pCon, "NONE", eValue);
- } else {
- path = GetHipadabaPath(foundNode);
- SCWrite(pCon, path, eValue);
- free(path);
- }
+ matchList = CreateDynString(128, 128);
+ foundNode =
+ matchHdbProp(root, argv[prop], buffer, matchList, invertmatch);
+ SCWrite(pCon, GetCharArray(matchList), eValue);
+ DeleteDynString(matchList);
return 1;
}
/*---------------------------------------------------------------------------*/
@@ -3953,7 +4290,9 @@ int InstallSICSHipadaba(SConnection * pCon, SicsInterp * pSics,
{
root = MakeHipadabaNode("/", HIPNONE, 0);
+ AddCommand(pSics, "hmake", MakeHdbNode, NULL, NULL);
AddCommand(pSics, "hfactory", HdbNodeFactory, NULL, NULL);
+ AddCommand(pSics, "hmakescript", MakeHdbScriptNode, NULL, NULL);
AddCommand(pSics, "hattach", SICSHdbAdapter, NULL, NULL);
AddCommand(pSics, "hsubsamplehm", HdbSubSample, NULL, NULL);
AddCommand(pSics, "hdel", DeleteHdbNode, NULL, NULL);
@@ -3961,22 +4300,26 @@ int InstallSICSHipadaba(SConnection * pCon, SicsInterp * pSics,
AddCommand(pSics, "hupdate", UpdateHdbNode, NULL, NULL);
AddCommand(pSics, "hget", GetHdbNode, NULL, NULL);
AddCommand(pSics, "hval", GetHdbVal, NULL, NULL);
+ AddCommand(pSics, "hvali", GetHdbValIgnoreError, NULL, NULL);
AddCommand(pSics, "hzipget", ZipGetHdbNode, NULL, NULL);
AddCommand(pSics, "hzipread", ZipReadHdbNode, NULL, NULL);
AddCommand(pSics, "hbinread", BinReadHdbNode, NULL, NULL);
AddCommand(pSics, "hlist", ListHdbNode, NULL, NULL);
AddCommand(pSics, "hnotify", AutoNotifyHdbNode, NULL, NULL);
AddCommand(pSics, "hdelcb", RemoveHdbCallback, NULL, NULL);
+ AddCommand(pSics, "hlink", LinkHdbNode, NULL, NULL);
AddCommand(pSics, "hinfo", HdbNodeInfo, NULL, NULL);
- /* AddCommand(pSics, "hval", HdbNodeVal, NULL, NULL);*/
AddCommand(pSics, "hchain", ChainHdbNode, NULL, NULL);
+ AddCommand(pSics, "hcommand", SicsCommandNode, NULL, NULL);
AddCommand(pSics, "harray", HdbArrayNode, NULL, NULL);
AddCommand(pSics, "hsetprop", SetSICSHdbProperty, NULL, NULL);
AddCommand(pSics, "hdelprop", DelSICSHdbProperty, NULL, NULL);
AddCommand(pSics, "hgetprop", GetSICSHdbProperty, NULL, NULL);
AddCommand(pSics, "hgetpropval", GetSICSHdbPropertyVal, NULL, NULL);
AddCommand(pSics, "hmatchprop", MatchHdbProperty, NULL, NULL);
+ AddCommand(pSics, "hpropexists", HasSICSHdbProperty, NULL, NULL);
AddCommand(pSics, "hlistprop", ListSICSHdbProperty, NULL, NULL);
+ AddCommand(pSics, "hfindprop", ANSTO_ListSICSHdbProperty, NULL, NULL);
AddCommand(pSics, "hcallnotify",CallNotify, NULL, NULL);
AddCommand(pSics, "haddcheck",AddCheck, NULL, NULL);
AddCommand(pSics, "hscriptnotify",AddScriptNotify, NULL, NULL);
diff --git a/sicslist.c b/sicslist.c
index 9da82d6a..92e6cc9c 100644
--- a/sicslist.c
+++ b/sicslist.c
@@ -169,6 +169,18 @@ static int printObjectData(SConnection * pCon, pDummy obj, char *key)
}
}
+static int existsObjectData(SConnection * pCon, pDummy obj, char *key)
+{
+ char *ptr = NULL;
+ ptr = IFindOption(obj->pDescriptor->pKeys, key);
+ if (ptr != NULL)
+ SCPrintf(pCon, eValue, "%s", "true");
+ else
+ SCPrintf(pCon, eValue, "%s", "false");
+
+ return 1;
+}
+
/*-----------------------------------------------------------------
* this function implements a set on top of a list. This means that
* the list is first searched for the occurence of name. name is only
@@ -518,6 +530,21 @@ int SicsList(SConnection * pCon, SicsInterp * pSics, void *pData,
}
}
+ if (strcmp(argv[1], "exists") == 0) {
+ if (argc < 4) {
+ SCWrite(pCon, "ERROR: not enough arguments", eError);
+ return 0;
+ } else {
+ pCom = FindCommand(pSics, argv[2]);
+ if (pCom == NULL) {
+ SCWrite(pCon, "ERROR: Object doesn't exist", eError);
+ return 0;
+ } else
+ return existsObjectData(pCon, (pDummy) pCom->pData, argv[3]);
+ }
+ }
+
+
/*
* object properties
*/
diff --git a/sicsobj.c b/sicsobj.c
index 330e5933..8f08d396 100644
--- a/sicsobj.c
+++ b/sicsobj.c
@@ -477,7 +477,6 @@ int InvokeSICSOBJ(SConnection * pCon, SicsInterp * pSics, void *pData,
status = GetHdbProperty(parNode,"geterror",buffer,sizeof(buffer));
if (status == 1 && strstr(buffer,"none") == NULL){
SCPrintf(pCon,eValue,"ERROR: %s on last read of %s", buffer, argv[0]);
- SCPrintf(pCon,eValue,"%s = -99999", argv[0]);
return 0;
}
status = GetHipadabaPar(parNode, &data, pCon);
diff --git a/sicsutil.c b/sicsutil.c
index 53f89034..c18f6686 100644
--- a/sicsutil.c
+++ b/sicsutil.c
@@ -51,7 +51,8 @@ double DoubleTime(void)
{
struct timeval now;
/* the resolution of this function is usec, if the machine supports this
- and the mantissa of a double is 51 bits or more (31 for sec and 20 for mic$
+ and the mantissa of a double is 51 bits or more (31 bits for seconds
+ and 20 for microseconds)
*/
gettimeofday(&now, NULL);
return now.tv_sec + now.tv_usec / 1e6;
diff --git a/sicvar.c b/sicvar.c
index 8286c0b1..dbf496d8 100644
--- a/sicvar.c
+++ b/sicvar.c
@@ -245,7 +245,7 @@ int VarSetFloat(pSicsVariable self, float fNew, int iUserRights)
if (self->iAccessCode < iUserRights) {
return 0;
- } else {
+ } else if (self->fVal != fNew) {
self->fVal = fNew;
InvokeCallBack(self->pCall, VALUECHANGE, self);
tracePar(self->name,"%f",fNew);
@@ -261,7 +261,7 @@ int VarSetInt(pSicsVariable self, int iNew, int iUserRights)
if (self->iAccessCode < iUserRights) {
return 0;
- } else {
+ } else if (self->iVal != iNew) {
self->iVal = iNew;
InvokeCallBack(self->pCall, VALUECHANGE, self);
tracePar(self->name,"%d",iNew);
@@ -277,7 +277,7 @@ int VarSetText(pSicsVariable self, char *pNew, int iUserRights)
if (self->iAccessCode < iUserRights) {
return 0;
- } else {
+ } else if (self->text && strcmp(self->text, pNew)) {
if (self->text) {
free(self->text);
}
@@ -461,7 +461,7 @@ int VarWrapper(SConnection * pCon, SicsInterp * pInterp, void *pData,
return 1;
case veFloat:
VarGetFloat(pVar, &fVal);
- snprintf(pBueffel,sizeof(pBueffel)-1, "%s = %f", argv[0], fVal);
+ snprintf(pBueffel,sizeof(pBueffel)-1, "%s = %#g", argv[0], fVal);
SCWrite(pCon, pBueffel, eValue);
DeleteTokenList(pList);
return 1;
diff --git a/simdriv.c b/simdriv.c
index a32e72c1..0f123a6a 100644
--- a/simdriv.c
+++ b/simdriv.c
@@ -244,6 +244,12 @@ static int SimSetPar(void *self, SConnection * pCon, char *name,
return 0;
}
+static int SimGetTextPar(void *pData, char *name, char *textPar)
+{
+ snprintf(textPar, 8, "unknown");
+ return 1;
+}
+
/*---------------------------------------------------------------------------*/
void KillSIM(void *pData)
{
@@ -336,6 +342,7 @@ MotorDriver *CreateSIM(SConnection * pCon, int argc, char *argv[])
pDriv->fSpeed = .01;
pDriv->iTime = 0;
pDriv->KillPrivate = KillSIM;
+ pDriv->GetDriverTextPar = SimGetTextPar;
/* check for optional speed paramter */
pCurrent = pCurrent->pNext;
diff --git a/simidx.h b/simidx.h
index 17668ecc..b29d14df 100644
--- a/simidx.h
+++ b/simidx.h
@@ -85,6 +85,7 @@ int SimIdxRun();
* \param errCode pointer to an integer error code
* \param error A text buffer to hold a description of the error
* \param errLen The length of error
+ */
void SimIdxGetError(int *errCode, char *error, int errLen);
/**
diff --git a/sinfox.h b/sinfox.h
index 4d688532..c256c259 100644
--- a/sinfox.h
+++ b/sinfox.h
@@ -56,7 +56,7 @@ char *pDeviceTypes[25] = {
"connection",
"crystalselector",
"environment monitor",
- "environment controller",
+ "environment_controller",
"gpib",
"hklscan",
"hmcontrol",
diff --git a/singlex.c b/singlex.c
index 60a8f70f..848fe039 100644
--- a/singlex.c
+++ b/singlex.c
@@ -450,7 +450,7 @@ static int SymRefCmd(pSICSOBJ self, SConnection * pCon, pHdb commandNode,
hkl[2] = equiv.l[i];
if (priv->diffractometer->
calculateSettings(priv->diffractometer, hkl, settings) == 1) {
- snprintf(buffer, 512, "%d,%d,%d", equiv.h[i], equiv.k[i],
+ snprintf(buffer, sizeof(buffer), "%d,%d,%d", equiv.h[i], equiv.k[i],
equiv.l[i]);
SCWrite(pCon, buffer, eValue);
return 1;
diff --git a/stack_trace.c b/stack_trace.c
new file mode 100644
index 00000000..64b64f97
--- /dev/null
+++ b/stack_trace.c
@@ -0,0 +1,322 @@
+#define _GNU_SOURCE
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "dynstring.h"
+#include "sics.h"
+
+/*---------------------------------------------------------------------------*/
+static const char* timestamp() {
+ static char ts[80];
+ int year, month, day;
+ int hour, min, sec, usec;
+ struct timeval tv;
+ struct tm *time;
+ gettimeofday(&tv, NULL);
+ time = localtime(&tv.tv_sec);
+ year = 1900 + time->tm_year;
+ month = time->tm_mon + 1;
+ day = time->tm_mday;
+ hour = time->tm_hour;
+ min = time->tm_min;
+ sec = time->tm_sec;
+ usec = (int) tv.tv_usec;
+ snprintf(ts, 80, "%04d%02d%02d%02d%02d%02d",
+ year, month, day, hour, min, sec);
+ return ts;
+}
+
+#ifdef SICS_HAS_BACKTRACE
+void stack_trace_backtrace() {
+ void *array[STRACE_SIZE];
+ size_t stack_size;
+ size_t i;
+ char **strings;
+ char buffer[1024];
+ pDynString s = CreateDynString(1000, 100);
+
+ stack_size = backtrace(array, STRACE_SIZE);
+ strings = backtrace_symbols(array, stack_size);
+ for (i = 0; i < stack_size; ++i) {
+ snprintf(buffer, 1024, "%p: %s\n", array[i], strings[i]);
+ DynStringConcat(s, buffer);
+ }
+ free(strings);
+
+ SICSLogWrite(GetCharArray(s), eLogError);
+ DeleteDynString(s);
+}
+#endif
+
+#ifdef SICS_HAS_BFD
+#include
+#define STRACE_SIZE 64
+
+/* globals retained across calls to resolve. */
+static bfd* abfd = 0;
+static asymbol **syms = 0;
+static asection *text = 0;
+static void resolve(void *address, char *buffer, int buf_len) {
+ if (!abfd) {
+ char ename[1024];
+ int l = readlink("/proc/self/exe",ename,sizeof(ename));
+ if (l == -1) {
+ perror("failed to find executable\n");
+ return;
+ }
+ ename[l] = 0;
+
+ bfd_init();
+
+ abfd = bfd_openr(ename, 0);
+ if (!abfd) {
+ perror("bfd_openr failed: ");
+ return;
+ }
+
+ /* oddly, this is required for it to work... */
+ bfd_check_format(abfd,bfd_object);
+
+ unsigned storage_needed = bfd_get_symtab_upper_bound(abfd);
+ syms = (asymbol **) malloc(storage_needed);
+ unsigned cSymbols = bfd_canonicalize_symtab(abfd, syms);
+
+ text = bfd_get_section_by_name(abfd, ".text");
+ }
+
+ long offset = ((long)address) - text->vma;
+ if (offset > 0) {
+ const char *file;
+ const char *func;
+ unsigned line;
+ if (bfd_find_nearest_line(abfd, text, syms, offset, &file, &func, &line) && file)
+ snprintf(buffer, buf_len, "file: %s, line: %u, func %s\n",file,line,func);
+ }
+}
+
+void stack_trace_bfd() {
+ void *array[STRACE_SIZE];
+ size_t stack_size;
+ size_t i;
+ char buffer[1024];
+ pDynString s = CreateDynString(1000, 100);
+
+ stack_size = backtrace(array, STRACE_SIZE);
+ for (i = 0; i < stack_size; ++i) {
+ resolve(array[i], buffer, 1024);
+ DynStringConcat(s, buffer);
+ }
+ SICSLogWrite(GetCharArray(s), eLogError);
+ DeleteDynString(s);
+}
+#endif
+
+#ifdef SICS_HAS_GDB
+void stack_trace_gdb() {
+ int pipefd[2];
+ char pid_buf[30];
+ sprintf(pid_buf, "%d", getpid());
+ char name_buf[512];
+ name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
+ if (pipe(pipefd) == -1) {
+ perror("pipe");
+ abort();
+ }
+ int child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork");
+ abort();
+ }
+ if (!child_pid) {
+ close(pipefd[0]);
+ dup2(pipefd[1], 1); /* redirect stdout to pipe */
+ dup2(pipefd[1], 2); /* redirect stderr to pipe */
+ fprintf(stdout,"stack trace for %s pid=%s\n",name_buf,pid_buf);
+ fflush(stdout);
+ execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
+ abort(); /* If gdb failed to start */
+ } else {
+ char buff;
+ pDynString s = CreateDynString(100, 100);
+ close(pipefd[1]);
+ while (read(pipefd[0], &buff, 1) > 0) {
+ DynStringConcatChar(s, buff);
+ }
+ SICSLogWrite(GetCharArray(s), eLogError);
+ DeleteDynString(s);
+ close(pipefd[0]);
+ waitpid(child_pid,NULL,0);
+ }
+}
+#endif
+
+#ifdef SICS_HAS_ABORT
+void stack_trace_abort() {
+ int child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork");
+ abort();
+ }
+ if (!child_pid) {
+ /* Dump core */
+ abort();
+ } else {
+ waitpid(child_pid,NULL,0);
+ }
+}
+#endif
+
+size_t get_size_of_files(char *glob) {
+ int pipefd[2];
+ if (pipe(pipefd) == -1) {
+ perror("pipe");
+ abort();
+ }
+ int child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork");
+ abort();
+ }
+ if (!child_pid) {
+ char cmd[120];
+ close(pipefd[0]);
+ dup2(pipefd[1], 1); /* redirect stdout to pipe */
+ dup2(pipefd[1], 2); /* redirect stderr to pipe */
+ snprintf(cmd, sizeof(cmd)-1, "du -s %s", glob);
+ execlp("bash", "bash", "-c", cmd, NULL);
+ exit(0); /* If gdb failed to start */
+ } else {
+ size_t total_size = 0;
+ char buff;
+ pDynString s = CreateDynString(100, 100);
+ close(pipefd[1]);
+ while (read(pipefd[0], &buff, 1) > 0) {
+ DynStringConcatChar(s, buff);
+ if (buff == '\n') {
+ SICSLogWrite(GetCharArray(s), eLog);
+ int this_size = atoi(GetCharArray(s));
+ total_size += this_size;
+ DynStringClear(s);
+ }
+ }
+ if (GetDynStringLength(s) > 0)
+ SICSLogWrite(GetCharArray(s), eLogError);
+ DeleteDynString(s);
+ close(pipefd[0]);
+ waitpid(child_pid, NULL, 0);
+ SICSLogPrintf(eLog, "Total size of files \"%s\" is %ldK", glob, total_size);
+ return total_size;
+ }
+}
+
+void generate_stack_trace(int full, int dump) {
+ int pipefd[2];
+ char pid_buf[30];
+ sprintf(pid_buf, "%d", getpid());
+ char name_buf[512];
+ name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
+ if (pipe(pipefd) == -1) {
+ perror("pipe");
+ abort();
+ }
+ int child_pid = fork();
+ if (child_pid == -1) {
+ perror("fork");
+ abort();
+ }
+ if (!child_pid) {
+ char *bt;
+ close(pipefd[0]);
+ dup2(pipefd[1], 1); /* redirect stdout to pipe */
+ dup2(pipefd[1], 2); /* redirect stderr to pipe */
+ fprintf(stdout, "stack trace for %s pid=%s\n", name_buf, pid_buf);
+ fflush(stdout);
+ if (full)
+ bt = "bt full";
+ else
+ bt = "bt";
+ if (dump) {
+ char gen_buf[128];
+ snprintf(gen_buf, sizeof(gen_buf)-1, "generate-core-file /tmp/core.%s.%s", timestamp(), pid_buf);
+ execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", bt, "-ex", gen_buf, name_buf, pid_buf, NULL);
+ } else {
+ execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", bt, name_buf, pid_buf, NULL);
+ }
+ fprintf(stdout, "gdb failed to start for %s pid=%d\n", name_buf, getpid());
+ fflush(stdout);
+ abort(); /* If gdb failed to start */
+ } else {
+ char buff;
+ pDynString s = CreateDynString(100, 100);
+ close(pipefd[1]);
+ while (read(pipefd[0], &buff, 1) > 0) {
+ DynStringConcatChar(s, buff);
+ }
+ SICSLogWrite(GetCharArray(s), eLogError);
+ DeleteDynString(s);
+ close(pipefd[0]);
+ waitpid(child_pid, NULL, 0);
+ }
+}
+
+void stack_trace(int mode) {
+ switch (mode) {
+ case 0:
+ default:
+#ifdef SICS_HAS_BACKTRACE
+ stack_trace_0();
+#endif
+ break;
+ case 1:
+#ifdef SICS_HAS_BFD
+ stack_trace_bfd();
+#endif
+ break;
+ case 2:
+#ifdef SICS_HAS_GDB
+ stack_trace_gdb();
+#endif
+ break;
+ case 3:
+#ifdef SICS_HAS_ABORT
+ stack_trace_abort();
+#endif
+ break;
+ case 4:
+ generate_stack_trace(0, 0);
+ break;
+ case 5:
+ generate_stack_trace(0, 1);
+ break;
+ case 6:
+ generate_stack_trace(1, 0);
+ break;
+ case 7:
+ generate_stack_trace(1, 1);
+ break;
+ case 99:
+ get_size_of_files("/tmp/core.*");
+ break;
+ }
+ fflush(stdout);
+}
+/**
+ * \brief Tests the stack_trace facility
+ */
+int Ansto_Stack_Trace(SConnection *pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) {
+ int mode;
+ void stack_trace(int mode);
+ if (argc > 1)
+ mode = atoi(argv[1]);
+ else
+ mode = 1;
+ stack_trace(mode);
+}
diff --git a/stack_trace.h b/stack_trace.h
new file mode 100644
index 00000000..cbc58f77
--- /dev/null
+++ b/stack_trace.h
@@ -0,0 +1,4 @@
+#ifndef STACK_TRACE_H
+#define STACK_TRACE_H
+void stack_trace(int mode);
+#endif
diff --git a/statemon.c b/statemon.c
index 63167f05..56b0ca8e 100644
--- a/statemon.c
+++ b/statemon.c
@@ -3,7 +3,7 @@
* from the device executor and from scan and batch commands. Clients can
* listen to this in order to figure out what is actually going on in a
* given SICS installation. This might in the end supersede the status code
- * managment in status.c
+ * management in status.c
*
* copyright: see file COPYRIGHT
*
@@ -83,7 +83,7 @@ static int ExeCallback(int iEvent, void *pEvent, void *pUser)
}
if (iEvent == BATCHSTART) {
- snprintf(pBueffel, 131, "exe %s", name);
+ snprintf(pBueffel, 131, "exe %s", name);
InvokeCallBack(self->pCall, STSTART, pBueffel);
return 1;
}
@@ -106,6 +106,7 @@ static int StateInterest(int iEvent, void *pEvent, void *pUser)
return -1;
}
+
if (pCon == NULL || device == NULL) {
printf("Bad StateInterest in statemon\n");
return 0;
@@ -283,7 +284,7 @@ int StateMonFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
if (argc > 1) {
pPtr = FindCommandData(pSics, argv[1], "ScanObject");
if (pPtr == NULL) {
- SCWrite(pCon, "ERROR: failked to locate scan object", eError);
+ SCWrite(pCon, "ERROR: failed to locate scan object", eError);
} else {
target = GetCallbackInterface(pPtr);
assert(target != NULL);
@@ -296,6 +297,20 @@ int StateMonFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
return 1;
}
+/*
+ * Context test function for callback removal
+ */
+ int CheckConMatch(const void* context, const void* pUserData)
+{
+ SConnection *pKon = (SConnection*) pUserData;
+ SConnection *pCon = (SConnection*) context;
+ if (VerifyConnection(pCon) && VerifyConnection(pKon)) {
+ if (pCon->ident == pKon->ident)
+ return 0;
+ }
+ return 1;
+}
+
/*---------------------------------------------------------------------------*/
int StateMonAction(SConnection * pCon, SicsInterp * pSics, void *pData,
int argc, char *argv[])
@@ -314,6 +329,7 @@ int StateMonAction(SConnection * pCon, SicsInterp * pSics, void *pData,
}
strtolower(argv[1]);
if (strcmp(argv[1], "interest") == 0) {
+ RemoveCallbackUsr(self->pCall, StateInterest, CheckConMatch, pCon); /* only this one */
callCon = SCCopyConnection(pCon);
if (callCon == NULL) {
SCWrite(pCon, "ERROR: out of memory registering interest", eError);
@@ -324,19 +340,18 @@ int StateMonAction(SConnection * pCon, SicsInterp * pSics, void *pData,
SCRegister(pCon, pSics, self->pCall, lID);
lID = RegisterCallback(self->pCall, STEND, StateInterest,
callCon, NULL);
- SCRegister(pCon, pSics, self->pCall, lID);
SCSendOK(pCon);
return 1;
} else if (strcmp(argv[1], "uninterest") == 0) {
- for (i = 0; i < 2; i++) {
- lID = SCgetCallbackID(pCon, self->pCall);
- if (lID >= 0) {
- RemoveCallback(self->pCall, lID);
- }
- }
+ RemoveCallbackUsr(self->pCall, StateInterest, CheckConMatch, pCon); /* only this one */
+ SCSendOK(pCon);
+ return 1;
+ } else if (strcmp(argv[1], "hdbuninterest") == 0) {
+ RemoveCallbackUsr(self->pCall, StateHdbInterest, CheckConMatch, pCon); /* only this one */
SCSendOK(pCon);
return 1;
} else if (strcmp(argv[1], "hdbinterest") == 0) {
+ RemoveCallbackUsr(self->pCall, StateHdbInterest, CheckConMatch, pCon); /* only this one */
callCon = SCCopyConnection(pCon);
if (callCon == NULL) {
SCWrite(pCon, "ERROR: out of memory registering interest", eError);
diff --git a/statistics.c b/statistics.c
index 406be5a4..da2a622d 100644
--- a/statistics.c
+++ b/statistics.c
@@ -58,7 +58,7 @@ int StatisticsCommand(SConnection * con, SicsInterp * pSics, void *pData,
gettimeofday(&now, 0);
dif = timeFloat(timeDif(lastStat, now));
- SCPrintf(con, eLog, "calls/s time[%] full[%] mean[ms] command");
+ SCPrintf(con, eLog, "calls/s time[%%] full[%%] mean[ms] command");
SCPrintf(con, eLog, "----------------------------------------------");
for (p = list; p != NULL; p = p->next) {
if (dif > 0) {
diff --git a/statusfile.c b/statusfile.c
index 454ab5ef..84eb343c 100644
--- a/statusfile.c
+++ b/statusfile.c
@@ -52,12 +52,17 @@
#include "lld.h"
#include "exebuf.h"
+#define CLEAN_LOCKED 1
+#define CLEAN_MISSING 2
+
static int parameterChange = 0;
/*-----------------------------------------------------------------------*/
int StatusFileTask(void *data)
{
char *pFile = NULL;
+ if (!hasRestored())
+ return 1;
if (parameterChange) {
parameterChange = 0;
@@ -215,6 +220,94 @@ static int listRestoreErr(pRestoreObj self, SConnection * pCon)
return 1;
}
+/*-----------------------------------------------------------------------*/
+static int cleanRestoreErr(pRestoreObj self, SConnection * pCon, int hard)
+{
+ char command[1024];
+ char message[1024];
+ char *errMsg = NULL;
+ int status;
+ int newErrList;
+ pDynString data = NULL;
+ pStringDict dict;
+ int count_in = 0;
+ int count_ex = 0;
+
+ if (!self->errList)
+ return 1;
+
+ data = CreateDynString(256,256);
+ if (data == NULL) {
+ SCWrite(pCon,"ERROR: out of memory cleaning errors",eError);
+ return 0;
+ }
+ dict = CreateStringDict();
+ if (dict == NULL) {
+ SCWrite(pCon,"ERROR: out of memory cleaning errors",eError);
+ DeleteDynString(data);
+ return 0;
+ }
+ /* create new list */
+ newErrList = LLDstringCreate();
+ if (newErrList < 0) {
+ SCWrite(pCon,"ERROR: out of blobs cleaning errors",eError);
+ DeleteDynString(data);
+ DeleteStringDict(dict);
+ return 0;
+ }
+ status = LLDnodePtr2First(self->errList);
+ while (status == 1) {
+ LLDstringData(self->errList, command);
+ status = LLDnodePtr2Next(self->errList);
+ if (status != 1) {
+ /* Error */
+ errMsg = "ERROR: unpaired error cleaning errors";
+ break;
+ }
+ LLDstringData(self->errList, message);
+ if (command[0] == '#' || message[0] != '#') {
+ /* Error */
+ errMsg = "ERROR: sequence error cleaning errors";
+ break;
+ }
+ DynStringClear(data);
+ DynStringConcat(data, command);
+ DynStringConcat(data, message);
+ status = LLDnodePtr2Next(self->errList);
+ ++count_in;
+ /* Skip duplicate messages */
+ if (StringDictExists(dict, GetCharArray(data)))
+ continue;
+ /* Skip "configured locked!" messages */
+ if (hard == CLEAN_LOCKED &&
+ strstr(message, "#ERR: ERROR: variable ") &&
+ strstr(message, " is configured locked!"))
+ continue;
+ /* Skip "not found" messages */
+ if (hard == CLEAN_MISSING &&
+ strstr(message, "#ERR: Object ") &&
+ strstr(message, "not found"))
+ continue;
+ /* add to dictionary and new list */
+ StringDictAddPair(dict, GetCharArray(data), "");
+ LLDstringAppend(newErrList, command);
+ LLDstringAppend(newErrList, message);
+ ++count_ex;
+ }
+ if (errMsg) {
+ SCWrite(pCon, errMsg, eError);
+ LLDstringDelete(newErrList);
+ } else {
+ /* swap lists */
+ LLDstringDelete(self->errList);
+ self->errList = newErrList;
+ SCPrintf(pCon, eLog, "CleanErr: %d pairs in, %d pairs out", count_in, count_ex);
+ }
+ DeleteDynString(data);
+ DeleteStringDict(dict);
+ return 1;
+}
+
/*-----------------------------------------------------------------------*/
int RestoreStatus(SConnection * pCon, SicsInterp * pSics, void *pData,
int argc, char *argv[])
@@ -243,6 +336,13 @@ int RestoreStatus(SConnection * pCon, SicsInterp * pSics, void *pData,
} else {
if (strcasecmp(argv[1], "listerr") == 0) {
return listRestoreErr(self, pCon);
+ } else if (strcasecmp(argv[1], "cleanerr") == 0) {
+ if (argc > 2 && strcasecmp(argv[2], "locked") == 0)
+ return cleanRestoreErr(self, pCon, CLEAN_LOCKED);
+ else if (argc > 2 && strcasecmp(argv[2], "missing") == 0)
+ return cleanRestoreErr(self, pCon, CLEAN_MISSING);
+ else
+ return cleanRestoreErr(self, pCon, 0);
} else if (strcasecmp(argv[1], "killerr") == 0) {
if (self->errList >= 0) {
LLDdeleteBlob(self->errList);
diff --git a/stdscan.c b/stdscan.c
index d1cc0234..5689b077 100644
--- a/stdscan.c
+++ b/stdscan.c
@@ -498,21 +498,21 @@ int WriteScanPoints(pScanData self, int iPoint)
strlcat(pSteps, pItem,255);
}
}
- strlcat(pLine, " Counts ",1024);
- strlcat(pLine, "Monitor1 ",1024);
- strlcat(pLine, "Monitor2 ",1024);
- strlcat(pLine, "Monitor3 ",1024);
- strlcat(pLine, "Time ",1024);
- strlcat(pInfo, pSteps,1024);
+ strlcat(pLine, " Counts ",sizeof(pLine));
+ strlcat(pLine, "Monitor1 ",sizeof(pLine));
+ strlcat(pLine, "Monitor2 ",sizeof(pLine));
+ strlcat(pLine, "Monitor3 ",sizeof(pLine));
+ strlcat(pLine, "Time ",sizeof(pLine));
+ strlcat(pInfo, pSteps,sizeof(pLine));
snprintf(pItem,sizeof(pItem)-1, "\n%d Points,", self->iNP);
- strlcat(pInfo, pItem,1024);
+ strlcat(pInfo, pItem,sizeof(pLine));
if (self->iMode == eTimer) {
- strlcat(pInfo, " Mode: Timer,",1024);
+ strlcat(pInfo, " Mode: Timer,",sizeof(pLine));
} else {
- strlcat(pInfo, " Mode: Monitor,",1024);
+ strlcat(pInfo, " Mode: Monitor,",sizeof(pLine));
}
snprintf(pItem,sizeof(pItem)-1, " Preset %f", self->fPreset);
- strlcat(pInfo, pItem,1024);
+ strlcat(pInfo, pItem,sizeof(pLine));
fprintf(self->fd, "%s\n", pInfo);
fprintf(self->fd, "%s\n", pLine);
@@ -525,7 +525,7 @@ int WriteScanPoints(pScanData self, int iPoint)
pVar = (pVarEntry) pPtr;
if (pVar) {
snprintf(pItem,sizeof(pItem)-1, "%-9.4f ", GetScanVarPos(pVar, i));
- strlcat(pLine, pItem,1024);
+ strlcat(pLine, pItem,sizeof(pLine));
}
}
/* print Counts & Monitor */
@@ -533,15 +533,15 @@ int WriteScanPoints(pScanData self, int iPoint)
pData = (pCountEntry) pPtr;
if (pData) {
snprintf(pItem,sizeof(pItem)-1, " %-11ld ", pData->lCount);
- strlcat(pLine, pItem,1024);
+ strlcat(pLine, pItem,sizeof(pLine));
snprintf(pItem,sizeof(pItem)-1, "%-11ld ", pData->Monitors[0]);
- strlcat(pLine, pItem,1024);
+ strlcat(pLine, pItem,sizeof(pLine));
snprintf(pItem,sizeof(pItem)-1, "%-11ld ", pData->Monitors[1]);
- strlcat(pLine, pItem,1024);
+ strlcat(pLine, pItem,sizeof(pLine));
snprintf(pItem,sizeof(pItem)-1, "%-11ld ", pData->Monitors[2]);
- strlcat(pLine, pItem,1024);
- snprintf(pItem,sizeof(pItem)-1, "%-5.1f ", pData->fTime);
- strlcat(pLine, pItem,1024);
+ strlcat(pLine, pItem,sizeof(pLine));
+ snprintf(pItem,sizeof(pItem)-1, "%-8.3f ", pData->fTime);
+ strlcat(pLine, pItem,sizeof(pLine));
}
fprintf(self->fd, "%s\n", pLine);
}
diff --git a/task.c b/task.c
index a511357f..396f782d 100644
--- a/task.c
+++ b/task.c
@@ -338,12 +338,14 @@ int TaskYield(pTaskMan self)
int TaskSignal(pTaskMan self, int iSignal, void *pSigData)
{
pTaskHead pTemp, pEnd;
+ int myStatus = self->pCurrent->iStatus;
assert(self);
assert(self->iID == TASKERID);
/* Do one cycle until we are at the caller, then return to him */
pEnd = self->pCurrent;
+ pEnd->iStatus = WAITING;
IncrTaskPointer(self);
while (self->pCurrent != pEnd) {
if (self->pCurrent->pSignal) {
@@ -352,6 +354,7 @@ int TaskSignal(pTaskMan self, int iSignal, void *pSigData)
IncrTaskPointer(self);
}
/* finally, tell me about the thingie as well */
+ pEnd->iStatus = myStatus;
if (pEnd->pSignal) {
pEnd->pSignal(pEnd->pData, iSignal, pSigData);
}
diff --git a/tasscanub.c b/tasscanub.c
index a1ce7343..19a4a5d3 100644
--- a/tasscanub.c
+++ b/tasscanub.c
@@ -491,8 +491,8 @@ static int TASUBHeader(pScanData self)
start with the scan variables
*/
if (pTAS->iPOL >= 0) {
- strcpy(pBueffel, "FORMT: (I4,I4,");
- strcpy(pHeader, " PNT PAL");
+ strcpy(pBueffel, "FORMT: (I3,1X,I3,1X,");
+ strcpy(pHeader, " PNT PAL");
} else {
strcpy(pBueffel, "FORMT: (I4,1X,");
strcpy(pHeader, " PNT ");
@@ -587,7 +587,7 @@ static int TASUBScanPoint(pScanData self, int iPoint)
write point number
*/
if (pTAS->iPOL >= 0) {
- snprintf(pBueffel,sizeof(pBueffel)-1, "%3d %3d", iPoint + 1, pTAS->iPOL);
+ snprintf(pBueffel,sizeof(pBueffel)-1, "%3d %3d ", iPoint + 1, pTAS->iPOL);
} else {
snprintf(pBueffel,sizeof(pBueffel)-1, "%4d ", iPoint + 1);
}
diff --git a/tasub.c b/tasub.c
index e76e248c..f5a1dd5c 100644
--- a/tasub.c
+++ b/tasub.c
@@ -257,6 +257,7 @@ int TasUBFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
"ef", "kf",
"en"
};
+ char *defltMot[] = {"a1", "a2", "mcv", "mch", "a3", "a4", "sgu", "sgl", "a5", "a6", "acv", "ach"};
if (argc < 2) {
SCWrite(pCon, "ERROR: need name to install tasUB", eError);
@@ -292,6 +293,10 @@ int TasUBFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
pNew->motors[9] = TasFindMotor(pSics, "a6");
pNew->motors[10] = TasFindMotor(pSics, "acv");
pNew->motors[11] = TasFindMotor(pSics, "ach");
+ for (i=0; i < 12; i++) {
+ strcpy(pNew->motname[i], defltMot[i]);
+ }
+
} else {
/*
* user defined names
@@ -308,18 +313,21 @@ int TasUBFactory(SConnection * pCon, SicsInterp * pSics, void *pData,
pNew->motors[9] = TasFindMotor(pSics, argv[11]);
pNew->motors[10] = TasFindMotor(pSics, argv[12]);
pNew->motors[11] = TasFindMotor(pSics, argv[13]);
+ for (i=0; i < 12; i++) {
+ strcpy(pNew->motname[i], argv[i+2]);
+ }
}
/*
curvature motors may be missing, anything else is a serious problem
*/
- status += testMotor(pNew, pCon, "a1", A1);
- status += testMotor(pNew, pCon, "a2", A2);
- status += testMotor(pNew, pCon, "a3", A3);
- status += testMotor(pNew, pCon, "a4", A4);
- status += testMotor(pNew, pCon, "sgu", SGU);
- status += testMotor(pNew, pCon, "sgl", SGL);
- status += testMotor(pNew, pCon, "a5", A5);
- status += testMotor(pNew, pCon, "a6", A6);
+ status += testMotor(pNew, pCon, pNew->motname[A1], A1);
+ status += testMotor(pNew, pCon, pNew->motname[A2], A2);
+ status += testMotor(pNew, pCon, pNew->motname[A3], A3);
+ status += testMotor(pNew, pCon, pNew->motname[A4], A4);
+ status += testMotor(pNew, pCon, pNew->motname[SGU], SGU);
+ status += testMotor(pNew, pCon, pNew->motname[SGL], SGL);
+ status += testMotor(pNew, pCon, pNew->motname[A5], A5);
+ status += testMotor(pNew, pCon, pNew->motname[A6], A6);
if (status != 8) {
SCWrite(pCon, "ERROR: a required motor is missing, tasub NOT installed",
eError);
@@ -582,7 +590,9 @@ static void listReflections(ptasUB self, SConnection * pCon)
Tcl_DStringInit(&list);
snprintf(line, 255,
- " NO QH QK QL A3 A4 SGU SGL EI EF\n");
+ " NO QH QK QL %s %s %s %s EI EF\n",
+ self->motname[A3], self->motname[A4], self->motname[SGU],
+ self->motname[SGL]);
Tcl_DStringAppend(&list, line, -1);
status = LLDnodePtr2First(self->reflectionList);
while (status == 1) {
@@ -726,7 +736,9 @@ static int addReflection(ptasUB self, SicsInterp * pSics,
LLDnodeAppend(self->reflectionList, &r);
Tcl_DStringInit(&list);
snprintf(pBueffel, 255,
- " QH QK QL A3 A4 SGU SGL EI EF\n");
+ " QH QK QL %s %s %s %s EI EF\n",
+ self->motname[A3], self->motname[A4], self->motname[SGU],
+ self->motname[SGL]);
Tcl_DStringAppend(&list, pBueffel, -1);
snprintf(pBueffel, 255,
" %6.2f %6.2f %6.2f %7.2f %7.2f %6.2f %6.2f %6.2f %6.2f\n",
@@ -880,7 +892,9 @@ static void listUB(ptasUB self, SConnection * pCon)
snprintf(pBueffel, 255, "UB generated from reflections:\n");
Tcl_DStringAppend(&list, pBueffel, -1);
snprintf(pBueffel, 255,
- " QH QK QL A3 A4 SGU SGL EI EF\n");
+ " QH QK QL %s %s %s %s EI EF\n",
+ self->motname[A3], self->motname[A4], self->motname[SGU],
+ self->motname[SGL]);
Tcl_DStringAppend(&list, pBueffel, -1);
r = self->r1;
snprintf(pBueffel, 255,
@@ -920,7 +934,9 @@ static void printReflectionDiagnostik(ptasUB self, SConnection * pCon,
Tcl_DStringInit(&list);
snprintf(line, 255,
- "METHOD QH QK QL A3 A4 SGU SGL EI EF\n");
+ "METHOD QH QK QL %s %s %s %s EI EF\n",
+ self->motname[A3], self->motname[A4], self->motname[SGU],
+ self->motname[SGL]);
Tcl_DStringAppend(&list, line, -1);
snprintf(line, 255,
"INPUT %8.4f %8.4f %8.4f %7.2f %7.2f %6.2f %6.2f %6.2f %6.2f\n",
@@ -2148,7 +2164,7 @@ int TasUBWrapper(SConnection * pCon, SicsInterp * pSics, void *pData,
}
} else {
snprintf(pBueffel, 131, "ERROR: subcommand %s to %s not defined",
- argv[1], argv[0]);
+ argv[1], argv[0]);
SCWrite(pCon, pBueffel, eError);
return 0;
}
diff --git a/tasub.h b/tasub.h
index 5ea047ef..c6a6f606 100644
--- a/tasub.h
+++ b/tasub.h
@@ -29,13 +29,14 @@
int mustDrive;
int mustDriveQ;
pMotor motors[12];
+ char motname[12][32];
tasReflection r1, r2;
pIDrivable mono;
long monoTaskID;
void *monoData;
int ubValid;
int silent;
- int stopFixed; /* flag to stop multiple fixed messages in scans*/
+ int stopFixed; /* flag to stop multiple fixed messages in scans*/
char *updater;
long groupID;
}tasUB, *ptasUB;
diff --git a/tclClock.c b/tclClock.c
new file mode 100644
index 00000000..aca08dba
--- /dev/null
+++ b/tclClock.c
@@ -0,0 +1,410 @@
+/*
+ * tclClock.c --
+ *
+ * Contains the time and date related commands. This code
+ * is derived from the time and date facilities of TclX,
+ * by Mark Diekhans and Karl Lehenbauer.
+ *
+ * Copyright 1991-1995 Karl Lehenbauer and Mark Diekhans.
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: tclClock.c,v 1.1 2012/03/29 08:45:52 koennecke Exp $
+ *
+ * Slightly modified for inclusion into SICS, Mark Koennecke, February 2012
+ */
+
+#include
+#include
+
+
+
+#include
+
+typedef long TIMEZONE_t;
+/*
+ * from tclDate.c
+ */
+int TclGetDate(char *p, Tcl_WideInt now, long zone, Tcl_WideInt *timePtr);
+/*
+ * from tclUnixDate.c
+ */
+size_t TclpStrftime(char *s, size_t maxsize, CONST char *format, CONST struct tm *t, int useGMT);
+struct tm * TclppGetDate(time_t time, int useGMT);
+
+
+/*
+ * The date parsing stuff uses lexx and has tons o statics.
+ */
+
+TCL_DECLARE_MUTEX(clockMutex)
+
+/*
+ * Function prototypes for local procedures in this file:
+ */
+
+static int FormatClock _ANSI_ARGS_((Tcl_Interp *interp,
+ Tcl_WideInt clockVal, int useGMT,
+ char *format));
+
+/*
+ *-------------------------------------------------------------------------
+ *
+ * Tcl_ClockObjCmd --
+ *
+ * This procedure is invoked to process the "clock" Tcl command.
+ * See the user documentation for details on what it does.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *-------------------------------------------------------------------------
+ */
+
+int
+Tcl_ClockObjCmd (client, interp, objc, objv)
+ ClientData client; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument values. */
+{
+ Tcl_Obj *resultPtr;
+ int index;
+ Tcl_Obj *CONST *objPtr;
+ int useGMT = 0;
+ char *format = "%a %b %d %X %Z %Y";
+ int dummy;
+ Tcl_WideInt baseClock, clockVal;
+ long zone;
+ Tcl_Obj *baseObjPtr = NULL;
+ char *scanStr;
+ int n;
+
+ static CONST char *switches[] =
+ {"clicks", "format", "scan", "seconds", (char *) NULL};
+ enum command { COMMAND_CLICKS, COMMAND_FORMAT, COMMAND_SCAN,
+ COMMAND_SECONDS
+ };
+ static CONST char *formatSwitches[] = {"-format", "-gmt", (char *) NULL};
+ static CONST char *scanSwitches[] = {"-base", "-gmt", (char *) NULL};
+
+ resultPtr = Tcl_GetObjResult(interp);
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj(interp, objv[1], switches, "option", 0, &index)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch ((enum command) index) {
+ case COMMAND_CLICKS: { /* clicks */
+ int forceMilli = 0;
+
+ if (objc == 3) {
+ format = Tcl_GetStringFromObj(objv[2], &n);
+ if ( ( n >= 2 )
+ && ( strncmp( format, "-milliseconds",
+ (unsigned int) n) == 0 ) ) {
+ forceMilli = 1;
+ } else {
+ Tcl_AppendStringsToObj(resultPtr,
+ "bad switch \"", format,
+ "\": must be -milliseconds", (char *) NULL);
+ return TCL_ERROR;
+ }
+ } else if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, "?-milliseconds?");
+ return TCL_ERROR;
+ }
+ if (forceMilli) {
+ /*
+ * We can enforce at least millisecond granularity
+ */
+ Tcl_Time time;
+ Tcl_GetTime(&time);
+ Tcl_SetLongObj(resultPtr,
+ (long) (time.sec*1000 + time.usec/1000));
+ } else {
+ Tcl_SetLongObj(resultPtr, (long) TclpGetClicks());
+ }
+ return TCL_OK;
+ }
+
+ case COMMAND_FORMAT: /* format */
+ if ((objc < 3) || (objc > 7)) {
+ wrongFmtArgs:
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "clockval ?-format string? ?-gmt boolean?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetWideIntFromObj(interp, objv[2], &clockVal)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ objPtr = objv+3;
+ objc -= 3;
+ while (objc > 1) {
+ if (Tcl_GetIndexFromObj(interp, objPtr[0], formatSwitches,
+ "switch", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch (index) {
+ case 0: /* -format */
+ format = Tcl_GetStringFromObj(objPtr[1], &dummy);
+ break;
+ case 1: /* -gmt */
+ if (Tcl_GetBooleanFromObj(interp, objPtr[1],
+ &useGMT) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ }
+ objPtr += 2;
+ objc -= 2;
+ }
+ if (objc != 0) {
+ goto wrongFmtArgs;
+ }
+ return FormatClock(interp, clockVal, useGMT,
+ format);
+
+ case COMMAND_SCAN: /* scan */
+ if ((objc < 3) || (objc > 7)) {
+ wrongScanArgs:
+ Tcl_WrongNumArgs(interp, 2, objv,
+ "dateString ?-base clockValue? ?-gmt boolean?");
+ return TCL_ERROR;
+ }
+
+ objPtr = objv+3;
+ objc -= 3;
+ while (objc > 1) {
+ if (Tcl_GetIndexFromObj(interp, objPtr[0], scanSwitches,
+ "switch", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch (index) {
+ case 0: /* -base */
+ baseObjPtr = objPtr[1];
+ break;
+ case 1: /* -gmt */
+ if (Tcl_GetBooleanFromObj(interp, objPtr[1],
+ &useGMT) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ break;
+ }
+ objPtr += 2;
+ objc -= 2;
+ }
+ if (objc != 0) {
+ goto wrongScanArgs;
+ }
+
+ if (baseObjPtr != NULL) {
+ if (Tcl_GetWideIntFromObj(interp, baseObjPtr,
+ &baseClock) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else {
+ baseClock = TclpGetSeconds();
+ }
+
+ if (useGMT) {
+ zone = -50000; /* Force GMT */
+ } else {
+ zone = TclpGetTimeZone(baseClock);
+ }
+
+ scanStr = Tcl_GetStringFromObj(objv[2], &dummy);
+ Tcl_MutexLock(&clockMutex);
+ if (TclGetDate(scanStr, baseClock, zone,
+ &clockVal) < 0) {
+ Tcl_MutexUnlock(&clockMutex);
+ Tcl_AppendStringsToObj(resultPtr,
+ "unable to convert date-time string \"",
+ scanStr, "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_MutexUnlock(&clockMutex);
+
+ Tcl_SetWideIntObj(resultPtr, clockVal);
+ return TCL_OK;
+
+ case COMMAND_SECONDS: /* seconds */
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 2, objv, NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetLongObj(resultPtr, (long) TclpGetSeconds());
+ return TCL_OK;
+ default:
+ return TCL_ERROR; /* Should never be reached. */
+ }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FormatClock --
+ *
+ * Formats a time value based on seconds into a human readable
+ * string.
+ *
+ * Results:
+ * Standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+FormatClock(interp, clockVal, useGMT, format)
+ Tcl_Interp *interp; /* Current interpreter. */
+ Tcl_WideInt clockVal; /* Time in seconds. */
+ int useGMT; /* Boolean */
+ char *format; /* Format string */
+{
+ struct tm *timeDataPtr;
+ Tcl_DString buffer, uniBuffer;
+ int bufSize;
+ char *p;
+ int result;
+ time_t tclockVal;
+#if !defined(HAVE_TM_ZONE) && !defined(WIN32)
+ TIMEZONE_t savedTimeZone = 0; /* lint. */
+ char *savedTZEnv = NULL; /* lint. */
+#endif
+
+#ifdef HAVE_TZSET
+ /*
+ * Some systems forgot to call tzset in localtime, make sure its done.
+ */
+ static int calledTzset = 0;
+
+ Tcl_MutexLock(&clockMutex);
+ if (!calledTzset) {
+ tzset();
+ calledTzset = 1;
+ }
+ Tcl_MutexUnlock(&clockMutex);
+#endif
+
+ /*
+ * If the user gave us -format "", just return now
+ */
+ if (*format == '\0') {
+ return TCL_OK;
+ }
+
+#if !defined(HAVE_TM_ZONE) && !defined(WIN32)
+ /*
+ * This is a kludge for systems not having the timezone string in
+ * struct tm. No matter what was specified, they use the local
+ * timezone string. Since this kludge requires fiddling with the
+ * TZ environment variable, it will mess up if done on multiple
+ * threads at once. Protect it with a the clock mutex.
+ */
+
+ Tcl_MutexLock( &clockMutex );
+ if (useGMT) {
+ CONST char *varValue;
+
+ varValue = Tcl_GetVar2(interp, "env", "TZ", TCL_GLOBAL_ONLY);
+ if (varValue != NULL) {
+ savedTZEnv = strcpy(ckalloc(strlen(varValue) + 1), varValue);
+ } else {
+ savedTZEnv = NULL;
+ }
+ Tcl_SetVar2(interp, "env", "TZ", "GMT0", TCL_GLOBAL_ONLY);
+ savedTimeZone = timezone;
+ timezone = 0;
+ tzset();
+ }
+#endif
+
+ tclockVal = (time_t) clockVal;
+ timeDataPtr = TclppGetDate(tclockVal, useGMT);
+
+ /*
+ * Make a guess at the upper limit on the substituted string size
+ * based on the number of percents in the string.
+ */
+
+ for (bufSize = 1, p = format; *p != '\0'; p++) {
+ if (*p == '%') {
+ bufSize += 40;
+ if (p[1] == 'c') {
+ bufSize += 226;
+ }
+ } else {
+ bufSize++;
+ }
+ }
+ Tcl_DStringInit(&uniBuffer);
+ Tcl_UtfToExternalDString(NULL, format, -1, &uniBuffer);
+ Tcl_DStringInit(&buffer);
+ Tcl_DStringSetLength(&buffer, bufSize);
+
+ /* If we haven't locked the clock mutex up above, lock it now. */
+
+#if defined(HAVE_TM_ZONE) || defined(WIN32)
+ Tcl_MutexLock(&clockMutex);
+#endif
+ result = TclpStrftime(buffer.string, (unsigned int) bufSize,
+ Tcl_DStringValue(&uniBuffer), timeDataPtr, useGMT);
+#if defined(HAVE_TM_ZONE) || defined(WIN32)
+ Tcl_MutexUnlock(&clockMutex);
+#endif
+ Tcl_DStringFree(&uniBuffer);
+
+#if !defined(HAVE_TM_ZONE) && !defined(WIN32)
+ if (useGMT) {
+ if (savedTZEnv != NULL) {
+ Tcl_SetVar2(interp, "env", "TZ", savedTZEnv, TCL_GLOBAL_ONLY);
+ ckfree(savedTZEnv);
+ } else {
+ Tcl_UnsetVar2(interp, "env", "TZ", TCL_GLOBAL_ONLY);
+ }
+ timezone = savedTimeZone;
+ tzset();
+ }
+ Tcl_MutexUnlock( &clockMutex );
+#endif
+
+ if (result == 0) {
+ /*
+ * A zero return is the error case (can also mean the strftime
+ * didn't get enough space to write into). We know it doesn't
+ * mean that we wrote zero chars because the check for an empty
+ * format string is above.
+ */
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "bad format string \"", format, "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Convert the time to UTF from external encoding [Bug: 3345]
+ */
+ Tcl_DStringInit(&uniBuffer);
+ Tcl_ExternalToUtfDString(NULL, buffer.string, -1, &uniBuffer);
+
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), uniBuffer.string, -1);
+
+ Tcl_DStringFree(&uniBuffer);
+ Tcl_DStringFree(&buffer);
+ return TCL_OK;
+}
+
diff --git a/tclDate.c b/tclDate.c
new file mode 100644
index 00000000..6abca447
--- /dev/null
+++ b/tclDate.c
@@ -0,0 +1,1876 @@
+/*
+ * tclDate.c --
+ *
+ * This file is generated from a yacc grammar defined in
+ * the file tclGetDate.y. It should not be edited directly.
+ *
+ * Copyright (c) 1992-1995 Karl Lehenbauer and Mark Diekhans.
+ * Copyright (c) 1995-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: tclDate.c,v 1.1 2012/03/29 08:45:52 koennecke Exp $
+ */
+
+#include
+#include
+#include
+
+#if defined(MAC_TCL) && !defined(TCL_MAC_USE_MSL_EPOCH)
+# define EPOCH 1904
+# define START_OF_TIME 1904
+# define END_OF_TIME 2039
+#else
+# define EPOCH 1970
+# define START_OF_TIME 1902
+# define END_OF_TIME 2037
+#endif
+
+/*
+ * The offset of tm_year of struct tm returned by localtime, gmtime, etc.
+ * I don't know how universal this is; K&R II, the NetBSD manpages, and
+ * ../compat/strftime.c all agree that tm_year is the year-1900. However,
+ * some systems may have a different value. This #define should be the
+ * same as in ../compat/strftime.c.
+ */
+#define TM_YEAR_BASE 1900
+
+#define HOUR(x) ((int) (60 * x))
+#define SECSPERDAY (24L * 60L * 60L)
+#define IsLeapYear(x) ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0))
+
+/*
+ * An entry in the lexical lookup table.
+ */
+typedef struct _TABLE {
+ char *name;
+ int type;
+ time_t value;
+} TABLE;
+
+
+/*
+ * Daylight-savings mode: on, off, or not yet known.
+ */
+typedef enum _DSTMODE {
+ DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+ * Meridian: am, pm, or 24-hour style.
+ */
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+ * Global variables. We could get rid of most of these by using a good
+ * union as the yacc stack. (This routine was originally written before
+ * yacc had the %union construct.) Maybe someday; right now we only use
+ * the %union very rarely.
+ */
+static char *TclDateInput;
+static DSTMODE TclDateDSTmode;
+static time_t TclDateDayOrdinal;
+static time_t TclDateDayNumber;
+static time_t TclDateMonthOrdinal;
+static int TclDateHaveDate;
+static int TclDateHaveDay;
+static int TclDateHaveOrdinalMonth;
+static int TclDateHaveRel;
+static int TclDateHaveTime;
+static int TclDateHaveZone;
+static time_t TclDateTimezone;
+static time_t TclDateDay;
+static time_t TclDateHour;
+static time_t TclDateMinutes;
+static time_t TclDateMonth;
+static time_t TclDateSeconds;
+static time_t TclDateYear;
+static MERIDIAN TclDateMeridian;
+static time_t TclDateRelMonth;
+static time_t TclDateRelDay;
+static time_t TclDateRelSeconds;
+static time_t *TclDateRelPointer;
+
+/*
+ * Prototypes of internal functions.
+ */
+static void TclDateerror _ANSI_ARGS_((char *s));
+static time_t ToSeconds _ANSI_ARGS_((time_t Hours, time_t Minutes,
+ time_t Seconds, MERIDIAN Meridian));
+static int Convert _ANSI_ARGS_((time_t Month, time_t Day, time_t Year,
+ time_t Hours, time_t Minutes, time_t Seconds,
+ MERIDIAN Meridia, DSTMODE DSTmode, time_t *TimePtr));
+static time_t DSTcorrect _ANSI_ARGS_((time_t Start, time_t Future));
+static time_t NamedDay _ANSI_ARGS_((time_t Start, time_t DayOrdinal,
+ time_t DayNumber));
+static time_t NamedMonth _ANSI_ARGS_((time_t Start, time_t MonthOrdinal,
+ time_t MonthNumber));
+static int RelativeMonth _ANSI_ARGS_((time_t Start, time_t RelMonth,
+ time_t *TimePtr));
+static int RelativeDay _ANSI_ARGS_((time_t Start, time_t RelDay,
+ time_t *TimePtr));
+static int LookupWord _ANSI_ARGS_((char *buff));
+static int TclDatelex _ANSI_ARGS_((void));
+
+int
+TclDateparse _ANSI_ARGS_((void));
+typedef union
+#ifdef __cplusplus
+ YYSTYPE
+#endif
+ {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+} YYSTYPE;
+# define tAGO 257
+# define tDAY 258
+# define tDAYZONE 259
+# define tID 260
+# define tMERIDIAN 261
+# define tMINUTE_UNIT 262
+# define tMONTH 263
+# define tMONTH_UNIT 264
+# define tSTARDATE 265
+# define tSEC_UNIT 266
+# define tSNUMBER 267
+# define tUNUMBER 268
+# define tZONE 269
+# define tEPOCH 270
+# define tDST 271
+# define tISOBASE 272
+# define tDAY_UNIT 273
+# define tNEXT 274
+
+
+
+
+#if defined(__cplusplus) || defined(__STDC__)
+
+#if defined(__cplusplus) && defined(__EXTERN_C__)
+extern "C" {
+#endif
+#ifndef TclDateerror
+#if defined(__cplusplus)
+ void TclDateerror(CONST char *);
+#endif
+#endif
+#ifndef TclDatelex
+ int TclDatelex(void);
+#endif
+ int TclDateparse(void);
+#if defined(__cplusplus) && defined(__EXTERN_C__)
+}
+#endif
+
+#endif
+
+#define TclDateclearin TclDatechar = -1
+#define TclDateerrok TclDateerrflag = 0
+extern int TclDatechar;
+extern int TclDateerrflag;
+YYSTYPE TclDatelval;
+YYSTYPE TclDateval;
+typedef int TclDatetabelem;
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 150
+#endif
+#if YYMAXDEPTH > 0
+int TclDate_TclDates[YYMAXDEPTH], *TclDates = TclDate_TclDates;
+YYSTYPE TclDate_TclDatev[YYMAXDEPTH], *TclDatev = TclDate_TclDatev;
+#else /* user does initial allocation */
+int *TclDates;
+YYSTYPE *TclDatev;
+#endif
+static int TclDatemaxdepth = YYMAXDEPTH;
+# define YYERRCODE 256
+
+
+/*
+ * Month and day table.
+ */
+static TABLE MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL }
+};
+
+/*
+ * Time units table.
+ */
+static TABLE UnitsTable[] = {
+ { "year", tMONTH_UNIT, 12 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tDAY_UNIT, 14 },
+ { "week", tDAY_UNIT, 7 },
+ { "day", tDAY_UNIT, 1 },
+ { "hour", tSEC_UNIT, 60 * 60 },
+ { "minute", tSEC_UNIT, 60 },
+ { "min", tSEC_UNIT, 60 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL }
+};
+
+/*
+ * Assorted relative-time words.
+ */
+static TABLE OtherTable[] = {
+ { "tomorrow", tDAY_UNIT, 1 },
+ { "yesterday", tDAY_UNIT, -1 },
+ { "today", tDAY_UNIT, 0 },
+ { "now", tSEC_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tSEC_UNIT, 0 },
+ { "next", tNEXT, 1 },
+#if 0
+ { "first", tUNUMBER, 1 },
+ { "second", tUNUMBER, 2 },
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+#endif
+ { "ago", tAGO, 1 },
+ { "epoch", tEPOCH, 0 },
+ { "stardate", tSTARDATE, 0},
+ { NULL }
+};
+
+/*
+ * The timezone table. (Note: This table was modified to not use any floating
+ * point constants to work around an SGI compiler bug).
+ */
+static TABLE TimezoneTable[] = {
+ { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR( 0) },
+ { "uct", tZONE, HOUR( 0) }, /* Universal Coordinated Time */
+ { "wet", tZONE, HOUR( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR( 1) }, /* West Africa */
+ { "at", tZONE, HOUR( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
+#endif
+ { "nft", tZONE, HOUR( 7/2) }, /* Newfoundland */
+ { "nst", tZONE, HOUR( 7/2) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR( 7/2) }, /* Newfoundland Daylight */
+ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR(10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR(11) }, /* Nome */
+ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR( 1) }, /* Central European */
+ { "cest", tDAYZONE, -HOUR( 1) }, /* Central European Summer */
+ { "met", tZONE, -HOUR( 1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR( 1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR( 1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR( 1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR( 1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR( 1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR( 1) }, /* French Summer */
+ { "eet", tZONE, -HOUR( 2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR( 3) }, /* Baghdad, USSR Zone 2 */
+ { "it", tZONE, -HOUR( 7/2) }, /* Iran */
+ { "zp4", tZONE, -HOUR( 4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR( 5) }, /* USSR Zone 4 */
+ { "ist", tZONE, -HOUR(11/2) }, /* Indian Standard */
+ { "zp6", tZONE, -HOUR( 6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, nad SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR(13/2) }, /* North Sumatra */
+ { "sst", tZONE, -HOUR( 7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR( 7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR( 7) }, /* West Australian Daylight */
+ { "jt", tZONE, -HOUR(15/2) }, /* Java (3pm in Cronusland!) */
+ { "cct", tZONE, -HOUR( 8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR( 9) }, /* Japan Standard, USSR Zone 8 */
+ { "jdt", tDAYZONE, -HOUR( 9) }, /* Japan Daylight */
+ { "kst", tZONE, -HOUR( 9) }, /* Korea Standard */
+ { "kdt", tDAYZONE, -HOUR( 9) }, /* Korea Daylight */
+ { "cast", tZONE, -HOUR(19/2) }, /* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(19/2) }, /* Central Australian Daylight */
+ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
+ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
+ /* ADDED BY Marco Nijdam */
+ { "dst", tDST, HOUR( 0) }, /* DST on (hour is ignored) */
+ /* End ADDED */
+ { NULL }
+};
+
+/*
+ * Military timezone table.
+ */
+static TABLE MilitaryTable[] = {
+ { "a", tZONE, HOUR( 1) },
+ { "b", tZONE, HOUR( 2) },
+ { "c", tZONE, HOUR( 3) },
+ { "d", tZONE, HOUR( 4) },
+ { "e", tZONE, HOUR( 5) },
+ { "f", tZONE, HOUR( 6) },
+ { "g", tZONE, HOUR( 7) },
+ { "h", tZONE, HOUR( 8) },
+ { "i", tZONE, HOUR( 9) },
+ { "k", tZONE, HOUR( 10) },
+ { "l", tZONE, HOUR( 11) },
+ { "m", tZONE, HOUR( 12) },
+ { "n", tZONE, HOUR(- 1) },
+ { "o", tZONE, HOUR(- 2) },
+ { "p", tZONE, HOUR(- 3) },
+ { "q", tZONE, HOUR(- 4) },
+ { "r", tZONE, HOUR(- 5) },
+ { "s", tZONE, HOUR(- 6) },
+ { "t", tZONE, HOUR(- 7) },
+ { "u", tZONE, HOUR(- 8) },
+ { "v", tZONE, HOUR(- 9) },
+ { "w", tZONE, HOUR(-10) },
+ { "x", tZONE, HOUR(-11) },
+ { "y", tZONE, HOUR(-12) },
+ { "z", tZONE, HOUR( 0) },
+ { NULL }
+};
+
+
+/*
+ * Dump error messages in the bit bucket.
+ */
+static void
+TclDateerror(s)
+ char *s;
+{
+}
+
+
+static time_t
+ToSeconds(Hours, Minutes, Seconds, Meridian)
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+{
+ if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
+ return -1;
+ switch (Meridian) {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return ((Hours % 12) * 60L + Minutes) * 60L + Seconds;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return (((Hours % 12) + 12) * 60L + Minutes) * 60L + Seconds;
+ }
+ return -1; /* Should never be reached */
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Convert --
+ *
+ * Convert a {month, day, year, hours, minutes, seconds, meridian, dst}
+ * tuple into a clock seconds value.
+ *
+ * Results:
+ * 0 or -1 indicating success or failure.
+ *
+ * Side effects:
+ * Fills TimePtr with the computed value.
+ *
+ *-----------------------------------------------------------------------------
+ */
+static int
+Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode, TimePtr)
+ time_t Month;
+ time_t Day;
+ time_t Year;
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+ DSTMODE DSTmode;
+ time_t *TimePtr;
+{
+ static int DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t tod;
+ time_t Julian;
+ int i;
+
+ /* Figure out how many days are in February for the given year.
+ * Every year divisible by 4 is a leap year.
+ * But, every year divisible by 100 is not a leap year.
+ * But, every year divisible by 400 is a leap year after all.
+ */
+ DaysInMonth[1] = IsLeapYear(Year) ? 29 : 28;
+
+ /* Check the inputs for validity */
+ if (Month < 1 || Month > 12
+ || Year < START_OF_TIME || Year > END_OF_TIME
+ || Day < 1 || Day > DaysInMonth[(int)--Month])
+ return -1;
+
+ /* Start computing the value. First determine the number of days
+ * represented by the date, then multiply by the number of seconds/day.
+ */
+ for (Julian = Day - 1, i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ if (Year >= EPOCH) {
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + IsLeapYear(i);
+ } else {
+ for (i = (int)Year; i < EPOCH; i++)
+ Julian -= 365 + IsLeapYear(i);
+ }
+ Julian *= SECSPERDAY;
+
+ /* Add the timezone offset ?? */
+ Julian += TclDateTimezone * 60L;
+
+ /* Add the number of seconds represented by the time component */
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+
+ /* Perform a preliminary DST compensation ?? */
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && TclpGetDate((time_t *)&Julian, 0)->tm_isdst))
+ Julian -= 60 * 60;
+ *TimePtr = Julian;
+ return 0;
+}
+
+
+static time_t
+DSTcorrect(Start, Future)
+ time_t Start;
+ time_t Future;
+{
+ time_t StartDay;
+ time_t FutureDay;
+ StartDay = (TclpGetDate((time_t *)&Start, 0)->tm_hour + 1) % 24;
+ FutureDay = (TclpGetDate((time_t *)&Future, 0)->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+NamedDay(Start, DayOrdinal, DayNumber)
+ time_t Start;
+ time_t DayOrdinal;
+ time_t DayNumber;
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = TclpGetDate((time_t *)&now, 0);
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now);
+}
+
+static time_t
+NamedMonth(Start, MonthOrdinal, MonthNumber)
+ time_t Start;
+ time_t MonthOrdinal;
+ time_t MonthNumber;
+{
+ struct tm *tm;
+ time_t now;
+ int result;
+
+ now = Start;
+ tm = TclpGetDate((time_t *)&now, 0);
+ /* To compute the next n'th month, we use this alg:
+ * add n to year value
+ * if currentMonth < requestedMonth decrement year value by 1 (so that
+ * doing next february from january gives us february of the current year)
+ * set day to 1, time to 0
+ */
+ tm->tm_year += (int)MonthOrdinal;
+ if (tm->tm_mon < MonthNumber - 1) {
+ tm->tm_year--;
+ }
+ result = Convert(MonthNumber, (time_t) 1, tm->tm_year + TM_YEAR_BASE,
+ (time_t) 0, (time_t) 0, (time_t) 0, MER24, DSTmaybe, &now);
+ if (result < 0) {
+ return 0;
+ }
+ return DSTcorrect(Start, now);
+}
+
+static int
+RelativeMonth(Start, RelMonth, TimePtr)
+ time_t Start;
+ time_t RelMonth;
+ time_t *TimePtr;
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+ time_t Julian;
+ int result;
+
+ if (RelMonth == 0) {
+ *TimePtr = 0;
+ return 0;
+ }
+ tm = TclpGetDate((time_t *)&Start, 0);
+ Month = 12 * (tm->tm_year + TM_YEAR_BASE) + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ result = Convert(Month, (time_t) tm->tm_mday, Year,
+ (time_t) tm->tm_hour, (time_t) tm->tm_min, (time_t) tm->tm_sec,
+ MER24, DSTmaybe, &Julian);
+
+ /*
+ * The Julian time returned above is behind by one day, if "month"
+ * or "year" is used to specify relative time and the GMT flag is true.
+ * This problem occurs only when the current time is closer to
+ * midnight, the difference being not more than its time difference
+ * with GMT. For example, in US/Pacific time zone, the problem occurs
+ * whenever the current time is between midnight to 8:00am or 7:00amDST.
+ * See Bug# 413397 for more details and sample script.
+ * To resolve this bug, we simply add the number of seconds corresponding
+ * to timezone difference with GMT to Julian time, if GMT flag is true.
+ */
+
+ if (TclDateTimezone == 0) {
+ Julian += TclpGetTimeZone((unsigned long) Start) * 60L;
+ }
+
+ /*
+ * The following iteration takes into account the case were we jump
+ * into a "short month". Far example, "one month from Jan 31" will
+ * fail because there is no Feb 31. The code below will reduce the
+ * day and try converting the date until we succed or the date equals
+ * 28 (which always works unless the date is bad in another way).
+ */
+
+ while ((result != 0) && (tm->tm_mday > 28)) {
+ tm->tm_mday--;
+ result = Convert(Month, (time_t) tm->tm_mday, Year,
+ (time_t) tm->tm_hour, (time_t) tm->tm_min, (time_t) tm->tm_sec,
+ MER24, DSTmaybe, &Julian);
+ }
+ if (result != 0) {
+ return -1;
+ }
+ *TimePtr = DSTcorrect(Start, Julian);
+ return 0;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * RelativeDay --
+ *
+ * Given a starting time and a number of days before or after, compute the
+ * DST corrected difference between those dates.
+ *
+ * Results:
+ * 1 or -1 indicating success or failure.
+ *
+ * Side effects:
+ * Fills TimePtr with the computed value.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+RelativeDay(Start, RelDay, TimePtr)
+ time_t Start;
+ time_t RelDay;
+ time_t *TimePtr;
+{
+ time_t new;
+
+ new = Start + (RelDay * 60 * 60 * 24);
+ *TimePtr = DSTcorrect(Start, new);
+ return 1;
+}
+
+static int
+LookupWord(buff)
+ char *buff;
+{
+ register char *p;
+ register char *q;
+ register TABLE *tp;
+ int i;
+ int abbrev;
+
+ /*
+ * Make it lowercase.
+ */
+
+ Tcl_UtfToLower(buff);
+
+ if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
+ TclDatelval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
+ TclDatelval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /*
+ * See if we have an abbreviation for a month.
+ */
+ if (strlen(buff) == 3) {
+ abbrev = 1;
+ } else if (strlen(buff) == 4 && buff[3] == '.') {
+ abbrev = 1;
+ buff[3] = '\0';
+ } else {
+ abbrev = 0;
+ }
+
+ for (tp = MonthDayTable; tp->name; tp++) {
+ if (abbrev) {
+ if (strncmp(buff, tp->name, 3) == 0) {
+ TclDatelval.Number = tp->value;
+ return tp->type;
+ }
+ } else if (strcmp(buff, tp->name) == 0) {
+ TclDatelval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++) {
+ if (strcmp(buff, tp->name) == 0) {
+ TclDatelval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = UnitsTable; tp->name; tp++) {
+ if (strcmp(buff, tp->name) == 0) {
+ TclDatelval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /*
+ * Strip off any plural and try the units table again.
+ */
+ i = strlen(buff) - 1;
+ if (buff[i] == 's') {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++) {
+ if (strcmp(buff, tp->name) == 0) {
+ TclDatelval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ }
+
+ for (tp = OtherTable; tp->name; tp++) {
+ if (strcmp(buff, tp->name) == 0) {
+ TclDatelval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /*
+ * Military timezones.
+ */
+ if (buff[1] == '\0' && !(*buff & 0x80)
+ && isalpha(UCHAR(*buff))) { /* INTL: ISO only */
+ for (tp = MilitaryTable; tp->name; tp++) {
+ if (strcmp(buff, tp->name) == 0) {
+ TclDatelval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ }
+
+ /*
+ * Drop out any periods and try the timezone table again.
+ */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.') {
+ *p++ = *q;
+ } else {
+ i++;
+ }
+ *p = '\0';
+ if (i) {
+ for (tp = TimezoneTable; tp->name; tp++) {
+ if (strcmp(buff, tp->name) == 0) {
+ TclDatelval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ }
+
+ return tID;
+}
+
+
+static int
+TclDatelex()
+{
+ register char c;
+ register char *p;
+ char buff[20];
+ int Count;
+
+ for ( ; ; ) {
+ while (isspace(UCHAR(*TclDateInput))) {
+ TclDateInput++;
+ }
+
+ if (isdigit(UCHAR(c = *TclDateInput))) { /* INTL: digit */
+ /* convert the string into a number; count the number of digits */
+ Count = 0;
+ for (TclDatelval.Number = 0;
+ isdigit(UCHAR(c = *TclDateInput++)); ) { /* INTL: digit */
+ TclDatelval.Number = 10 * TclDatelval.Number + c - '0';
+ Count++;
+ }
+ TclDateInput--;
+ /* A number with 6 or more digits is considered an ISO 8601 base */
+ if (Count >= 6) {
+ return tISOBASE;
+ } else {
+ return tUNUMBER;
+ }
+ }
+ if (!(c & 0x80) && isalpha(UCHAR(c))) { /* INTL: ISO only. */
+ for (p = buff; isalpha(UCHAR(c = *TclDateInput++)) /* INTL: ISO only. */
+ || c == '.'; ) {
+ if (p < &buff[sizeof buff - 1]) {
+ *p++ = c;
+ }
+ }
+ *p = '\0';
+ TclDateInput--;
+ return LookupWord(buff);
+ }
+ if (c != '(') {
+ return *TclDateInput++;
+ }
+ Count = 0;
+ do {
+ c = *TclDateInput++;
+ if (c == '\0') {
+ return c;
+ } else if (c == '(') {
+ Count++;
+ } else if (c == ')') {
+ Count--;
+ }
+ } while (Count > 0);
+ }
+}
+
+/*
+ * Specify zone is of -50000 to force GMT. (This allows BST to work).
+ */
+
+int
+TclGetDate(p, now, zone, timePtr)
+ char *p;
+ Tcl_WideInt now;
+ long zone;
+ Tcl_WideInt *timePtr;
+{
+ struct tm *tm;
+ time_t Start;
+ time_t Time;
+ time_t tod;
+ int thisyear;
+
+ TclDateInput = p;
+ /* now has to be cast to a time_t for 64bit compliance */
+ Start = (time_t) now;
+ tm = TclpGetDate((time_t *) &Start, (zone == -50000));
+ thisyear = tm->tm_year + TM_YEAR_BASE;
+ TclDateYear = thisyear;
+ TclDateMonth = tm->tm_mon + 1;
+ TclDateDay = tm->tm_mday;
+ TclDateTimezone = zone;
+ if (zone == -50000) {
+ TclDateDSTmode = DSToff; /* assume GMT */
+ TclDateTimezone = 0;
+ } else {
+ TclDateDSTmode = DSTmaybe;
+ }
+ TclDateHour = 0;
+ TclDateMinutes = 0;
+ TclDateSeconds = 0;
+ TclDateMeridian = MER24;
+ TclDateRelSeconds = 0;
+ TclDateRelMonth = 0;
+ TclDateRelDay = 0;
+ TclDateRelPointer = NULL;
+
+ TclDateHaveDate = 0;
+ TclDateHaveDay = 0;
+ TclDateHaveOrdinalMonth = 0;
+ TclDateHaveRel = 0;
+ TclDateHaveTime = 0;
+ TclDateHaveZone = 0;
+
+ if (TclDateparse() || TclDateHaveTime > 1 || TclDateHaveZone > 1 || TclDateHaveDate > 1 ||
+ TclDateHaveDay > 1 || TclDateHaveOrdinalMonth > 1) {
+ return -1;
+ }
+
+ if (TclDateHaveDate || TclDateHaveTime || TclDateHaveDay) {
+ if (TclDateYear < 0) {
+ TclDateYear = -TclDateYear;
+ }
+ /*
+ * The following line handles years that are specified using
+ * only two digits. The line of code below implements a policy
+ * defined by the X/Open workgroup on the millinium rollover.
+ * Note: some of those dates may not actually be valid on some
+ * platforms. The POSIX standard startes that the dates 70-99
+ * shall refer to 1970-1999 and 00-38 shall refer to 2000-2038.
+ * This later definition should work on all platforms.
+ */
+
+ if (TclDateYear < 100) {
+ if (TclDateYear >= 69) {
+ TclDateYear += 1900;
+ } else {
+ TclDateYear += 2000;
+ }
+ }
+ if (Convert(TclDateMonth, TclDateDay, TclDateYear, TclDateHour, TclDateMinutes, TclDateSeconds,
+ TclDateMeridian, TclDateDSTmode, &Start) < 0) {
+ return -1;
+ }
+ } else {
+ Start = (time_t) now;
+ if (!TclDateHaveRel) {
+ Start -= ((tm->tm_hour * 60L * 60L) +
+ tm->tm_min * 60L) + tm->tm_sec;
+ }
+ }
+
+ Start += TclDateRelSeconds;
+ if (RelativeMonth(Start, TclDateRelMonth, &Time) < 0) {
+ return -1;
+ }
+ Start += Time;
+
+ if (RelativeDay(Start, TclDateRelDay, &Time) < 0) {
+ return -1;
+ }
+ Start += Time;
+
+ if (TclDateHaveDay && !TclDateHaveDate) {
+ tod = NamedDay(Start, TclDateDayOrdinal, TclDateDayNumber);
+ Start += tod;
+ }
+
+ if (TclDateHaveOrdinalMonth) {
+ tod = NamedMonth(Start, TclDateMonthOrdinal, TclDateMonth);
+ Start += tod;
+ }
+
+ *timePtr = Start;
+ return 0;
+}
+static CONST TclDatetabelem TclDateexca[] ={
+-1, 1,
+ 0, -1,
+ -2, 0,
+ };
+# define YYNPROD 56
+# define YYLAST 261
+static CONST TclDatetabelem TclDateact[]={
+
+ 24, 40, 23, 36, 54, 81, 41, 28, 53, 26,
+ 37, 42, 58, 38, 56, 28, 27, 26, 28, 33,
+ 26, 32, 61, 50, 27, 80, 76, 27, 51, 75,
+ 74, 73, 30, 72, 71, 70, 69, 52, 49, 48,
+ 47, 45, 39, 62, 78, 46, 79, 68, 25, 65,
+ 60, 67, 66, 55, 44, 21, 63, 11, 10, 9,
+ 8, 35, 7, 6, 5, 4, 3, 43, 2, 1,
+ 20, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 57, 0, 0, 59, 77, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 19, 14, 0, 0, 0,
+ 16, 28, 22, 26, 0, 12, 13, 17, 0, 15,
+ 27, 18, 31, 0, 0, 29, 0, 34, 28, 0,
+ 26, 0, 0, 0, 0, 0, 0, 27, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 64,
+ 64 };
+static CONST TclDatetabelem TclDatepact[]={
+
+-10000000, -43,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,
+-10000000,-10000000, -26, -268,-10000000, -259, -226,-10000000, -257, 10,
+ -227, -212, -228,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,-10000000,
+ -229,-10000000, -230, -240, -231,-10000000,-10000000, -264,-10000000, 9,
+-10000000,-10000000, -249,-10000000,-10000000, -246,-10000000, 4, -2, 2,
+ 7, 6,-10000000,-10000000, -11, -232,-10000000,-10000000,-10000000,-10000000,
+ -233,-10000000, -234, -235,-10000000, -237, -238, -239, -242,-10000000,
+-10000000,-10000000, -1,-10000000,-10000000,-10000000, -12,-10000000, -243, -263,
+-10000000,-10000000 };
+static CONST TclDatetabelem TclDatepgo[]={
+
+ 0, 48, 70, 22, 69, 68, 66, 65, 64, 63,
+ 62, 60, 59, 58, 57, 55 };
+static CONST TclDatetabelem TclDater1[]={
+
+ 0, 4, 4, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 6, 6, 6, 6, 6, 7, 7, 7,
+ 10, 10, 10, 10, 10, 8, 8, 8, 8, 8,
+ 8, 8, 8, 8, 8, 9, 9, 12, 12, 12,
+ 13, 11, 11, 15, 15, 15, 15, 15, 2, 2,
+ 1, 1, 1, 14, 3, 3 };
+static CONST TclDatetabelem TclDater2[]={
+
+ 0, 0, 4, 3, 3, 3, 3, 3, 3, 3,
+ 3, 2, 5, 9, 11, 13, 15, 5, 3, 3,
+ 3, 5, 5, 7, 5, 7, 11, 3, 11, 11,
+ 5, 9, 5, 3, 7, 5, 7, 7, 15, 5,
+ 9, 5, 2, 7, 5, 5, 7, 3, 3, 3,
+ 3, 3, 3, 3, 1, 3 };
+static CONST TclDatetabelem TclDatechk[]={
+
+-10000000, -4, -5, -6, -7, -8, -9, -10, -11, -12,
+ -13, -14, 268, 269, 259, 272, 263, 270, 274, 258,
+ -2, -15, 265, 45, 43, -1, 266, 273, 264, 261,
+ 58, 258, 47, 45, 263, -1, 271, 269, 272, 268,
+ 258, 263, 268, -1, 44, 268, 257, 268, 268, 268,
+ 263, 268, 268, 272, 268, 44, 263, -1, 258, -1,
+ 46, -3, 45, 58, 261, 47, 45, 45, 58, 268,
+ 268, 268, 268, 268, 268, 268, 268, -3, 45, 58,
+ 268, 268 };
+static CONST TclDatetabelem TclDatedef[]={
+
+ 1, -2, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 53, 18, 19, 27, 0, 33, 0, 20,
+ 0, 42, 0, 48, 49, 47, 50, 51, 52, 12,
+ 0, 22, 0, 0, 32, 44, 17, 0, 39, 30,
+ 24, 35, 0, 45, 21, 0, 41, 0, 54, 25,
+ 0, 0, 34, 37, 0, 0, 36, 46, 23, 43,
+ 0, 13, 0, 0, 55, 0, 0, 0, 0, 31,
+ 40, 14, 54, 26, 28, 29, 0, 15, 0, 0,
+ 16, 38 };
+typedef struct
+#ifdef __cplusplus
+ TclDatetoktype
+#endif
+{ char *t_name; int t_val; } TclDatetoktype;
+#ifndef YYDEBUG
+# define YYDEBUG 0 /* don't allow debugging */
+#endif
+
+#if YYDEBUG
+
+TclDatetoktype TclDatetoks[] =
+{
+ "tAGO", 257,
+ "tDAY", 258,
+ "tDAYZONE", 259,
+ "tID", 260,
+ "tMERIDIAN", 261,
+ "tMINUTE_UNIT", 262,
+ "tMONTH", 263,
+ "tMONTH_UNIT", 264,
+ "tSTARDATE", 265,
+ "tSEC_UNIT", 266,
+ "tSNUMBER", 267,
+ "tUNUMBER", 268,
+ "tZONE", 269,
+ "tEPOCH", 270,
+ "tDST", 271,
+ "tISOBASE", 272,
+ "tDAY_UNIT", 273,
+ "tNEXT", 274,
+ "-unknown-", -1 /* ends search */
+};
+
+char * TclDatereds[] =
+{
+ "-no such reduction-",
+ "spec : /* empty */",
+ "spec : spec item",
+ "item : time",
+ "item : zone",
+ "item : date",
+ "item : ordMonth",
+ "item : day",
+ "item : relspec",
+ "item : iso",
+ "item : trek",
+ "item : number",
+ "time : tUNUMBER tMERIDIAN",
+ "time : tUNUMBER ':' tUNUMBER o_merid",
+ "time : tUNUMBER ':' tUNUMBER '-' tUNUMBER",
+ "time : tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid",
+ "time : tUNUMBER ':' tUNUMBER ':' tUNUMBER '-' tUNUMBER",
+ "zone : tZONE tDST",
+ "zone : tZONE",
+ "zone : tDAYZONE",
+ "day : tDAY",
+ "day : tDAY ','",
+ "day : tUNUMBER tDAY",
+ "day : sign tUNUMBER tDAY",
+ "day : tNEXT tDAY",
+ "date : tUNUMBER '/' tUNUMBER",
+ "date : tUNUMBER '/' tUNUMBER '/' tUNUMBER",
+ "date : tISOBASE",
+ "date : tUNUMBER '-' tMONTH '-' tUNUMBER",
+ "date : tUNUMBER '-' tUNUMBER '-' tUNUMBER",
+ "date : tMONTH tUNUMBER",
+ "date : tMONTH tUNUMBER ',' tUNUMBER",
+ "date : tUNUMBER tMONTH",
+ "date : tEPOCH",
+ "date : tUNUMBER tMONTH tUNUMBER",
+ "ordMonth : tNEXT tMONTH",
+ "ordMonth : tNEXT tUNUMBER tMONTH",
+ "iso : tISOBASE tZONE tISOBASE",
+ "iso : tISOBASE tZONE tUNUMBER ':' tUNUMBER ':' tUNUMBER",
+ "iso : tISOBASE tISOBASE",
+ "trek : tSTARDATE tUNUMBER '.' tUNUMBER",
+ "relspec : relunits tAGO",
+ "relspec : relunits",
+ "relunits : sign tUNUMBER unit",
+ "relunits : tUNUMBER unit",
+ "relunits : tNEXT unit",
+ "relunits : tNEXT tUNUMBER unit",
+ "relunits : unit",
+ "sign : '-'",
+ "sign : '+'",
+ "unit : tSEC_UNIT",
+ "unit : tDAY_UNIT",
+ "unit : tMONTH_UNIT",
+ "number : tUNUMBER",
+ "o_merid : /* empty */",
+ "o_merid : tMERIDIAN",
+};
+#endif /* YYDEBUG */
+/*
+ * Copyright (c) 1993 by Sun Microsystems, Inc.
+ */
+
+
+/*
+** Skeleton parser driver for yacc output
+*/
+
+/*
+** yacc user known macros and defines
+*/
+#define YYERROR goto TclDateerrlab
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYBACKUP( newtoken, newvalue )\
+{\
+ if ( TclDatechar >= 0 || ( TclDater2[ TclDatetmp ] >> 1 ) != 1 )\
+ {\
+ TclDateerror( "syntax error - cannot backup" );\
+ goto TclDateerrlab;\
+ }\
+ TclDatechar = newtoken;\
+ TclDatestate = *TclDateps;\
+ TclDatelval = newvalue;\
+ goto TclDatenewstate;\
+}
+#define YYRECOVERING() (!!TclDateerrflag)
+#define YYNEW(type) malloc(sizeof(type) * TclDatenewmax)
+#define YYCOPY(to, from, type) \
+ (type *) memcpy(to, (char *) from, TclDatemaxdepth * sizeof (type))
+#define YYENLARGE( from, type) \
+ (type *) realloc((char *) from, TclDatenewmax * sizeof(type))
+#ifndef YYDEBUG
+# define YYDEBUG 1 /* make debugging available */
+#endif
+
+/*
+** user known globals
+*/
+int TclDatedebug; /* set to 1 to get debugging */
+
+/*
+** driver internal defines
+*/
+#define YYFLAG (-10000000)
+
+/*
+** global variables used by the parser
+*/
+YYSTYPE *TclDatepv; /* top of value stack */
+int *TclDateps; /* top of state stack */
+
+int TclDatestate; /* current state */
+int TclDatetmp; /* extra var (lasts between blocks) */
+
+int TclDatenerrs; /* number of errors */
+int TclDateerrflag; /* error recovery flag */
+int TclDatechar; /* current input token number */
+
+
+
+#ifdef YYNMBCHARS
+#define YYLEX() TclDatecvtok(TclDatelex())
+/*
+** TclDatecvtok - return a token if i is a wchar_t value that exceeds 255.
+** If i<255, i itself is the token. If i>255 but the neither
+** of the 30th or 31st bit is on, i is already a token.
+*/
+#if defined(__STDC__) || defined(__cplusplus)
+int TclDatecvtok(int i)
+#else
+int TclDatecvtok(i) int i;
+#endif
+{
+ int first = 0;
+ int last = YYNMBCHARS - 1;
+ int mid;
+ wchar_t j;
+
+ if(i&0x60000000){/*Must convert to a token. */
+ if( TclDatembchars[last].character < i ){
+ return i;/*Giving up*/
+ }
+ while ((last>=first)&&(first>=0)) {/*Binary search loop*/
+ mid = (first+last)/2;
+ j = TclDatembchars[mid].character;
+ if( j==i ){/*Found*/
+ return TclDatembchars[mid].tvalue;
+ }else if( j= 0;
+ TclDate_i++ )
+ {
+ if ( TclDatetoks[TclDate_i].t_val == TclDatechar )
+ break;
+ }
+ printf( "%s\n", TclDatetoks[TclDate_i].t_name );
+ }
+ }
+#endif /* YYDEBUG */
+ if ( ++TclDate_ps >= &TclDates[ TclDatemaxdepth ] ) /* room on stack? */
+ {
+ /*
+ ** reallocate and recover. Note that pointers
+ ** have to be reset, or bad things will happen
+ */
+ long TclDateps_index = (TclDate_ps - TclDates);
+ long TclDatepv_index = (TclDate_pv - TclDatev);
+ long TclDatepvt_index = (TclDatepvt - TclDatev);
+ int TclDatenewmax;
+#ifdef YYEXPAND
+ TclDatenewmax = YYEXPAND(TclDatemaxdepth);
+#else
+ TclDatenewmax = 2 * TclDatemaxdepth; /* double table size */
+ if (TclDatemaxdepth == YYMAXDEPTH) /* first time growth */
+ {
+ char *newTclDates = (char *)YYNEW(int);
+ char *newTclDatev = (char *)YYNEW(YYSTYPE);
+ if (newTclDates != 0 && newTclDatev != 0)
+ {
+ TclDates = YYCOPY(newTclDates, TclDates, int);
+ TclDatev = YYCOPY(newTclDatev, TclDatev, YYSTYPE);
+ }
+ else
+ TclDatenewmax = 0; /* failed */
+ }
+ else /* not first time */
+ {
+ TclDates = YYENLARGE(TclDates, int);
+ TclDatev = YYENLARGE(TclDatev, YYSTYPE);
+ if (TclDates == 0 || TclDatev == 0)
+ TclDatenewmax = 0; /* failed */
+ }
+#endif
+ if (TclDatenewmax <= TclDatemaxdepth) /* tables not expanded */
+ {
+ TclDateerror( "yacc stack overflow" );
+ YYABORT;
+ }
+ TclDatemaxdepth = TclDatenewmax;
+
+ TclDate_ps = TclDates + TclDateps_index;
+ TclDate_pv = TclDatev + TclDatepv_index;
+ TclDatepvt = TclDatev + TclDatepvt_index;
+ }
+ *TclDate_ps = TclDate_state;
+ *++TclDate_pv = TclDateval;
+
+ /*
+ ** we have a new state - find out what to do
+ */
+ TclDate_newstate:
+ if ( ( TclDate_n = TclDatepact[ TclDate_state ] ) <= YYFLAG )
+ goto TclDatedefault; /* simple state */
+#if YYDEBUG
+ /*
+ ** if debugging, need to mark whether new token grabbed
+ */
+ TclDatetmp = TclDatechar < 0;
+#endif
+ if ( ( TclDatechar < 0 ) && ( ( TclDatechar = YYLEX() ) < 0 ) )
+ TclDatechar = 0; /* reached EOF */
+#if YYDEBUG
+ if ( TclDatedebug && TclDatetmp )
+ {
+ register int TclDate_i;
+
+ printf( "Received token " );
+ if ( TclDatechar == 0 )
+ printf( "end-of-file\n" );
+ else if ( TclDatechar < 0 )
+ printf( "-none-\n" );
+ else
+ {
+ for ( TclDate_i = 0; TclDatetoks[TclDate_i].t_val >= 0;
+ TclDate_i++ )
+ {
+ if ( TclDatetoks[TclDate_i].t_val == TclDatechar )
+ break;
+ }
+ printf( "%s\n", TclDatetoks[TclDate_i].t_name );
+ }
+ }
+#endif /* YYDEBUG */
+ if ( ( ( TclDate_n += TclDatechar ) < 0 ) || ( TclDate_n >= YYLAST ) )
+ goto TclDatedefault;
+ if ( TclDatechk[ TclDate_n = TclDateact[ TclDate_n ] ] == TclDatechar ) /*valid shift*/
+ {
+ TclDatechar = -1;
+ TclDateval = TclDatelval;
+ TclDate_state = TclDate_n;
+ if ( TclDateerrflag > 0 )
+ TclDateerrflag--;
+ goto TclDate_stack;
+ }
+
+ TclDatedefault:
+ if ( ( TclDate_n = TclDatedef[ TclDate_state ] ) == -2 )
+ {
+#if YYDEBUG
+ TclDatetmp = TclDatechar < 0;
+#endif
+ if ( ( TclDatechar < 0 ) && ( ( TclDatechar = YYLEX() ) < 0 ) )
+ TclDatechar = 0; /* reached EOF */
+#if YYDEBUG
+ if ( TclDatedebug && TclDatetmp )
+ {
+ register int TclDate_i;
+
+ printf( "Received token " );
+ if ( TclDatechar == 0 )
+ printf( "end-of-file\n" );
+ else if ( TclDatechar < 0 )
+ printf( "-none-\n" );
+ else
+ {
+ for ( TclDate_i = 0;
+ TclDatetoks[TclDate_i].t_val >= 0;
+ TclDate_i++ )
+ {
+ if ( TclDatetoks[TclDate_i].t_val
+ == TclDatechar )
+ {
+ break;
+ }
+ }
+ printf( "%s\n", TclDatetoks[TclDate_i].t_name );
+ }
+ }
+#endif /* YYDEBUG */
+ /*
+ ** look through exception table
+ */
+ {
+ register CONST int *TclDatexi = TclDateexca;
+
+ while ( ( *TclDatexi != -1 ) ||
+ ( TclDatexi[1] != TclDate_state ) )
+ {
+ TclDatexi += 2;
+ }
+ while ( ( *(TclDatexi += 2) >= 0 ) &&
+ ( *TclDatexi != TclDatechar ) )
+ ;
+ if ( ( TclDate_n = TclDatexi[1] ) < 0 )
+ YYACCEPT;
+ }
+ }
+
+ /*
+ ** check for syntax error
+ */
+ if ( TclDate_n == 0 ) /* have an error */
+ {
+ /* no worry about speed here! */
+ switch ( TclDateerrflag )
+ {
+ case 0: /* new error */
+ TclDateerror( "syntax error" );
+ goto skip_init;
+ /*
+ ** get globals into registers.
+ ** we have a user generated syntax type error
+ */
+ TclDate_pv = TclDatepv;
+ TclDate_ps = TclDateps;
+ TclDate_state = TclDatestate;
+ skip_init:
+ TclDatenerrs++;
+ /* FALLTHRU */
+ case 1:
+ case 2: /* incompletely recovered error */
+ /* try again... */
+ TclDateerrflag = 3;
+ /*
+ ** find state where "error" is a legal
+ ** shift action
+ */
+ while ( TclDate_ps >= TclDates )
+ {
+ TclDate_n = TclDatepact[ *TclDate_ps ] + YYERRCODE;
+ if ( TclDate_n >= 0 && TclDate_n < YYLAST &&
+ TclDatechk[TclDateact[TclDate_n]] == YYERRCODE) {
+ /*
+ ** simulate shift of "error"
+ */
+ TclDate_state = TclDateact[ TclDate_n ];
+ goto TclDate_stack;
+ }
+ /*
+ ** current state has no shift on
+ ** "error", pop stack
+ */
+#if YYDEBUG
+# define _POP_ "Error recovery pops state %d, uncovers state %d\n"
+ if ( TclDatedebug )
+ printf( _POP_, *TclDate_ps,
+ TclDate_ps[-1] );
+# undef _POP_
+#endif
+ TclDate_ps--;
+ TclDate_pv--;
+ }
+ /*
+ ** there is no state on stack with "error" as
+ ** a valid shift. give up.
+ */
+ YYABORT;
+ case 3: /* no shift yet; eat a token */
+#if YYDEBUG
+ /*
+ ** if debugging, look up token in list of
+ ** pairs. 0 and negative shouldn't occur,
+ ** but since timing doesn't matter when
+ ** debugging, it doesn't hurt to leave the
+ ** tests here.
+ */
+ if ( TclDatedebug )
+ {
+ register int TclDate_i;
+
+ printf( "Error recovery discards " );
+ if ( TclDatechar == 0 )
+ printf( "token end-of-file\n" );
+ else if ( TclDatechar < 0 )
+ printf( "token -none-\n" );
+ else
+ {
+ for ( TclDate_i = 0;
+ TclDatetoks[TclDate_i].t_val >= 0;
+ TclDate_i++ )
+ {
+ if ( TclDatetoks[TclDate_i].t_val
+ == TclDatechar )
+ {
+ break;
+ }
+ }
+ printf( "token %s\n",
+ TclDatetoks[TclDate_i].t_name );
+ }
+ }
+#endif /* YYDEBUG */
+ if ( TclDatechar == 0 ) /* reached EOF. quit */
+ YYABORT;
+ TclDatechar = -1;
+ goto TclDate_newstate;
+ }
+ }/* end if ( TclDate_n == 0 ) */
+ /*
+ ** reduction by production TclDate_n
+ ** put stack tops, etc. so things right after switch
+ */
+#if YYDEBUG
+ /*
+ ** if debugging, print the string that is the user's
+ ** specification of the reduction which is just about
+ ** to be done.
+ */
+ if ( TclDatedebug )
+ printf( "Reduce by (%d) \"%s\"\n",
+ TclDate_n, TclDatereds[ TclDate_n ] );
+#endif
+ TclDatetmp = TclDate_n; /* value to switch over */
+ TclDatepvt = TclDate_pv; /* $vars top of value stack */
+ /*
+ ** Look in goto table for next state
+ ** Sorry about using TclDate_state here as temporary
+ ** register variable, but why not, if it works...
+ ** If TclDater2[ TclDate_n ] doesn't have the low order bit
+ ** set, then there is no action to be done for
+ ** this reduction. So, no saving & unsaving of
+ ** registers done. The only difference between the
+ ** code just after the if and the body of the if is
+ ** the goto TclDate_stack in the body. This way the test
+ ** can be made before the choice of what to do is needed.
+ */
+ {
+ /* length of production doubled with extra bit */
+ register int TclDate_len = TclDater2[ TclDate_n ];
+
+ if ( !( TclDate_len & 01 ) )
+ {
+ TclDate_len >>= 1;
+ TclDateval = ( TclDate_pv -= TclDate_len )[1]; /* $$ = $1 */
+ TclDate_state = TclDatepgo[ TclDate_n = TclDater1[ TclDate_n ] ] +
+ *( TclDate_ps -= TclDate_len ) + 1;
+ if ( TclDate_state >= YYLAST ||
+ TclDatechk[ TclDate_state =
+ TclDateact[ TclDate_state ] ] != -TclDate_n )
+ {
+ TclDate_state = TclDateact[ TclDatepgo[ TclDate_n ] ];
+ }
+ goto TclDate_stack;
+ }
+ TclDate_len >>= 1;
+ TclDateval = ( TclDate_pv -= TclDate_len )[1]; /* $$ = $1 */
+ TclDate_state = TclDatepgo[ TclDate_n = TclDater1[ TclDate_n ] ] +
+ *( TclDate_ps -= TclDate_len ) + 1;
+ if ( TclDate_state >= YYLAST ||
+ TclDatechk[ TclDate_state = TclDateact[ TclDate_state ] ] != -TclDate_n )
+ {
+ TclDate_state = TclDateact[ TclDatepgo[ TclDate_n ] ];
+ }
+ }
+ /* save until reenter driver code */
+ TclDatestate = TclDate_state;
+ TclDateps = TclDate_ps;
+ TclDatepv = TclDate_pv;
+ }
+ /*
+ ** code supplied by user is placed in this switch
+ */
+ switch( TclDatetmp )
+ {
+
+case 3:{
+ TclDateHaveTime++;
+ } break;
+case 4:{
+ TclDateHaveZone++;
+ } break;
+case 5:{
+ TclDateHaveDate++;
+ } break;
+case 6:{
+ TclDateHaveOrdinalMonth++;
+ } break;
+case 7:{
+ TclDateHaveDay++;
+ } break;
+case 8:{
+ TclDateHaveRel++;
+ } break;
+case 9:{
+ TclDateHaveTime++;
+ TclDateHaveDate++;
+ } break;
+case 10:{
+ TclDateHaveTime++;
+ TclDateHaveDate++;
+ TclDateHaveRel++;
+ } break;
+case 12:{
+ TclDateHour = TclDatepvt[-1].Number;
+ TclDateMinutes = 0;
+ TclDateSeconds = 0;
+ TclDateMeridian = TclDatepvt[-0].Meridian;
+ } break;
+case 13:{
+ TclDateHour = TclDatepvt[-3].Number;
+ TclDateMinutes = TclDatepvt[-1].Number;
+ TclDateSeconds = 0;
+ TclDateMeridian = TclDatepvt[-0].Meridian;
+ } break;
+case 14:{
+ TclDateHour = TclDatepvt[-4].Number;
+ TclDateMinutes = TclDatepvt[-2].Number;
+ TclDateMeridian = MER24;
+ TclDateDSTmode = DSToff;
+ TclDateTimezone = (TclDatepvt[-0].Number % 100 + (TclDatepvt[-0].Number / 100) * 60);
+ } break;
+case 15:{
+ TclDateHour = TclDatepvt[-5].Number;
+ TclDateMinutes = TclDatepvt[-3].Number;
+ TclDateSeconds = TclDatepvt[-1].Number;
+ TclDateMeridian = TclDatepvt[-0].Meridian;
+ } break;
+case 16:{
+ TclDateHour = TclDatepvt[-6].Number;
+ TclDateMinutes = TclDatepvt[-4].Number;
+ TclDateSeconds = TclDatepvt[-2].Number;
+ TclDateMeridian = MER24;
+ TclDateDSTmode = DSToff;
+ TclDateTimezone = (TclDatepvt[-0].Number % 100 + (TclDatepvt[-0].Number / 100) * 60);
+ } break;
+case 17:{
+ TclDateTimezone = TclDatepvt[-1].Number;
+ TclDateDSTmode = DSTon;
+ } break;
+case 18:{
+ TclDateTimezone = TclDatepvt[-0].Number;
+ TclDateDSTmode = DSToff;
+ } break;
+case 19:{
+ TclDateTimezone = TclDatepvt[-0].Number;
+ TclDateDSTmode = DSTon;
+ } break;
+case 20:{
+ TclDateDayOrdinal = 1;
+ TclDateDayNumber = TclDatepvt[-0].Number;
+ } break;
+case 21:{
+ TclDateDayOrdinal = 1;
+ TclDateDayNumber = TclDatepvt[-1].Number;
+ } break;
+case 22:{
+ TclDateDayOrdinal = TclDatepvt[-1].Number;
+ TclDateDayNumber = TclDatepvt[-0].Number;
+ } break;
+case 23:{
+ TclDateDayOrdinal = TclDatepvt[-2].Number * TclDatepvt[-1].Number;
+ TclDateDayNumber = TclDatepvt[-0].Number;
+ } break;
+case 24:{
+ TclDateDayOrdinal = 2;
+ TclDateDayNumber = TclDatepvt[-0].Number;
+ } break;
+case 25:{
+ TclDateMonth = TclDatepvt[-2].Number;
+ TclDateDay = TclDatepvt[-0].Number;
+ } break;
+case 26:{
+ TclDateMonth = TclDatepvt[-4].Number;
+ TclDateDay = TclDatepvt[-2].Number;
+ TclDateYear = TclDatepvt[-0].Number;
+ } break;
+case 27:{
+ TclDateYear = TclDatepvt[-0].Number / 10000;
+ TclDateMonth = (TclDatepvt[-0].Number % 10000)/100;
+ TclDateDay = TclDatepvt[-0].Number % 100;
+ } break;
+case 28:{
+ TclDateDay = TclDatepvt[-4].Number;
+ TclDateMonth = TclDatepvt[-2].Number;
+ TclDateYear = TclDatepvt[-0].Number;
+ } break;
+case 29:{
+ TclDateMonth = TclDatepvt[-2].Number;
+ TclDateDay = TclDatepvt[-0].Number;
+ TclDateYear = TclDatepvt[-4].Number;
+ } break;
+case 30:{
+ TclDateMonth = TclDatepvt[-1].Number;
+ TclDateDay = TclDatepvt[-0].Number;
+ } break;
+case 31:{
+ TclDateMonth = TclDatepvt[-3].Number;
+ TclDateDay = TclDatepvt[-2].Number;
+ TclDateYear = TclDatepvt[-0].Number;
+ } break;
+case 32:{
+ TclDateMonth = TclDatepvt[-0].Number;
+ TclDateDay = TclDatepvt[-1].Number;
+ } break;
+case 33:{
+ TclDateMonth = 1;
+ TclDateDay = 1;
+ TclDateYear = EPOCH;
+ } break;
+case 34:{
+ TclDateMonth = TclDatepvt[-1].Number;
+ TclDateDay = TclDatepvt[-2].Number;
+ TclDateYear = TclDatepvt[-0].Number;
+ } break;
+case 35:{
+ TclDateMonthOrdinal = 1;
+ TclDateMonth = TclDatepvt[-0].Number;
+ } break;
+case 36:{
+ TclDateMonthOrdinal = TclDatepvt[-1].Number;
+ TclDateMonth = TclDatepvt[-0].Number;
+ } break;
+case 37:{
+ if (TclDatepvt[-1].Number != HOUR(- 7)) YYABORT;
+ TclDateYear = TclDatepvt[-2].Number / 10000;
+ TclDateMonth = (TclDatepvt[-2].Number % 10000)/100;
+ TclDateDay = TclDatepvt[-2].Number % 100;
+ TclDateHour = TclDatepvt[-0].Number / 10000;
+ TclDateMinutes = (TclDatepvt[-0].Number % 10000)/100;
+ TclDateSeconds = TclDatepvt[-0].Number % 100;
+ } break;
+case 38:{
+ if (TclDatepvt[-5].Number != HOUR(- 7)) YYABORT;
+ TclDateYear = TclDatepvt[-6].Number / 10000;
+ TclDateMonth = (TclDatepvt[-6].Number % 10000)/100;
+ TclDateDay = TclDatepvt[-6].Number % 100;
+ TclDateHour = TclDatepvt[-4].Number;
+ TclDateMinutes = TclDatepvt[-2].Number;
+ TclDateSeconds = TclDatepvt[-0].Number;
+ } break;
+case 39:{
+ TclDateYear = TclDatepvt[-1].Number / 10000;
+ TclDateMonth = (TclDatepvt[-1].Number % 10000)/100;
+ TclDateDay = TclDatepvt[-1].Number % 100;
+ TclDateHour = TclDatepvt[-0].Number / 10000;
+ TclDateMinutes = (TclDatepvt[-0].Number % 10000)/100;
+ TclDateSeconds = TclDatepvt[-0].Number % 100;
+ } break;
+case 40:{
+ /*
+ * Offset computed year by -377 so that the returned years will
+ * be in a range accessible with a 32 bit clock seconds value
+ */
+ TclDateYear = TclDatepvt[-2].Number/1000 + 2323 - 377;
+ TclDateDay = 1;
+ TclDateMonth = 1;
+ TclDateRelDay += ((TclDatepvt[-2].Number%1000)*(365 + IsLeapYear(TclDateYear)))/1000;
+ TclDateRelSeconds += TclDatepvt[-0].Number * 144 * 60;
+ } break;
+case 41:{
+ TclDateRelSeconds *= -1;
+ TclDateRelMonth *= -1;
+ TclDateRelDay *= -1;
+ } break;
+case 43:{ *TclDateRelPointer += TclDatepvt[-2].Number * TclDatepvt[-1].Number * TclDatepvt[-0].Number; } break;
+case 44:{ *TclDateRelPointer += TclDatepvt[-1].Number * TclDatepvt[-0].Number; } break;
+case 45:{ *TclDateRelPointer += TclDatepvt[-0].Number; } break;
+case 46:{ *TclDateRelPointer += TclDatepvt[-1].Number * TclDatepvt[-0].Number; } break;
+case 47:{ *TclDateRelPointer += TclDatepvt[-0].Number; } break;
+case 48:{ TclDateval.Number = -1; } break;
+case 49:{ TclDateval.Number = 1; } break;
+case 50:{ TclDateval.Number = TclDatepvt[-0].Number; TclDateRelPointer = &TclDateRelSeconds; } break;
+case 51:{ TclDateval.Number = TclDatepvt[-0].Number; TclDateRelPointer = &TclDateRelDay; } break;
+case 52:{ TclDateval.Number = TclDatepvt[-0].Number; TclDateRelPointer = &TclDateRelMonth; } break;
+case 53:{
+ if (TclDateHaveTime && TclDateHaveDate && !TclDateHaveRel) {
+ TclDateYear = TclDatepvt[-0].Number;
+ } else {
+ TclDateHaveTime++;
+ if (TclDatepvt[-0].Number < 100) {
+ TclDateHour = TclDatepvt[-0].Number;
+ TclDateMinutes = 0;
+ } else {
+ TclDateHour = TclDatepvt[-0].Number / 100;
+ TclDateMinutes = TclDatepvt[-0].Number % 100;
+ }
+ TclDateSeconds = 0;
+ TclDateMeridian = MER24;
+ }
+ } break;
+case 54:{
+ TclDateval.Meridian = MER24;
+ } break;
+case 55:{
+ TclDateval.Meridian = TclDatepvt[-0].Meridian;
+ } break;
+ }
+ goto TclDatestack; /* reset registers in driver code */
+}
+
diff --git a/tclUnixTime.c b/tclUnixTime.c
new file mode 100644
index 00000000..c285365d
--- /dev/null
+++ b/tclUnixTime.c
@@ -0,0 +1,493 @@
+/*
+ * tclUnixTime.c --
+ *
+ * Contains Unix specific versions of Tcl functions that
+ * obtain time values from the operating system.
+ *
+ * Copyright (c) 1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id: tclUnixTime.c,v 1.1 2012/03/29 08:45:52 koennecke Exp $
+ */
+
+#include
+#include
+#include
+#include
+
+#define TM_YEAR_BASE 1900
+#define IsLeapYear(x) ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0))
+
+/*
+ * TclpGetDate is coded to return a pointer to a 'struct tm'. For
+ * thread safety, this structure must be in thread-specific data.
+ * The 'tmKey' variable is the key to this buffer.
+ */
+
+static Tcl_ThreadDataKey tmKey;
+typedef struct ThreadSpecificData {
+ struct tm gmtime_buf;
+ struct tm localtime_buf;
+} ThreadSpecificData;
+
+/*
+ * If we fall back on the thread-unsafe versions of gmtime and localtime,
+ * use this mutex to try to protect them.
+ */
+
+TCL_DECLARE_MUTEX(tmMutex)
+
+static char* lastTZ = NULL; /* Holds the last setting of the
+ * TZ environment variable, or an
+ * empty string if the variable was
+ * not set. */
+
+/* Static functions declared in this file */
+
+static void SetTZIfNecessary _ANSI_ARGS_((void));
+static void CleanupMemory _ANSI_ARGS_((ClientData));
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * TclpGetSeconds --
+ *
+ * This procedure returns the number of seconds from the epoch. On
+ * most Unix systems the epoch is Midnight Jan 1, 1970 GMT.
+ *
+ * Results:
+ * Number of seconds from the epoch.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+unsigned long
+TclpGetSeconds()
+{
+ return time((time_t *) NULL);
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * TclpGetClicks --
+ *
+ * This procedure returns a value that represents the highest resolution
+ * clock available on the system. There are no garantees on what the
+ * resolution will be. In Tcl we will call this value a "click". The
+ * start time is also system dependant.
+ *
+ * Results:
+ * Number of clicks from some start time.
+ *
+ * Side effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+unsigned long
+TclpGetClicks()
+{
+ unsigned long now;
+#ifdef NO_GETTOD
+ struct tms dummy;
+#else
+ struct timeval date;
+#endif
+
+#ifdef NO_GETTOD
+ now = (unsigned long) times(&dummy);
+#else
+ gettimeofday(&date, NULL);
+ now = date.tv_sec*1000000 + date.tv_usec;
+#endif
+
+ return now;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpGetTimeZone --
+ *
+ * Determines the current timezone. The method varies wildly
+ * between different platform implementations, so its hidden in
+ * this function.
+ *
+ * Results:
+ * The return value is the local time zone, measured in
+ * minutes away from GMT (-ve for east, +ve for west).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclpGetTimeZone (currentTime)
+ unsigned long currentTime;
+{
+ /*
+ * We prefer first to use the time zone in "struct tm" if the
+ * structure contains such a member. Following that, we try
+ * to locate the external 'timezone' variable and use its value.
+ * If both of those methods fail, we attempt to convert a known
+ * time to local time and use the difference from UTC as the local
+ * time zone. In all cases, we need to undo any Daylight Saving Time
+ * adjustment.
+ */
+
+#if defined(HAVE_TM_TZADJ)
+# define TCL_GOT_TIMEZONE
+
+ /* Struct tm contains tm_tzadj - that value may be used. */
+
+ time_t curTime = (time_t) currentTime;
+ struct tm *timeDataPtr = TclpLocaltime((time_t *) &curTime);
+ int timeZone;
+
+ timeZone = timeDataPtr->tm_tzadj / 60;
+ if (timeDataPtr->tm_isdst) {
+ timeZone += 60;
+ }
+
+ return timeZone;
+
+#endif
+
+#if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE)
+# define TCL_GOT_TIMEZONE
+
+ /* Struct tm contains tm_gmtoff - that value may be used. */
+
+ time_t curTime = (time_t) currentTime;
+ struct tm *timeDataPtr = TclpLocaltime((time_t *) &curTime);
+ int timeZone;
+
+ timeZone = -(timeDataPtr->tm_gmtoff / 60);
+ if (timeDataPtr->tm_isdst) {
+ timeZone += 60;
+ }
+
+ return timeZone;
+
+#endif
+
+#if defined(HAVE_TIMEZONE_VAR) && !defined(TCL_GOT_TIMEZONE) && !defined(USE_DELTA_FOR_TZ)
+# define TCL_GOT_TIMEZONE
+
+ int timeZone;
+
+ /* The 'timezone' external var is present and may be used. */
+
+ SetTZIfNecessary();
+
+ /*
+ * Note: this is not a typo in "timezone" below! See tzset
+ * documentation for details.
+ */
+
+ timeZone = timezone / 60;
+ return timeZone;
+
+#endif
+
+#if !defined(TCL_GOT_TIMEZONE)
+#define TCL_GOT_TIMEZONE 1
+ /*
+ * Fallback - determine time zone with a known reference time.
+ */
+
+ int timeZone;
+ time_t tt;
+ struct tm *stm;
+ tt = 849268800L; /* 1996-11-29 12:00:00 GMT */
+ stm = TclpLocaltime((time_t *) &tt); /* eg 1996-11-29 6:00:00 CST6CDT */
+ /* The calculation below assumes a max of +12 or -12 hours from GMT */
+ timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min);
+ if ( stm -> tm_isdst ) {
+ timeZone += 60;
+ }
+ return timeZone; /* eg +360 for CST6CDT */
+#endif
+
+#ifndef TCL_GOT_TIMEZONE
+ /*
+ * Cause compile error, we don't know how to get timezone.
+ */
+
+#error autoconf did not figure out how to determine the timezone.
+
+#endif
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_GetTime --
+ *
+ * Gets the current system time in seconds and microseconds
+ * since the beginning of the epoch: 00:00 UCT, January 1, 1970.
+ *
+ * Results:
+ * Returns the current time in timePtr.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tcl_GetTime(timePtr)
+ Tcl_Time *timePtr; /* Location to store time information. */
+{
+ struct timeval tv;
+
+ (void) gettimeofday(&tv, NULL);
+ timePtr->sec = tv.tv_sec;
+ timePtr->usec = tv.tv_usec;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpGetDate --
+ *
+ * This function converts between seconds and struct tm. If
+ * useGMT is true, then the returned date will be in Greenwich
+ * Mean Time (GMT). Otherwise, it will be in the local time zone.
+ *
+ * Results:
+ * Returns a static tm structure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+struct tm *
+TclppGetDate(time, useGMT)
+ time_t time;
+ int useGMT;
+{
+ time_t mtime = time;
+ if (useGMT) {
+ return TclpGmtime(&mtime);
+ } else {
+ return TclpLocaltime(&mtime);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpStrftime --
+ *
+ * On Unix, we can safely call the native strftime implementation,
+ * and also ignore the useGMT parameter.
+ *
+ * Results:
+ * The normal strftime result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+size_t
+TclpStrftime(s, maxsize, format, t, useGMT)
+ char *s;
+ size_t maxsize;
+ CONST char *format;
+ CONST struct tm *t;
+ int useGMT;
+{
+ if (format[0] == '%' && format[1] == 'Q') {
+ /* Format as a stardate */
+ sprintf(s, "Stardate %2d%03d.%01d",
+ (((t->tm_year + TM_YEAR_BASE) + 377) - 2323),
+ (((t->tm_yday + 1) * 1000) /
+ (365 + IsLeapYear((t->tm_year + TM_YEAR_BASE)))),
+ (((t->tm_hour * 60) + t->tm_min)/144));
+ return(strlen(s));
+ }
+ setlocale(LC_TIME, "");
+ return strftime(s, maxsize, format, t);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpGmtime --
+ *
+ * Wrapper around the 'gmtime' library function to make it thread
+ * safe.
+ *
+ * Results:
+ * Returns a pointer to a 'struct tm' in thread-specific data.
+ *
+ * Side effects:
+ * Invokes gmtime or gmtime_r as appropriate.
+ *
+ *----------------------------------------------------------------------
+ */
+
+struct tm *
+TclpGmtime( tt )
+ CONST time_t *tt;
+{
+ CONST time_t *timePtr = (CONST time_t *) tt;
+ /* Pointer to the number of seconds
+ * since the local system's epoch */
+
+ /*
+ * Get a thread-local buffer to hold the returned time.
+ */
+
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT( &tmKey );
+#ifdef HAVE_GMTIME_R
+ gmtime_r(timePtr, &( tsdPtr->gmtime_buf ));
+#else
+ Tcl_MutexLock( &tmMutex );
+ memcpy( (VOID *) &( tsdPtr->gmtime_buf ),
+ (VOID *) gmtime( timePtr ),
+ sizeof( struct tm ) );
+ Tcl_MutexUnlock( &tmMutex );
+#endif
+ return &( tsdPtr->gmtime_buf );
+}
+/*
+ * Forwarder for obsolete item in Stubs
+ */
+struct tm*
+TclpGmtime_unix( timePtr )
+ CONST time_t *timePtr;
+{
+ return TclpGmtime( timePtr );
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclpLocaltime --
+ *
+ * Wrapper around the 'localtime' library function to make it thread
+ * safe.
+ *
+ * Results:
+ * Returns a pointer to a 'struct tm' in thread-specific data.
+ *
+ * Side effects:
+ * Invokes localtime or localtime_r as appropriate.
+ *
+ *----------------------------------------------------------------------
+ */
+
+struct tm *
+TclpLocaltime( tt )
+ CONST time_t *tt;
+{
+ CONST time_t *timePtr = (CONST time_t *) tt;
+ /* Pointer to the number of seconds
+ * since the local system's epoch */
+ /*
+ * Get a thread-local buffer to hold the returned time.
+ */
+
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT( &tmKey );
+ SetTZIfNecessary();
+#ifdef HAVE_LOCALTIME_R
+ localtime_r( timePtr, &( tsdPtr->localtime_buf ) );
+#else
+ Tcl_MutexLock( &tmMutex );
+ memcpy( (VOID *) &( tsdPtr -> localtime_buf ),
+ (VOID *) localtime( timePtr ),
+ sizeof( struct tm ) );
+ Tcl_MutexUnlock( &tmMutex );
+#endif
+ return &( tsdPtr->localtime_buf );
+}
+/*
+ * Forwarder for obsolete item in Stubs
+ */
+struct tm*
+TclpLocaltime_unix( timePtr )
+ CONST time_t *timePtr;
+{
+ return TclpLocaltime( timePtr );
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetTZIfNecessary --
+ *
+ * Determines whether a call to 'tzset' is needed prior to the
+ * next call to 'localtime' or examination of the 'timezone' variable.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If 'tzset' has never been called in the current process, or if
+ * the value of the environment variable TZ has changed since the
+ * last call to 'tzset', then 'tzset' is called again.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SetTZIfNecessary() {
+
+ CONST char* newTZ = getenv( "TZ" );
+ Tcl_MutexLock(&tmMutex);
+ if ( newTZ == NULL ) {
+ newTZ = "";
+ }
+ if ( lastTZ == NULL || strcmp( lastTZ, newTZ ) ) {
+ tzset();
+ if ( lastTZ == NULL ) {
+ Tcl_CreateExitHandler( CleanupMemory, (ClientData) NULL );
+ } else {
+ Tcl_Free( lastTZ );
+ }
+ lastTZ = ckalloc( strlen( newTZ ) + 1 );
+ strcpy( lastTZ, newTZ );
+ }
+ Tcl_MutexUnlock(&tmMutex);
+
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CleanupMemory --
+ *
+ * Releases the private copy of the TZ environment variable
+ * upon exit from Tcl.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees allocated memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+CleanupMemory( ClientData ignored )
+{
+ ckfree( lastTZ );
+}
diff --git a/test/object.tcl b/test/object.tcl
index 22460fd2..7aa7e856 100644
--- a/test/object.tcl
+++ b/test/object.tcl
@@ -1,5 +1,5 @@
#
-# $Id: object.tcl,v 1.1 2006/10/20 14:57:27 koennecke Exp $
+# $Id$
#
# This software is copyright (C) 1995 by the Lawrence Berkeley Laboratory.
#
diff --git a/test/tcltest.tcl b/test/tcltest.tcl
index ef49ab4e..407c31d4 100644
--- a/test/tcltest.tcl
+++ b/test/tcltest.tcl
@@ -16,7 +16,7 @@
# Contributions from Don Porter, NIST, 2002. (not subject to US copyright)
# All rights reserved.
#
-# RCS: @(#) $Id: tcltest.tcl,v 1.1 2006/08/16 14:17:42 koennecke Exp $
+# RCS: @(#) $Id$
package require Tcl 8.3 ;# uses [glob -directory]
namespace eval tcltest {
diff --git a/trace.c b/trace.c
index db2dade1..77b24178 100644
--- a/trace.c
+++ b/trace.c
@@ -424,7 +424,7 @@ static int TraceLogTask(void *data)
memset(pBuffer, 0, sizeof(pBuffer));
strftime(pBuffer, sizeof(pBuffer)-1, "%Y-%m-%d@%H-%M-%S", psTime);
lastTen = tenmin;
- traceSys("TIMESTAMP",pBuffer);
+ traceSys("TIMESTAMP","%s",pBuffer);
}
return 1;
diff --git a/trace.h b/trace.h
index afd6d71b..24a266d5 100644
--- a/trace.h
+++ b/trace.h
@@ -18,17 +18,24 @@
* the socket number, a host:port or something appropriate
* which tells us where the stuff is going to or coming from.
*/
-void traceIO(char *id, char *format, ...);
-void traceDevice(char *id, char *format, ...);
-void traceCommand(char *id, char *format, ...);
-void tracePar(char *id, char *format, ...);
-void traceSys(char *id, char *format, ...);
+#if __GNUC__ > 2
+#define G_GNUC_PRINTF( format_idx, arg_idx ) \
+ __attribute__((__format__ (__printf__, format_idx, arg_idx)))
+#else
+#define G_GNUC_PRINTF( format_idx, arg_idx )
+#endif
+void traceIO(char *id, char *format, ...) G_GNUC_PRINTF (2, 3);
+void traceDevice(char *id, char *format, ...) G_GNUC_PRINTF (2, 3);
+void traceCommand(char *id, char *format, ...) G_GNUC_PRINTF (2, 3);
+void tracePar(char *id, char *format, ...) G_GNUC_PRINTF (2, 3);
+void traceSys(char *id, char *format, ...) G_GNUC_PRINTF (2, 3);
void traceprint(char *sub, char *id,char *data);
/**
* A debugging trace. This has to be switched on separately
*/
-void traceDebug(char *id, char *format, ...);
+void traceDebug(char *id, char *format, ...) G_GNUC_PRINTF (2, 3);
+#undef G_GNUC_PRINTF
/*
* 1 when tracing active, 0 else