/*
 * execobj.c  V2.0
 *
 * TMObject, Type: Exec
 *
 * (c) 1990-1992 Stefan Becker
 */

#include "ToolManagerLib.h"

/* extended TMObject structure for TMOBJTYPE_Exec objects */
struct TMObjectExec {
                     struct TMObject    eo_Object;
                     UWORD              eo_Type;
                     UWORD              eo_Flags;
                     char              *eo_Command;
                     char              *eo_CurrentDir;
                     LONG               eo_Delay;
                     char              *eo_HotKey;
                     char              *eo_Output;
                     char              *eo_Path;
                     LONG               eo_Priority;
                     char              *eo_PubScreen;
                     LONG               eo_Stack;
                     CxObj             *eo_CxObj;
                     struct TMLink      eo_Link;
                     struct TMTimerReq  eo_TimerReq;
                    };

/* eo_Flags */
#define EO_Arguments (1L<<0)
#define EO_ToFront   (1L<<1)

/* Tag to Flag mapping table for PackBoolTags */
static struct TagItem flagmap[]={TMOP_Arguments, EO_Arguments,
                                 TMOP_ToFront,   EO_ToFront,
                                 TAG_DONE};

/* Structure for path list */
struct PathList {
                 BPTR NextPath; /* Pointer to next PathList */
                 BPTR PathLock; /* Lock on directory */
                };

/* Create an Exec object */
struct TMObject *CreateTMObjectExec(struct TMHandle *handle, char *name,
                                    struct TagItem *tags)
{
 struct TMObjectExec *tmobj;

 /* allocate memory for object */
 if (tmobj=AllocateTMObject(sizeof(struct TMObjectExec))) {
  struct TagItem *ti,*tstate;
  BOOL noerror=TRUE;

  /* Set object defaults */
  tmobj->eo_Type=TMET_CLI;
  tmobj->eo_Command=DefaultNoName;
  tmobj->eo_CurrentDir=DefaultDirName;
  tmobj->eo_Output=DefaultOutput;
  tmobj->eo_Stack=4096;

  /* Scan tag list */
  tstate=tags;
  while (noerror && (ti=NextTagItem(&tstate))) {

   DEBUG_PRINTF("Got Tag (0x%08lx)\n",ti->ti_Tag);

   switch (ti->ti_Tag) {
    case TMOP_Command:    {
                           char *s=(char *) ti->ti_Data;
                           if (s) tmobj->eo_Command=s;
                          }
                          break;
    case TMOP_CurrentDir: {
                           char *s=(char *) ti->ti_Data;
                           if (s) tmobj->eo_CurrentDir=s;
                          }
                          break;
    case TMOP_Delay:      tmobj->eo_Delay=ti->ti_Data;
                          break;
    case TMOP_ExecType:   tmobj->eo_Type=ti->ti_Data;

                          /* Sanity check */
                          if (tmobj->eo_Type>=TMETYPES) noerror=FALSE;
                          break;
    case TMOP_HotKey:     tmobj->eo_HotKey=(char *) ti->ti_Data;
                          break;
    case TMOP_Output:     {
                           char *s=(char *) ti->ti_Data;
                           if (s) tmobj->eo_Output=s;
                          }
                          break;
    case TMOP_Path:       tmobj->eo_Path=(char *) ti->ti_Data;
                          break;
    case TMOP_Priority:   tmobj->eo_Priority=ti->ti_Data;
                          break;
    case TMOP_PubScreen:  tmobj->eo_PubScreen=(char *) ti->ti_Data;
                          break;
    case TMOP_Stack:      tmobj->eo_Stack=ti->ti_Data;
                          break;
   }
  }

  /* No errors? */
  if (noerror) {
   BOOL hotkey=tmobj->eo_HotKey!=NULL;

   /* HotKey set? Create Commodities object from HotKey description */
   if  (hotkey && (tmobj->eo_CxObj=HotKey(tmobj->eo_HotKey,BrokerPort,
                                          (ULONG) &tmobj->eo_Link))) {

    DEBUG_PRINTF("Created CxObj (0x%08lx)\n",tmobj->eo_CxObj);

    /* Attach object to broker */
    AttachCxObj(Broker,tmobj->eo_CxObj);
   }

   /* No HotKey or no HotKey error? */
   if (!hotkey || (tmobj->eo_CxObj && !CxObjError(Broker))) {
    /* No. Set flags */
    tmobj->eo_Flags=PackBoolTags(EO_Arguments,tags,flagmap);

    /* Initialize rest of structure */
    tmobj->eo_Link.tml_Linked=tmobj;
    tmobj->eo_Link.tml_Active=FALSE;
    tmobj->eo_TimerReq.tmtr_Request=*deftimereq;
    tmobj->eo_TimerReq.tmtr_Link=&tmobj->eo_Link;

    /* All OK */
    return(tmobj);
   }

   /* Free resources */
   if (tmobj->eo_CxObj) SafeDeleteCxObjAll(tmobj->eo_CxObj,&tmobj->eo_Link);
  }
  FreeMem(tmobj,sizeof(struct TMObjectExec));
 }

 /* call failed */
 return(NULL);
}

/* Delete an Exec object */
BOOL DeleteTMObjectExec(struct TMObjectExec *tmobj)
{
 DEBUG_PRINTF("Delete/Exec (0x%08lx)\n",tmobj);

 /* timer request active? */
 if (tmobj->eo_Link.tml_Active) {
  struct IORequest *tr=&tmobj->eo_TimerReq;

  /* If timer request still active, abort it */
  if (!CheckIO(tr)) AbortIO(tr);

  /* Remove timer request */
  WaitIO(tr);
  tmobj->eo_Link.tml_Active=FALSE;
 }

 /* Remove links */
 DeleteAllLinksTMObject((struct TMObject *) tmobj);

 /* Free resources */
 if (tmobj->eo_CxObj) SafeDeleteCxObjAll(tmobj->eo_CxObj,&tmobj->eo_Link);

 /* Remove object from list */
 Remove((struct Node *) tmobj);

 /* Free object */
 FreeMem(tmobj,sizeof(struct TMObjectExec));

 /* All OK. */
 return(TRUE);
}

/* Change an Exec object */
struct TMObject *ChangeTMObjectExec(struct TMHandle *handle,
                                    struct TMObjectExec *tmobj,
                                    struct TagItem *tags)
{
 struct TagItem *ti,*tstate;

 /* Scan tag list */
 tstate=tags;
 while (ti=NextTagItem(&tstate)) {

  DEBUG_PRINTF("Got Tag (0x%08lx)\n",ti->ti_Tag);

  switch (ti->ti_Tag) {
   case TMOP_Command:    {
                          char *s=(char *) ti->ti_Data;
                          if (s) tmobj->eo_Command=s;
                         }
                         break;
   case TMOP_CurrentDir: {
                          char *s=(char *) ti->ti_Data;
                          if (s) tmobj->eo_CurrentDir=s;
                         }
                         break;
   case TMOP_Delay:      tmobj->eo_Delay=ti->ti_Data;
                         break;
   case TMOP_ExecType:   /* Sanity check */
                         if ((tmobj->eo_Type>=0) && (tmobj->eo_Type<TMETYPES))
                          tmobj->eo_Type=ti->ti_Data;
                         break;
   case TMOP_HotKey:     if (tmobj->eo_CxObj) {
                          SafeDeleteCxObjAll(tmobj->eo_CxObj,&tmobj->eo_Link);
                          tmobj->eo_CxObj=NULL;
                         }

                         tmobj->eo_HotKey=(char *) ti->ti_Data;
                         break;
   case TMOP_Output:     {
                          char *s=(char *) ti->ti_Data;
                          if (s) tmobj->eo_Output=s;
                         }
                         break;
   case TMOP_Path:       tmobj->eo_Path=(char *) ti->ti_Data;
                         break;
   case TMOP_Priority:   tmobj->eo_Priority=ti->ti_Data;
                         break;
   case TMOP_PubScreen:  tmobj->eo_PubScreen=(char *) ti->ti_Data;
                         break;
   case TMOP_Stack:      tmobj->eo_Stack=ti->ti_Data;
                         break;
  }
 }

 /* HotKey set? Create Commodities object from HotKey description */
 if  (tmobj->eo_HotKey && !tmobj->eo_CxObj &&
      (tmobj->eo_CxObj=HotKey(tmobj->eo_HotKey,BrokerPort,
                              (ULONG) &tmobj->eo_Link))) {

  DEBUG_PRINTF("Created CxObj (0x%08lx)\n",tmobj->eo_CxObj);

  /* Attach object to broker */
  AttachCxObj(Broker,tmobj->eo_CxObj);

  /* Commodities error? */
  if (CxObjError(Broker)) {
   SafeDeleteCxObjAll(tmobj->eo_CxObj,&tmobj->eo_Link);
   tmobj->eo_CxObj=NULL;
  }
 }

 /* Set flags */
 tmobj->eo_Flags=PackBoolTags(tmobj->eo_Flags,tags,flagmap);

 /* All OK */
 return(TRUE);
}

/* Allocate & Initialize a TMLink structure */
struct TMLink *AllocLinkTMObjectExec(struct TMObjectExec *tmobj)
{
 struct TMLink *tml;

 /* Allocate memory for link structure */
 if (tml=AllocMem(sizeof(struct TMLink),MEMF_CLEAR|MEMF_PUBLIC))
  /* Initialize link structure */
  tml->tml_Size=sizeof(struct TMLink);

 return(tml);
}

/* Copy a path list */
BOOL CopyPathList(struct PathList **pla, struct PathList **plc,
                  struct PathList *oldpl)
{
 struct PathList *pl1=oldpl,*pl2=*plc,*pl3=NULL;

 while (pl1) {
  /* Get memory for path list entry */
  if (!(pl3 || (pl3=AllocVec(sizeof(struct PathList),MEMF_PUBLIC|MEMF_CLEAR))))
   return(FALSE); /* No more memory... */

  /* Copy path entry */
  if (pl3->PathLock=DupLock(pl1->PathLock)) {
   /* Copy successful, append new entry to list. Head of list? */
   if (*pla)
    pl2->NextPath=MKBADDR(pl3); /* No, append it to list */
   else
    *pla=pl3;                   /* Yes, set list anchor */

   /* Save pointer */
   pl2=pl3;

   /* Invalidate pointer, next time a new PathList will be allocated */
   pl3=NULL;
  }

  /* Get next path list entry */
  pl1=BADDR(pl1->NextPath);
 }

 /* Free memory */
 if (pl3) FreeVec(pl3);

 /* All OK */
 *plc=pl2; /* Save pointer to new end of list */
 return(TRUE);
}

/* Free a path list */
void FreePathList(struct PathList *pla)
{
 /* Check for NULL */
 if (pla) {
  struct PathList *pl1=pla,*pl2;

  /* Scan list */
  do {
   /* Get pointer to next entry */
   pl2=BADDR(pl1->NextPath);

   /* Free entry */
   UnLock(pl1->PathLock);
   FreeVec(pl1);
  } while (pl1=pl2);
 }
}

/* Build a path list from a string */
static BOOL BuildPathList(struct PathList **pla, struct PathList **plc, char *s)
{
 struct FileInfoBlock *fib;
 char *cp1,*cp2=s;
 struct PathList *pl1=*plc,*pl2=NULL;

 /* Get memory for FIB */
 if (!(fib=AllocVec(sizeof(struct FileInfoBlock),MEMF_PUBLIC|MEMF_CLEAR)))
  return(FALSE);

 /* For every path part */
 while (cp1=cp2) {
  /* Search next path part */
  if (cp2=strchr(cp1,',')) *cp2='\0'; /* Add string end character */

  /* Get memory for path list entry */
  if (!(pl2 ||
       (pl2=AllocVec(sizeof(struct PathList),MEMF_PUBLIC|MEMF_CLEAR)))) {
   FreeVec(fib);
   return(FALSE); /* No more memory */
  }

  /* Get lock */
  if (pl2->PathLock=Lock(cp1,SHARED_LOCK))
   /* Is it a directory? */
   if (Examine(pl2->PathLock,fib) && (fib->fib_DirEntryType>0)) {
    /* Yes, it is a directory, append it to the list. Head of list? */
    if (*pla)
     pl1->NextPath=MKBADDR(pl2); /* No, append it to list */
    else
     *pla=pl2;                   /* Yes, set list anchor */

    /* Save pointer */
    pl1=pl2;

    /* Invalidate pointer, next time a new PathList will be allocated */
    pl2=NULL;
   }
   else UnLock(pl2->PathLock); /* No, it is a file */

  /* Restore string and move to next character */
  if (cp2) *cp2++=',';
 }

 *plc=pl1;              /* Save end of list */
 if (pl2) FreeVec(pl2); /* Last Lock() failed, free memory */
 FreeVec(fib);

 /* All OK. */
 return(TRUE);
}

#define NAMELEN    256  /* Buffer length for a file name */
#define CMDLINELEN 4096 /* Buffer length for command line */

/* Build a CLI or ARexx command line (supports [] argument substitution) */
static ULONG BuildCommandLine(char *buf, char *command, BPTR curdir,
                              struct AppMessage *msg)
{
 ULONG cmdlen;      /* Command line length */
 char *lp;          /* Pointer to current cmdline pos. */
 char *com=command;

 /* Copy command name to command line */
 if (lp=strchr(command,'[')) {
  /* [] parameter place holder found, copy first part of command */
  cmdlen=lp-com;
  strncpy(buf,com,cmdlen);
  buf[cmdlen]='\0';
 }
 else {
  strcpy(buf,command); /* No [] parameter place holder */
  cmdlen=strlen(buf);
 }
 lp=buf+cmdlen;

 /* Check for arguments */
 if (msg) {
  char *dir; /* Buffer for file names */

  /* Get memory for file names */
  if (dir=AllocMem(NAMELEN,MEMF_PUBLIC)) {
   struct WBArg *wa=msg->am_ArgList;  /* Pointer to WBArgs */
   int i;                             /* Counter for WBArgs */

   for (i=msg->am_NumArgs; i; i--,wa++) {
    char *name,*space;
    ULONG namelen;

    /* Skip args which don't support locks! */
    if (!wa->wa_Lock) continue;

    /* Append a space for each parameter */
    if (cmdlen>CMDLINELEN-2) break;
    *lp++=' ';
    cmdlen++;

    /* Build parameter from Lock & name */
    DEBUG_PRINTF("wa_Name: 0x%08lx\n",wa->wa_Name);
    DEBUG_PRINTF("wa_Name: '%s'\n",wa->wa_Name);
    if (*(wa->wa_Name)!='\0')
     /* File name --> build complete path name */
     if (SameLock(curdir,wa->wa_Lock)==LOCK_SAME)
      /* File in current directory -> "<file>" */
      name=wa->wa_Name;
     else {
      /* File in other directory -> "<path><seperator><file>" */
      if (!NameFromLock(wa->wa_Lock,dir,NAMELEN)) continue;
      if (!AddPart(dir,wa->wa_Name,NAMELEN)) continue;
      name=dir;
     }
    else {
     /* No file name --> Drawer */
     if (!NameFromLock(wa->wa_Lock,dir,NAMELEN)) continue;
     name=dir;
    }
    namelen=strlen(name);

    /* Handle special case: Space in a filename */
    if (space=strchr(name,' ')) namelen+=2;

    /* Does parameter fit into commandline? */
    if (cmdlen+namelen>CMDLINELEN-2) break;

    if (space) *lp++='"';   /* Quote file name (beginning) */
    strcpy(lp,name);        /* Append parameter */
    lp+=namelen;            /* Correct pointer */
    if (space) {
     lp--;                  /* Correct pointer */
     *(lp-1)='"';           /* Quote file name (end) */
    }
    cmdlen+=namelen;        /* New command line length */

    DEBUG_PRINTF("Parameter: '%s'\n",buf);
   }
   FreeMem(dir,NAMELEN);
  }
 }

 /* Check for [] parameter place holder, Find closing bracket */
 if ((com=strchr(com,']')) && (cmdlen+strlen(++com)<CMDLINELEN-1)) {
  strcpy(lp,com);   /* Copy second part of command */
  lp=lp+strlen(lp); /* Move to end of string */
 }
 else
  *lp='\0';       /* Set string terminator */

 /* Return command line length */
 return(lp-buf);
}

/* Start CLI program */
static BOOL StartCLIProgram(struct TMObjectExec *tmobj, struct AppMessage *msg)
{
 char *cmd;     /* Buffer for command line */
 BOOL rc=FALSE;

 /* Get memory for command line */
 if (cmd=AllocMem(CMDLINELEN,MEMF_PUBLIC)) {
  BPTR newcd; /* Lock for program's current directory */

  if (newcd=Lock(tmobj->eo_CurrentDir,SHARED_LOCK)) {
   BPTR ofh; /* AmigaDOS file handle (output) */

   /* Build command line */
   BuildCommandLine(cmd,tmobj->eo_Command,newcd,msg);

   /* Open output file */
   if (ofh=Open(tmobj->eo_Output,MODE_NEWFILE)) {
    BPTR ifh; /* AmigaDOS file handle (input) */
    struct MsgPort *newct=NULL;          /* New ConsoleTask pointer */

    /* Is the output file an interactive file? */
    if (IsInteractive(ofh)) {
     struct MsgPort *oldct;  /* Old ConsoleTask pointer */

     /* Yes. We need the same file as input file for CTRL-C/D/E/F redirection */
     /* Set our ConsoleTask to the new output file, so that we can re-open it */
     newct=((struct FileHandle *) BADDR(ofh))->fh_Type;
     oldct=SetConsoleTask(newct);

     /* Open the new input file (Now ifh points to the same file as ofh) */
     ifh=Open("CONSOLE:",MODE_OLDFILE);

     /* Change back to old ConsoleTask */
     SetConsoleTask(oldct);
    }
    /* Non-interactive output, open dummy input file */
    else ifh=Open(DefaultOutput,MODE_OLDFILE);

    if (ifh) {
     struct PathList *pla=NULL,*plc=NULL;

     /* Build path list, local path first, then global path*/
     if (BuildPathList(&pla,&plc,tmobj->eo_Path) &&
         CopyPathList(&pla,&plc,GlobalPath)) {
      BPTR oldcd; /* pointer to old current directory */

      /* Go to program's current directory */
      oldcd=CurrentDir(newcd);

      /* Start program */
      if (SystemTags(cmd,SYS_Output,     ofh,
                         SYS_Input,      ifh,
                         SYS_Asynch,     TRUE, /* Run program asynchron */
                         SYS_UserShell,  TRUE, /* Use user specified shell */
                         NP_StackSize,   tmobj->eo_Stack,
                         NP_Priority,    tmobj->eo_Priority,
                         NP_Path,        MKBADDR(pla),
                         NP_ConsoleTask, newct,
                         TAG_DONE)!=-1)
       rc=TRUE; /* Program started! */

      /* Go back to old current directory */
      CurrentDir(oldcd);
     }
     if (!rc) FreePathList(pla); /* Free path list if program wasn't started */
     if (!rc) Close(ifh); /* Close input file if program wasn't started */
    }
    if (!rc) Close(ofh); /* Close output file if program wasn't started */
   }
   UnLock(newcd);
  }
  FreeMem(cmd,CMDLINELEN);
 }
 return(rc);
}

/* Start WB program */
static BOOL StartWBProgram(struct TMObjectExec *tmobj, struct AppMessage *msg)
{
 struct MsgPort *hp;     /* Port of WBStart-Handler */
 struct WBStartMsg wbsm; /* Message for WBStart-Handler */
 BOOL rc=FALSE;

 /* Build message for WBStart-Handler */
 wbsm.wbsm_Msg.mn_Node.ln_Pri= 0;
 wbsm.wbsm_Msg.mn_ReplyPort  = DummyPort;
 wbsm.wbsm_Name              = tmobj->eo_Command;
 wbsm.wbsm_DirLock           = Lock(tmobj->eo_CurrentDir,SHARED_LOCK);
 wbsm.wbsm_Stack             = tmobj->eo_Stack;
 wbsm.wbsm_Prio              = tmobj->eo_Priority;
 wbsm.wbsm_NumArgs           = msg ? msg->am_NumArgs : 0;
 wbsm.wbsm_ArgList           = msg ? msg->am_ArgList : 0;

 /* Try to send a message to the WBStart-Handler */
 Forbid();
 hp=FindPort(WBS_PORTNAME);
 if (hp) PutMsg(hp,(struct Message *) &wbsm);
 Permit();

 /* No WBStart-Handler, try to start it! */
 if (!hp) {
  BPTR ifh;

  if (ifh=Open(DefaultOutput,MODE_NEWFILE)) {
   BPTR ofh;

   if (ofh=Open(DefaultOutput,MODE_OLDFILE))
    /* Start handler */
    if (SystemTags(WBS_LOADNAME,SYS_Input,ifh,
                                SYS_Output,ofh,
                                SYS_Asynch,TRUE,
                                SYS_UserShell,TRUE,
                                NP_ConsoleTask,NULL,
                                NP_WindowPtr,NULL,
                                TAG_DONE) != -1) {
     int i;

     /* Handler started, try to send message (Retry up to 5 seconds) */
     for (i=0; i<10; i++) {
      /* Try to send message */
      Forbid();
      hp=FindPort(WBS_PORTNAME);
      if (hp) PutMsg(hp,(struct Message *) &wbsm);
      Permit();

      /* Message sent? Yes, leave loop */
      if (hp) break;

      /* No, wait 1/2 second */
      Delay(25);
     }
    }
    /* Handler not started, close file handles */
    else {
     Close(ofh);
     Close(ifh);
    }
   else Close(ifh);
  }
 }

 /* Could we send the message? */
 if (hp) {
  /* Get reply message */
  WaitPort(DummyPort);
  GetMsg(DummyPort);
  rc=wbsm.wbsm_Stack; /* Has tool been started? */
  DEBUG_PRINTF("Return Code %ld\n",rc);
 }

 /* Free lock */
 if (wbsm.wbsm_DirLock) UnLock(wbsm.wbsm_DirLock);

 return(rc);
}

/* Start ARexx program */
static BOOL StartARexxProgram(struct TMObjectExec *tmobj,
                              struct AppMessage *msg)
{
 char *cmd;     /* Buffer for command line */
 BOOL rc=FALSE;

 /* Get memory for command line */
 if (cmd=AllocMem(CMDLINELEN,MEMF_PUBLIC)) {
  BPTR newcd; /* Lock for program's current directory */

  if (newcd=Lock(tmobj->eo_CurrentDir,SHARED_LOCK)) {
   ULONG cmdlen; /* Command line length */
   BPTR oldcd;   /* Lock for old current directory */

   /* Build command line */
   cmdlen=BuildCommandLine(cmd,tmobj->eo_Command,newcd,msg);

   /* Go to program's current directory */
   oldcd=CurrentDir(newcd);

   /* Send ARexx command */
   rc=SendARexxCommand(cmd,cmdlen);

   /* Go back to old current directory */
   CurrentDir(oldcd);
   UnLock(newcd);
  }
  FreeMem(cmd,CMDLINELEN);
 }
 return(rc);
}

/* Start program */
static void StartProgram(struct TMObjectExec *tmobj, struct AppMessage *msg)
{
 struct AppMessage *args=(tmobj->eo_Flags & EO_Arguments) ? msg : NULL;
 BOOL rc=FALSE;

 /* Check for command */
 if (!tmobj->eo_Command) return; /* Nothing to do */

 /* Check for ToFront flag */
 if (tmobj->eo_Flags & EO_ToFront) {
  /* Move screen to front */
  struct Screen *s=LockPubScreen(tmobj->eo_PubScreen);

  if (s) {
   ScreenToFront(s);
   UnlockPubScreen(NULL,s);
  }
 }

 /* Check for program type */
 switch (tmobj->eo_Type) {
  case TMET_CLI:  rc=StartCLIProgram(tmobj,args);
                  break;
  case TMET_WB:   rc=StartWBProgram(tmobj,args);
                  break;
  case TMET_ARexx:rc=StartARexxProgram(tmobj,args);
                  break;
  case TMET_Hook: {
                   struct Hook *hook=(struct Hook *) tmobj->eo_Command;
                   BOOL (*Entry)(__A0 struct Hook *, __A1 struct AppMessage *,
                                 __A2 void *)=hook->h_Entry;

                   /* Call hook function. Calling conventions:          */
                   /* A0 (hook)   : pointer to hook structure           */
                   /* A1 (message): pointer to AppMessage (may be NULL) */
                   /* A2 (object) : value of hook->h_Data               */
                   /* Return Code : BOOL, FALSE for failure             */
                   rc=Entry(hook,args,hook->h_Data);
                  }
                  break;
 }

 /* Error? */
 if (!rc) DisplayBeep(NULL);
}

/* Send timer request */
static void SendTimerRequest(struct TMObjectExec *tmobj)
{
 struct timerequest *tr=&tmobj->eo_TimerReq;

 /* Initialize timer request */
 tr->tr_node.io_Command=TR_ADDREQUEST;
 tr->tr_time.tv_secs=labs(tmobj->eo_Delay);
 tr->tr_time.tv_micro=0;

 /* Send timer request */
 SendIO((struct IORequest *) tr);
 tmobj->eo_Link.tml_Active=(void *) TRUE;
}

/* Activate an Exec object */
void ActivateTMObjectExec(struct TMLink *tml, struct AppMessage *msg)
{
 struct TMObjectExec *tmobj=tml->tml_Linked;

 DEBUG_PRINTF("Activate/Exec (0x%08lx)\n",msg);

 /* Is timer active? */
 if (tmobj->eo_Link.tml_Active) {
  /* Yes. a) got timer event, b) user wants to stop last request */
  struct IORequest *tr=&tmobj->eo_TimerReq;

  /* Check timer request */
  if (CheckIO(tr)) {
   /* a) Timer event finished, remove it. */
   WaitIO(tr);
   tmobj->eo_Link.tml_Active=FALSE;

   /* Start program, no arguments */
   StartProgram(tmobj,NULL);

   /* repeat event? */
   if (tmobj->eo_Delay<0) SendTimerRequest(tmobj);
  }
  else {
   /* b) user wants to stop last request, abort it */
   AbortIO(tr);
   WaitIO(tr);
   tmobj->eo_Link.tml_Active=FALSE;
  }
 }
 /* Timer not active, start program or time request */
 else if (tmobj->eo_Delay!=0) SendTimerRequest(tmobj);

 /* Start program */
 else StartProgram(tmobj,msg);
}
