/* A package of routines to help vxWorks look more like a UNIX
   environment.
 */

#define DEFAULT_RUNTASKPRI	10
#define DEFAULT_RUNTASKSTK	0x20000

#if !defined(VXWORKS_CFG)

/* If we are not running on a vxWorks system we don't need to implement
   these functions. */

requestFlagInit(addr)
int *addr;
{
}

requestExitFxn(fxn,arg)
int (*fxn)();
int arg;
{
}

#else

#include <vxWorks.h>
#include <ctype.h>
#include <symLib.h>
#include <taskLib.h>
#include <signal.h>

#ifndef VXWORKS_V5
char *malloc();
#endif

#include "../h/cdefs.h"

extern SYMTAB *sysSymTbl;

int errno;	/* stub variable, for routines that want UNIX errno */

typedef struct initFlagDesc {
  struct initFlagDesc *next;
  int *addr;
  } IFD;

typedef struct exitFxnDesc {
  struct exitFxnDesc *next;
  int (*fxn)();
  long arg;
  } EFD;

static programFxnTaskId;
static parentFxnTaskId;

static int runTaskPri = DEFAULT_RUNTASKPRI;
static int runTaskStk = DEFAULT_RUNTASKSTK;

static deleteHook();
static IFD *getIfd();
static EFD *getEfd();
static clearInitFlags();
static runExitFxns();

static resumeHandler()
{
	int ppid;
	if (ppid = parentFxnTaskId)
	 { parentFxnTaskId = 0;
	   taskResume(ppid);
	 }
}

#ifdef VXWORKS_V5
static deleteHook(tcbx)
WIND_TCB *tcbx;
{
	if (programFxnTaskId)
	 { if (strcmp("tShell", tcbx->name) == 0)
	    { taskDelete (programFxnTaskId);
	    }
	   else if (strcmp(tcbx->name,"runCmd") == 0)
	    { 
	      printf ("\nexiting run task ...\n");
	      resumeHandler();
	      runExitFxns();
	      programFxnTaskId = 0;
	      taskDeleteHookDelete (deleteHook);
	      printf ("done\n");
	    }
	 }
}
#else
static deleteHook(tcbx)
TCBX *tcbx;
{
	if (programFxnTaskId)
	 { if (taskNameToId("shell") == tcbx->taskId)
	    { taskDelete (programFxnTaskId);
	    }
	   else if (tcbx->taskId == programFxnTaskId)
	    { 
	      printf ("\nexiting run task ...\n");
	      resumeHandler();
	      runExitFxns();
	      programFxnTaskId = 0;
	      taskDeleteHookDelete (deleteHook);
	      printf ("done\n");
	    }
	 }
}
#endif

#define ALLOC_SIZE	32

static IFD *ifdList = NULL;
static IFD *ifdFree = NULL;

static IFD *getIfd()
{
	IFD *ifd;
	int k;

	if (ifdFree == NULL)
	 { if ((ifdFree = (IFD*)malloc(sizeof(IFD)*ALLOC_SIZE))==NULL)
	    { return (NULL);
	    }
	   for (k=0; k<ALLOC_SIZE-1; k++)
	    { ifdFree[k].next = &ifdFree[k+1];
	    }
	   ifdFree[ALLOC_SIZE-1].next = NULL;
	 }
	ifd = ifdFree;
	ifdFree = ifd->next;
	return (ifd);
}

static EFD *efdList = NULL;
static EFD *efdFree = NULL;

static EFD *getEfd()
{
	EFD *efd;
	int k;

	if (efdFree == NULL)
	 { if ((efdFree = (EFD*)malloc(sizeof(EFD)*ALLOC_SIZE))==NULL)
	    { return (NULL);
	    }
	   for (k=0; k<ALLOC_SIZE-1; k++)
	    { efdFree[k].next = &efdFree[k+1];
	    }
	   efdFree[ALLOC_SIZE-1].next = NULL;
	 }
	efd = efdFree;
	efdFree = efd->next;
	return (efd);
}

setRunTaskPri(pri)
int pri;
{
	int old = runTaskPri;

	runTaskPri = (pri >= 0 ? pri : DEFAULT_RUNTASKPRI);
	return (old);
}

setRunTaskStk(stk)
int stk;
{
	int old = runTaskStk;

	runTaskStk = (stk >= 0 ? stk : DEFAULT_RUNTASKSTK);
	return (old);
}

runCmd(cmdLine)
char *cmdLine;
{
#define NAMEBUFSIZE	100

	static int argc = 0;
	static char **argv = NULL;
	char fxnName[NAMEBUFSIZE];
	char *cp, *kp;
	char *cmdBuf = NULL;
	int insideArg;
	int insideQuote;
	int k;
	int s;

	int (*fxn)();
	UTINY type;

	/* reinitialize things, if necessary */

	signal(SIGSEGV,resumeHandler);
	signal(SIGBUS,resumeHandler);

	if (cmdBuf)
	 { free(cmdBuf);
	 }
	if (argv)
	 { free(argv);
	 }
	clearInitFlags();
	errno = 0;

	if ((cmdBuf = (char*)malloc(strlen(cmdLine)+1)) == NULL)
	 { printf ("Error: out of memory\n");
	 }
	strcpy(cmdBuf,cmdLine);

	/* count up the total number of arguments */

	argc = 0;

	insideArg = insideQuote = 0;
	for (cp=cmdBuf; *cp != '\0'; cp++)
	 { if (isspace(*cp))
	    { if (insideArg && !insideQuote)
	       { insideArg = 0;
	       }
	    }
	   else
	    { if (!insideArg)
	       { insideArg = 1;
		 argc++;
	       }
	      if (*cp == '\'')
	       { insideQuote = !insideQuote;
	       }
	    }	
	 }
	if (insideQuote)
	 { printf ("Error: unterminated quotes\n");
	   goto runEnd;
	 }

	/* allocate and init argv */

	if ((argv = (char**)malloc(sizeof(char*)*argc)) == NULL)
	 { printf ("Error: out of memory\n");
	   goto runEnd;
	 }
	insideArg = insideQuote = 0;

	for (cp=cmdBuf,k=0; *cp != '\0'; cp++)
	 { if (isspace(*cp))
	    { if (insideQuote)
	       { *kp++ = *cp;
	       }
	      else if (insideArg)
	       { *kp = '\0';
		 insideArg = 0;
	       }
	    }
	   else
	    { if (!insideArg)
	       { kp = cp;
		 argv[k++] = kp;
		 insideArg = 1;
	       }
	      if (*cp == '\'')
	       { insideQuote = !insideQuote;
	       }
	      else
	       { *kp++ = *cp;
	       }
	    }	
	 }
	if (insideArg)
	 { *kp = '\0';
	 }

	/* now try to locate the function for execution */

	snprintf (fxnName, NAMEBUFSIZE, "_%s_", argv[0]);
	if (symFindByName(sysSymTbl,fxnName,(char**)&fxn,&type) == ERROR)
	 { printf ("Error: symbol %s not found\n", fxnName);
	   goto runEnd;
	 }
	ifdList = NULL;		/* paranoid */
	efdList = NULL;		/* paranoid */	
	if (taskDeleteHookAdd (deleteHook) == ERROR)
	 { printf ("Error: can't add task delete hook\n");
	   goto runEnd;
	 }
	programFxnTaskId = taskIdSelf();
	(*fxn)(argc,argv);
	s = intLock();	
	runExitFxns();
	taskDeleteHookDelete (deleteHook);
	programFxnTaskId = 0;
	intUnlock(s);
	doSignalCleanup();
runEnd:
	taskResume(parentFxnTaskId);
	return (1);
}

run(cmdLine)
char *cmdLine;
{
	extern char *errorstr();
	int tid;

	parentFxnTaskId = taskIdSelf();

	tid = taskSpawn ("runCmd", runTaskPri,
			  VX_DEALLOC_STACK | VX_FP_TASK | VX_STDIO,
			  0x20000, runCmd, cmdLine);
	if (tid == ERROR)
	 { printf ("Can't create task: %s\n", errorstr());
	   return (-1);
	 }
	/* block until the task competes */
	taskSuspend(0);	
	return (1);
}

requestFlagInit(addr)
int *addr;
{
	/* called by any routine which needs to have an init flag cleared
	   at the start of a new "program".
	 */

	IFD *ifd;
	int s;
	
	s = intLock();
	if ((ifd = getIfd()) == NULL)
	 { return (-1);
	 }
	ifd->next = ifdList;
	ifdList = ifd;
	ifd->addr = addr;
	intUnlock(s);
	return (1);
}

requestExitFxn(fxn, arg)
int (*fxn)();
int arg;
{
	/* Called by any routine which needs to have a "cleanup" function
	   called at the end of its "program".
	 */

	EFD *efd;
	int s;

	s = intLock();
	if ((efd = getEfd()) == NULL)
	 { return (-1);
	 }
	efd->next = efdList;
	efdList = efd;
	efd->fxn = fxn;
	efd->arg = arg;
	intUnlock(s);
	return (1);
}

static clearInitFlags()
{
	/* run down th init flag list and clear all requested flags */

	IFD *ifd;
	IFD *last;

	for (ifd=ifdList, last=NULL; ifd; ifd=ifd->next)
	 { *ifd->addr = 0;
	   last = ifd;
	 }
	if (last)
	 { last->next = ifdFree;
	   ifdFree = ifdList;
	   ifdList = NULL;
	 }
}

static runExitFxns()
{
	/* run down the exit fxn list and fire all requested fxns */

	EFD *efd;
	EFD *last;

	for (efd=efdList, last=NULL; efd; efd=efd->next)
	 { 
/*	   printf ("fxn 0x%x ...\n", efd->fxn); */
	   (*efd->fxn)(efd->arg);
/*	   printf ("done\n"); */
	   last = efd;
	 }
	if (last)
	 { last->next = efdFree;
	   efdFree = efdList;
	   efdList = NULL;
	 }
}

doDelay(n)
int n;
{
	int k;
	for (k=0; k<n; k++)
	 { printf ("%d ", k+1);
	   taskDelay(100);
	 }
	printf ("\n");
}

echo_(argc, argv)
int argc;
char **argv;
{
	int i;
	printf ("argc=%d\n", argc);
	for (i=0; i<argc; i++)
	 { printf ("> %s\n", argv[i]);
	 }
}

#endif
