home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / amiga / utility / misc / cyber14.lha / CyberCron / Source / CyberCron.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-30  |  61.1 KB  |  2,692 lines

  1.  /*
  2.   * CyberCron.c
  3.   * 
  4.   * Copyright ⌐ 1992 by Christopher A. Wichura (caw@miroc.chi.il.us). All rights
  5.   * reserved.
  6.   */
  7.  
  8. struct RxsLib *RexxSysBase;
  9. unsigned long ARexxLibCount = 0;
  10.  
  11.  /*
  12.   * here we have storage for the current crontab file, sendmail command and
  13.   * the name of our log file
  14.   */
  15.  
  16. UBYTE CronTabName[256];
  17. UBYTE SendMailCmd[256];
  18. UBYTE LogFile[256];
  19.  
  20.  /*
  21.   * storage for two large buffers.  we reuse these in several different
  22.   * places within CyberCron
  23.   */
  24.  
  25. UBYTE BigBufOne[BIG_BUF_SIZE];
  26. UBYTE BigBufTwo[BIG_BUF_SIZE];
  27.  
  28.  /*
  29.   * these are used by the ParseEvent() routine when no priority or stack size
  30.   * is specified.
  31.   */
  32.  
  33. ULONG DefaultStackSize = 4096;
  34. BYTE DefaultPriority = 0;
  35.  
  36.  /*
  37.   * this global is the list header for all cybernodes.  we tell who added the
  38.   * event (crontab or via a rexx command) by whether or not the CNB_CRONTAB
  39.   * bit is set in the cn_Flags field.
  40.   */
  41.  
  42. struct List EventList;
  43.  
  44. #define CYBERCRON GetString(MSG_PROGNAME)
  45.  
  46. #define ARG_TEMPLATE "CRONTAB/K,LOGFILE/K,SENDMAIL/K,DEFSTACK/K/N,DEFPRI/K/N,TOOLPRI=CRONPRI/K/N,PORTNAME/K"
  47. enum CmdlineReadArgs {
  48.     ARG_CRONTAB,
  49.     ARG_LOGFILE,
  50.     ARG_SENDMAIL,
  51.     ARG_STACK,
  52.     ARG_PRI,
  53.     ARG_CPRI,
  54.     ARG_PORTNAME,
  55.     ARG_sizeof
  56. };
  57.  
  58.  /* extern references to our version and revision numbers */
  59.  
  60. extern ULONG __far Version;
  61. extern ULONG __far Revision;
  62. extern UBYTE __far VersionID[];
  63.  
  64.  /* storage for the pointer to StdErr */
  65.  
  66. BPTR StdErr = NULL;
  67.  
  68.  /* our old task priority */
  69.  
  70. WORD OldPriority = -1;
  71.  
  72.  /* for our main ReadArgs call so we can free it later */
  73.  
  74. struct RDArgs *MyArgs = NULL;
  75. struct RDArgs *ArgsPtr = NULL;
  76. STRPTR WBArgs = NULL;
  77.  
  78.  /* stuff used in launching/destroying jobs */
  79.  
  80. struct MyPublicSema *jobSema;
  81. ULONG NumSystemJobs = 0;
  82. ULONG NumARexxJobs = 0;
  83.  
  84.  /* Semaphore to protect Log() being called under EndSystemJob() */
  85.  
  86. struct MyPublicSema *logSema;
  87.  
  88.  /* stuff for our timer port */
  89.  
  90. struct MsgPort *TimerPort = NULL;
  91. struct timerequest TimerIO;
  92. BOOL TimerUP = FALSE;
  93. BOOL DoingTimeRequest = FALSE;
  94.  
  95.  /* stuff for our notify request */
  96.  
  97. struct NotifyRequest MyNotifyRequest;
  98. BYTE NotifySignal = -1;
  99. BOOL NotifyUP = FALSE;
  100.  
  101.  /* stuff for our rexx port */
  102.  
  103. struct MsgPort *RexxPort = NULL;
  104.  
  105.  /* global flags */
  106.  
  107. BOOL BringerDown = FALSE;    /* trying to quit ? */
  108. BOOL Suspended = FALSE;        /* currently suspended ? */
  109.  
  110.  /* storage for our old pr_WindowPtr */
  111.  
  112. APTR OldWindowPtr;
  113.  
  114.  /* specifies the maximum number of jobs for each of the queues */
  115.  
  116. struct JobQueue jobQueue[27];
  117.  
  118.  /*
  119.   * flag: should ErrorMsg() try and use requesters instead of writing to
  120.   * StdErr?
  121.   */
  122.  
  123. BOOL ErrorsToStdErr = TRUE;
  124.  
  125.  /* stuff for our locale support */
  126.  
  127. extern struct LocaleBase *LocaleBase;
  128. extern struct Catalog *MyCatalog;
  129.  
  130.  /* storage for a couple library bases */
  131.  
  132. struct Library *UtilityBase = NULL;
  133. struct Library *OwnDevUnitBase = NULL;
  134.  
  135.  /* used in some date calculations */
  136.  
  137. static UBYTE DayTable[2][12] =
  138. {
  139.     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  140.     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
  141. };
  142.  
  143.  /* this is our main routine */
  144.  
  145. int __regargs main(char *cmdptr, int cmdlen, struct WBStartup * WBMsg)
  146. {
  147.     char *ArgArray[ARG_sizeof];
  148.     ULONG NSignal, TSignal, RSignal;
  149.     ULONG signals;
  150.     ULONG numJobs;
  151.     int index;
  152.     BPTR lock;
  153.  
  154.     struct timeval tr_time;
  155.  
  156. #define TextBuf BigBufOne
  157.  
  158.     OldWindowPtr = ((struct Process *) FindTask(NULL))->pr_WindowPtr;
  159.     ((struct Process *) FindTask(NULL))->pr_WindowPtr = (APTR) - 1;
  160.  
  161.     StdErr = ((struct Process *) FindTask(NULL))->pr_CES;
  162.     if (StdErr == NULL)
  163.         StdErr = Output();
  164.  
  165.     /*
  166.      * the first thing we do is try and open up the local library and
  167.      * grab our catalog as we will need it if we want to print any error
  168.      * messages or other text
  169.      */
  170.  
  171.     if (LocaleBase = (struct LocaleBase *) OpenLibrary("locale.library", 38))
  172.         MyCatalog = OpenCatalog(NULL, "CyberCron.catalog",
  173.                     OC_BuiltInLanguage, "english",
  174.                     TAG_DONE);
  175.  
  176.     /*
  177.      * if started from the Workbench then we need to make an argstring
  178.      * from our tooltypes as well as try and clone Workbench's CLI
  179.      * structure
  180.      */
  181.  
  182.     if (WBMsg) {
  183.         ErrorsToStdErr = FALSE;
  184.  
  185.         if (!(WBArgs = WBtoCLIargs(WBMsg, ARG_TEMPLATE)))
  186.             MyExit(5);
  187.  
  188.         /*
  189.          * make us into a CLI so we have a path to propigate to jobs
  190.          * we start
  191.          */
  192.         WB2CLI(WBMsg, DefaultStackSize, DOSBase);
  193.     }
  194.  
  195.     /* try and open OwnDevUnit.library */
  196.     OwnDevUnitBase = OpenLibrary(ODU_NAME, 0);
  197.  
  198.     /* try and open up utility.library */
  199.     if (!(UtilityBase = OpenLibrary("utility.library", 37))) {
  200.         ErrorMsg(MSG_COULDNT_OPEN, "utility.library");
  201.         MyExit(20);
  202.     }
  203.  
  204.     NewList(&EventList);
  205.  
  206.     if (!(jobSema = InitMyPublicSemaphore(GetString(MSG_JOB_SEMA_NAME), sizeof(UBYTE) * JOB_TABLE_SIZE))) {
  207.         ErrorMsg(MSG_NO_PUB_SEMA);
  208.         MyExit(5);
  209.     }
  210.  
  211.     if (!(logSema = InitMyPublicSemaphore(GetString(MSG_LOG_SEMA_NAME), 0))) {
  212.         ErrorMsg(MSG_NO_PUB_SEMA);
  213.         MyExit(5);
  214.     }
  215.  
  216.     /* do the stuff needed to call ReadArgs to parse the command line */
  217.     memset(ArgArray, 0, sizeof(ArgArray));
  218.  
  219.     if (!(MyArgs = (struct RDArgs *) AllocDosObject(DOS_RDARGS, TAG_DONE))) {
  220.         ErrorMsg(MSG_NO_RDARGS);
  221.         MyExit(5);
  222.     }
  223.  
  224.     if (!(MyArgs->RDA_ExtHelp = (UBYTE *) AllocVec(strlen(GetString(MSG_ARG_HELP)) + strlen(GetString(MSG_COPYRIGHT)) + (2 * strlen(CYBERCRON)) + strlen(VersionID) + 10, MEMF_CLEAR))) {
  225.         ErrorMsg(MSG_OUTOFMEM);
  226.         MyExit(5);
  227.     }
  228.  
  229.     MySPrintf((char *) MyArgs->RDA_ExtHelp, GetString(MSG_ARG_HELP), CYBERCRON, VersionID, GetString(MSG_COPYRIGHT), CYBERCRON);
  230.  
  231.     if (WBArgs) {
  232.         MyArgs->RDA_Source.CS_Buffer = WBArgs;
  233.         MyArgs->RDA_Source.CS_Length = strlen(WBArgs);
  234.         MyArgs->RDA_Source.CS_CurChr = 0L;
  235.     }
  236.  
  237.     /* now call ReadArgs to parse the command line */
  238.     ArgsPtr = ReadArgs(ARG_TEMPLATE, (LONG *) & ArgArray, MyArgs);
  239.  
  240.     /* free the memory we used for this ReadArgs() call */
  241.     FreeVec((char *) MyArgs->RDA_ExtHelp);
  242.     FreeVec(WBArgs);
  243.     WBArgs = NULL;
  244.  
  245.     if (!ArgsPtr) {
  246.         Fault(IoErr(), NULL, TextBuf, BIG_BUF_SIZE_BASE);
  247.         ErrorMsg(MSG_STRING_HACK, TextBuf);
  248.         MyExit(5);
  249.     }
  250.  
  251.     if (ArgArray[ARG_CRONTAB])
  252.         if (strlen(ArgArray[ARG_CRONTAB]) + 1 > sizeof(CronTabName)) {
  253.             ErrorMsg(MSG_CRONTAB_NAME_TOO_LONG);
  254.             MyExit(5);
  255.         }
  256.         else
  257.             strcpy(CronTabName, ArgArray[ARG_CRONTAB]);
  258.     else
  259.         strcpy(CronTabName, "S:CronTab");
  260.  
  261.     if (ArgArray[ARG_LOGFILE])
  262.         if (strlen(ArgArray[ARG_LOGFILE]) + 1 > sizeof(LogFile)) {
  263.             ErrorMsg(MSG_LOGFILE_NAME_TOO_LONG);
  264.             MyExit(5);
  265.         }
  266.         else
  267.             strcpy(LogFile, ArgArray[ARG_LOGFILE]);
  268.  
  269.     if (ArgArray[ARG_SENDMAIL])
  270.         if (strlen(ArgArray[ARG_SENDMAIL]) + 1 > sizeof(SendMailCmd)) {
  271.             ErrorMsg(MSG_SENDMAIL_COMMAND_TOO_LONG);
  272.             MyExit(5);
  273.         }
  274.         else
  275.             strcpy(SendMailCmd, ArgArray[ARG_SENDMAIL]);
  276.  
  277.     if (ArgArray[ARG_STACK])
  278.         DefaultStackSize = *((LONG *) ArgArray[ARG_STACK]);
  279.     else {
  280.  
  281.         /*
  282.          * if we have a cli attached to us then get the default stack
  283.          * size out of it.  Otherwise leave it be. WBtoCLIargs() will
  284.          * probably have set DefaultStackSize for us already in such
  285.          * a case.  If not, the hard-coded default of 4096 will be
  286.          * used
  287.          */
  288.  
  289.         struct CommandLineInterface *cli;
  290.  
  291.         if (cli = Cli())
  292.             DefaultStackSize = sizeof(LONG) * cli->cli_DefaultStack;
  293.     }
  294.  
  295.     if (DefaultStackSize < 2048)
  296.         DefaultStackSize = 2048;
  297.  
  298.     if (ArgArray[ARG_PRI])
  299.         DefaultPriority = *((LONG *) ArgArray[ARG_PRI]) & 0xFF;
  300.  
  301.     if (ArgArray[ARG_CPRI])
  302.         OldPriority = SetTaskPri(FindTask(NULL), *((LONG *) ArgArray[ARG_CPRI]) & 0xFF);
  303.  
  304.     /*
  305.      * open up our ARexx port.  Check to see if we're already using that
  306.      * port first, though.
  307.      */
  308.     Forbid();
  309.  
  310.     if (FindPort(ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON")) {
  311.         /* port already exists so fail */
  312.         Permit();
  313.         ErrorMsg(MSG_ALREADY_RUNNING, (ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"));
  314.         MyExit(5);
  315.     }
  316.     else {
  317.         if (!(RexxPort = CreatePort((ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"), 0))) {
  318.             Permit();
  319.             ErrorMsg(MSG_CANT_CREATE_AREXX_PORT);
  320.             MyExit(5);
  321.         }
  322.     }
  323.  
  324.     Permit();
  325.  
  326.     RSignal = 1L << RexxPort->mp_SigBit;
  327.  
  328.     /* open up the timer */
  329.     if (!(TimerPort = CreatePort(NULL, 0))) {
  330.         ErrorMsg(MSG_CANT_CREATE_TIMER_PORT);
  331.         MyExit(5);
  332.     }
  333.  
  334.     if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) & TimerIO, 0)) {
  335.         ErrorMsg(MSG_COULDNT_OPEN, TIMERNAME);
  336.         MyExit(5);
  337.     }
  338.  
  339.     TimerIO.tr_node.io_Message.mn_ReplyPort = TimerPort;
  340.     TSignal = 1L << TimerPort->mp_SigBit;
  341.     TimerUP = TRUE;
  342.  
  343.     if ((NotifySignal = AllocSignal(-1)) == -1) {
  344.         ErrorMsg(MSG_COULDNT_ALLOC_NOTIFY_SIG);
  345.         MyExit(5);
  346.     }
  347.  
  348.     NSignal = 1L << NotifySignal;
  349.  
  350.     memset((char *) &MyNotifyRequest, 0, sizeof(struct NotifyRequest));
  351.  
  352.     MyNotifyRequest.nr_Name = CronTabName;
  353.     MyNotifyRequest.nr_Flags = NRF_SEND_SIGNAL;
  354.     MyNotifyRequest.nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
  355.     MyNotifyRequest.nr_stuff.nr_Signal.nr_SignalNum = NotifySignal;
  356.  
  357.     if (StartNotify(&MyNotifyRequest) == DOSFALSE) {
  358.         ErrorMsg(MSG_COULDNT_START_NOTIFY);
  359.         MyExit(5);
  360.     }
  361.     NotifyUP = TRUE;
  362.  
  363.     ReadCronTab();
  364.  
  365.     /* initialize jobQueue Maximums */
  366.     for (index = 0; index < 27; index++) {
  367.         jobQueue[index].jq_Max = 1;
  368.         jobQueue[index].jq_Cur = 0;
  369.         NewList((struct List *) & jobQueue[index].jq_FIFOList);
  370.     }
  371.  
  372.     /*
  373.      * set queue 0 to 0xFFFFFFFF (infinite) so that jobs with no queue
  374.      * specified will use one with no limits
  375.      */
  376.     jobQueue[0].jq_Max = 0xFFFFFFFF;
  377.  
  378.     /* print a banner to the world saying we've started */
  379.     if (!WBMsg) {
  380.         MySPrintf(TextBuf, GetString(MSG_DAEMON_STARTED), CYBERCRON, VersionID);
  381.         PutStr(TextBuf);
  382.         PutStr("\n");
  383.         PutStr(GetString(MSG_COPYRIGHT));
  384.         Flush(Output());
  385.     }
  386.  
  387.     Log(MSG_DAEMON_STARTED, CYBERCRON, VersionID);
  388.     Log(MSG_PORTNAME, (ArgArray[ARG_PORTNAME] ? ArgArray[ARG_PORTNAME] : "CYBERCRON"));
  389.  
  390.     ErrorsToStdErr = TRUE;
  391.  
  392.     /*
  393.      * loop forever waiting for each minute and checking to see if we
  394.      * need to do anything.  also look for break, notify, etc.
  395.      * BringerDown can be set by the ARexx SHUTDOWN command so check it
  396.      * as well.
  397.      */
  398.  
  399.     for (; BringerDown == FALSE;) {
  400.         TimerIO.tr_node.io_Command = TR_ADDREQUEST;
  401.         TimerIO.tr_time.tv_micro = 0;
  402.         GetSysTime(&tr_time);
  403.         TimerIO.tr_time.tv_secs = 60 - tr_time.tv_secs % 60;
  404.         SetSignal(0L, TSignal);
  405.         SendIO((struct IORequest *) & TimerIO);
  406.         DoingTimeRequest = TRUE;
  407.  
  408.         signals = Wait(TSignal | NSignal | RSignal | SIGBREAKF_CTRL_C);
  409.  
  410.         if (signals & TSignal) {
  411.             GetMsg(TimerPort);
  412.             DoingTimeRequest = FALSE;
  413.             if (Suspended == FALSE)
  414.                 ScanForJobs();
  415.         }
  416.  
  417.         if (signals & NSignal)
  418.             if (lock = Lock(CronTabName, ACCESS_READ)) {
  419.                 FreeEvents(TRUE);
  420.  
  421.                 /*
  422.                  * not really an error, but ReadCronTab()
  423.                  * will send messages along the lines of
  424.                  * parse error in line # if they occur so we
  425.                  * spit this out to let them know why they
  426.                  * are getting these parse errors.
  427.                  */
  428.  
  429.                 ErrorMsg(MSG_CRONTAB_MODIFIED);
  430.                 Log(MSG_CRONTAB_MODIFIED);
  431.  
  432.                 ReadCronTab();
  433.                 UnLock(lock);
  434.             }
  435.  
  436.         if (signals & RSignal)
  437.             HandleRexxEvents();
  438.  
  439.         if (DoingTimeRequest) {
  440.             DoingTimeRequest = FALSE;
  441.  
  442.             Disable();
  443.             if (((struct Message *) & TimerIO)->mn_Node.ln_Type != NT_REPLYMSG)
  444.                 AbortIO((struct IORequest *) & TimerIO);
  445.             Enable();
  446.  
  447.             WaitIO((struct IORequest *) & TimerIO);
  448.         }
  449.  
  450.         if (signals & SIGBREAKF_CTRL_C)
  451.             break;
  452.     }
  453.  
  454.     BringerDown = TRUE;
  455.  
  456.     ObtainSemaphore(&jobSema->mps_Sema);
  457.     numJobs = NumSystemJobs + NumARexxJobs;
  458.     ReleaseSemaphore(&jobSema->mps_Sema);
  459.  
  460.     if (numJobs) {
  461.         ErrorMsg(MSG_WAITING_TO_QUIT);
  462.  
  463.         for (; numJobs;) {
  464.             TimerIO.tr_node.io_Command = TR_ADDREQUEST;
  465.             TimerIO.tr_time.tv_secs = 15;
  466.             TimerIO.tr_time.tv_micro = 0;
  467.             SetSignal(0L, TSignal);
  468.             SendIO((struct IORequest *) & TimerIO);
  469.             DoingTimeRequest = TRUE;
  470.  
  471.             signals = Wait(TSignal | RSignal);
  472.  
  473.             if (signals & TSignal) {
  474.                 GetMsg(TimerPort);
  475.                 DoingTimeRequest = FALSE;
  476.             }
  477.  
  478.             if (signals & RSignal)
  479.                 HandleRexxEvents();
  480.  
  481.             if (DoingTimeRequest) {
  482.                 DoingTimeRequest = FALSE;
  483.  
  484.                 Disable();
  485.                 if (((struct Message *) & TimerIO)->mn_Node.ln_Type != NT_REPLYMSG)
  486.                     AbortIO((struct IORequest *) & TimerIO);
  487.                 Enable();
  488.  
  489.                 WaitIO((struct IORequest *) & TimerIO);
  490.             }
  491.  
  492.             ObtainSemaphore(&jobSema->mps_Sema);
  493.             numJobs = NumSystemJobs + NumARexxJobs;
  494.             ReleaseSemaphore(&jobSema->mps_Sema);
  495.         }
  496.     }
  497.  
  498.     MyExit(0);
  499. }
  500.  
  501.  /* loop through the event list looking for any jobs to start */
  502.  
  503. void ScanForJobs(void)
  504. {
  505.     struct CyberNode *cn, *tcn;
  506.  
  507.     UBYTE DayOfWeek;
  508.     UWORD Month;
  509.     ULONG Day;
  510.     ULONG Hour;
  511.     ULONG Min;
  512.     BOOL TimeMatch;
  513.  
  514.     SystemTime_t st;
  515.     static ULONG LastScan = 0;
  516.  
  517.     GetSystemTime(&st, FALSE);
  518.  
  519.     /*
  520.      * figure out if the time has gone backwards.  This could happen if
  521.      * the clock was reloaded, etc.  We use a 5 minute threshhold.  If it
  522.      * goes back farther than that then we assume the user knows what
  523.      * they are doing.
  524.      */
  525.     {
  526.         ULONG MSSC = st.st_tvsecs / 60;    /* MSSC stands for Minutes
  527.                          * Since System Creation */
  528.  
  529.         if (MSSC >= (LastScan - 5) && MSSC <= LastScan)
  530.             return;
  531.         else
  532.             LastScan = MSSC;
  533.     }
  534.  
  535.     /* initilize the bit fields for us to do comparisons against */
  536.     DayOfWeek = 1L << st.st_DOW;
  537.     Month = 1L << st.st_Month;
  538.     Day = 1L << st.st_Day;
  539.     Hour = 1L << st.st_Hour;
  540.     if (st.st_Min > 31)
  541.         Min = 1L << (st.st_Min - 32);
  542.     else
  543.         Min = 1L << st.st_Min;
  544.  
  545.     /* loop through the list looking for events to do */
  546.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  547.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
  548.  
  549.         TimeMatch = FALSE;
  550.  
  551.         if (cn->cn_DayOfWeek & DayOfWeek)
  552.             if (cn->cn_Month & Month)
  553.                 if (cn->cn_Day & Day)
  554.                     if (cn->cn_Hour & Hour)
  555.                         if ((st.st_Min > 31 && cn->cn_HiMin & Min) ||
  556.                             (st.st_Min < 32 && cn->cn_LoMin & Min))
  557.                             TimeMatch = TRUE;
  558.  
  559.         ObtainSemaphore(&jobSema->mps_Sema);
  560.  
  561.         if (TimeMatch || cn->cn_DelayedCount) {
  562.             if (ProcessQueue(cn, TimeMatch)) {
  563.                 if (cn->cn_Flags & CNF_REXX)
  564.                     StartRexxJob(cn);
  565.                 else
  566.                     StartSystemJob(cn);
  567.  
  568.                 if (cn->cn_Flags & CNF_EXECONE) {
  569.                     tcn = (struct CyberNode *) cn->cn_Node.ln_Pred;
  570.                     DeleteEvent(cn);
  571.                     cn = tcn;
  572.                 }
  573.             }
  574.         }
  575.  
  576.         ReleaseSemaphore(&jobSema->mps_Sema);
  577.     }
  578. }
  579.  
  580.  /*
  581.   * this routine will decide if we can run the job now or if it is to be
  582.   * delayed because we are currently at the maximum number of jobs allowed
  583.   * for the specified queue.  it serializes the delayed jobs on the queue by
  584.   * using a fifo mechanism which is maintained here
  585.   */
  586.  
  587. BOOL ProcessQueue(struct CyberNode * cn, BOOL TimeMatch)
  588. {
  589.     struct QueueFIFO *qf;
  590.     BOOL RetVal;
  591.     BOOL AddFIFO;
  592.  
  593.     AddFIFO = RetVal = FALSE;
  594.  
  595.     if (jobQueue[cn->cn_ObeyQueue].jq_Cur >= jobQueue[cn->cn_ObeyQueue].jq_Max) {
  596.         if (TimeMatch)
  597.             AddFIFO = TRUE;
  598.         else
  599.             return FALSE;
  600.     }
  601.  
  602.     if (!AddFIFO) {
  603.         if (cn->cn_DelayedCount) {
  604.             qf = (struct QueueFIFO *) RemHead((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList);
  605.  
  606.             if (qf->qf_CyberNode == cn) {
  607.                 RetVal = TRUE;
  608.                 cn->cn_DelayedCount--;
  609.                 FreeVec(qf);
  610.             }
  611.             else {
  612.                 AddHead((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList, (struct Node *) qf);
  613.             }
  614.  
  615.             /*
  616.              * if we were delayed but we would also start a job
  617.              * at this time anyway, we need to queue a new job to
  618.              * occur
  619.              */
  620.             if (TimeMatch)
  621.                 AddFIFO = TRUE;
  622.         }
  623.         else if (TimeMatch)
  624.             RetVal = TRUE;
  625.     }
  626.  
  627.     if (AddFIFO) {
  628.         if (qf = (struct QueueFIFO *) AllocVec(sizeof(struct QueueFIFO), MEMF_CLEAR)) {
  629.             qf->qf_CyberNode = cn;
  630.             AddTail((struct List *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList, (struct Node *) qf);
  631.             cn->cn_DelayedCount++;
  632.         }
  633.     }
  634.  
  635.     return RetVal;
  636. }
  637.  
  638.  
  639. void HandleRexxEvents(void)
  640. {
  641.     struct RexxMsg *msg;
  642.  
  643. /*    this is the table of ARexx commands that CyberCron knows.  the
  644.     format is as follows:
  645.  
  646.     1) a short that is the length of the command name
  647.     2) a pointer to the command name
  648.     3) a short to descibe the args to pass to the function.
  649.         value 0 = no args
  650.         value 1 = pointer to string after command name
  651.         value 2 = an integer
  652.         value 3 = pointer to the current ARexx message
  653.     4) a short to describe the return value from the function
  654.         value 0 = no returns, set rc to zero
  655.         value 1 = return an argstring
  656.         value 2 = return integer in rc
  657.         value 3 = return an argstring already in argstring format
  658.     5) a pointer to the function
  659. */
  660.  
  661. #define NUMRXCMDS 22
  662.     static struct {
  663.         short len;
  664.         char *RxCmd;
  665.         short args;
  666.         short ret;
  667.         APTR func;
  668.     } CmdTbl[NUMRXCMDS] = {
  669.  
  670.         /* indent makes this look so ugly...  sigh... */
  671.  
  672.         {
  673.             8, "SHUTDOWN", 0, 0, (APTR) & rx_Shutdown
  674.         },
  675.         {
  676.             4, "QUIT", 0, 0, (APTR) & rx_Shutdown
  677.         },
  678.         {
  679.             7, "VERSION", 0, 1, (APTR) & rx_Version
  680.         },
  681.         {
  682.             7, "SUSPEND", 0, 0, (APTR) & rx_Suspend
  683.         },
  684.         {
  685.             6, "RESUME", 0, 0, (APTR) & rx_Resume
  686.         },
  687.         {
  688.             14, "NEW_EVENT_FILE", 1, 2, (APTR) & rx_NewEventFile
  689.         },
  690.         {
  691.             16, "CLOSE_EVENT_FILE", 0, 0, (APTR) & rx_CloseEventFile
  692.         },
  693.         {
  694.             9, "ADD_EVENT", 1, 2, (APTR) & rx_AddEvent
  695.         },
  696.         {
  697.             11, "SHOW_STATUS", 0, 1, (APTR) & rx_ShowStatus
  698.         },
  699.         {
  700.             17, "PURGE_REXX_EVENTS", 0, 0, (APTR) & rx_PurgeRexxEvents
  701.         },
  702.         {
  703.             17, "DELETE_REXX_EVENT", 1, 2, (APTR) & rx_DeleteRexxEvent
  704.         },
  705.         {
  706.             12, "DELETE_EVENT", 1, 2, (APTR) & rx_DeleteEvent
  707.         },
  708.         {
  709.             11, "LIST_EVENTS", 0, 3, (APTR) & rx_ListEvents
  710.         },
  711.         {
  712.             10, "SHOW_EVENT", 1, 1, (APTR) & rx_ShowEvent
  713.         },
  714.         {
  715.             12, "NEW_LOG_FILE", 1, 2, (APTR) & rx_NewLogFile
  716.         },
  717.         {
  718.             14, "CLOSE_LOG_FILE", 0, 0, (APTR) & rx_CloseLogFile
  719.         },
  720.         {
  721.             13, "SET_QUEUE_MAX", 1, 2, (APTR) & rx_SetQueueMax
  722.         },
  723.         {
  724.             13, "GET_QUEUE_MAX", 1, 2, (APTR) & rx_GetQueueMax
  725.         },
  726.         {
  727.             15, "EVENT_NEXT_EXEC", 1, 1, (APTR) & rx_EventNextExec
  728.         },
  729.         {
  730.             18, "NEXT_EVENT_TO_EXEC", 0, 1, (APTR) & rx_NextEventToExec
  731.         },
  732.         {
  733.             11, "EXPAND_SSSC", 2, 1, (APTR) & rx_ExpandSSSC
  734.         },
  735.         {
  736.             13, "SSSC_TO_ASCII", 2, 1, (APTR) & rx_SSSCtoASCII
  737.         }
  738.     };
  739.  
  740.     /*
  741.      * if we can't get ahold of the rexx system library then we spin
  742.      * emptying our message port by replying to everything. shouldn't
  743.      * happen, but if some idiot tries sending us messages when they
  744.      * don't have ARexx then its better safe than sorry
  745.      */
  746.  
  747.     if (GetARexxLib() == FALSE) {
  748.         ErrorMsg(MSG_CANT_HANDLE_REXX_EVENT, RXSNAME);
  749.         Log(MSG_CANT_HANDLE_REXX_EVENT, RXSNAME);
  750.  
  751.         while ((msg = (struct RexxMsg *) GetMsg(RexxPort)))
  752.             ReplyMsg((struct Message *) msg);
  753.  
  754.         return;
  755.     }
  756.  
  757.     /*
  758.      * we've got the ARexx library so spin on our port looking for
  759.      * messages. if its a reply then a command/string we launched has
  760.      * finished and ARexx is returning its results to us.  Otherwise,
  761.      * it's a command we are to execute so call DoMsg() to dispatch it
  762.      */
  763.  
  764.     while ((msg = (struct RexxMsg *) GetMsg(RexxPort)))
  765.         if (msg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
  766.             if (!msg->rm_Args[3])
  767.                 Log(MSG_JOB_ENDED, (UWORD) msg->rm_Args[2]);
  768.  
  769.             /*
  770.              * when a system job can't find the command it wanted
  771.              * to execute, the shell automatically prints out
  772.              * that it couldn't find it or whatever the error
  773.              * might happen to be.  Thus, the user is informed of
  774.              * this based on wherever they may have redirected
  775.              * output.  ARexx, on the other hand, does not do
  776.              * this for us. So here I have duplicated what the RX
  777.              * activator prints out when it can't start a job.
  778.              * Note that ARexxErrorMsg() is a hack defined in
  779.              * CyberCron.h since this isn't normally available
  780.              * from the standard C= includes
  781.              */
  782.  
  783.             if (msg->rm_Result1)
  784.                 if (msg->rm_Result2)
  785.                     FPrintf(msg->rm_Stdout, GetString(MSG_AREXX_RETURNED_2), msg->rm_Result1,
  786.                         msg->rm_Result2, (ARexxErrorMsg(msg->rm_Result2))->ns_Buff);
  787.                 else
  788.                     FPrintf(msg->rm_Stdout, GetString(MSG_AREXX_RETURNED_1), msg->rm_Result1);
  789.  
  790.             Close(msg->rm_Stdout);
  791.             Close(msg->rm_Stdin);
  792.             FreeJobNum((UWORD) msg->rm_Args[2]);
  793.             ObtainSemaphore(&jobSema->mps_Sema);
  794.             jobQueue[(int) msg->rm_Args[4]].jq_Cur--;
  795.             ReleaseSemaphore(&jobSema->mps_Sema);
  796.             if (msg->rm_Args[1])
  797.                 DeleteArgstring(msg->rm_Args[1]);
  798.             DeleteArgstring(msg->rm_Args[0]);
  799.             DeleteRexxMsg(msg);
  800.             NumARexxJobs--;
  801.         }
  802.         else
  803.             DoMsg(msg, (APTR) & CmdTbl, NUMRXCMDS, BringerDown);
  804.  
  805.     FreeARexxLib();
  806. }
  807.  
  808. void rx_Shutdown(void)
  809. {
  810.     BringerDown = TRUE;
  811. }
  812.  
  813. STRPTR rx_Version(void)
  814. {
  815.     return VersionID;
  816. }
  817.  
  818. void rx_Suspend(void)
  819. {
  820.     Suspended = TRUE;
  821. }
  822.  
  823. void rx_Resume(void)
  824. {
  825.     Suspended = FALSE;
  826. }
  827.  
  828. int rx_NewEventFile(STRPTR name)
  829. {
  830.     BPTR lock;
  831.  
  832.     if (lock = Lock(name, ACCESS_READ)) {
  833.         if (strlen(name) + 1 > sizeof(CronTabName)) {
  834.             ErrorMsg(MSG_CRONTAB_NAME_TOO_LONG);
  835.             UnLock(lock);
  836.             return RC_WARN;
  837.         }
  838.  
  839.         FreeEvents(TRUE);
  840.  
  841.         if (NotifyUP) {
  842.             EndNotify(&MyNotifyRequest);
  843.             NotifyUP = FALSE;
  844.         }
  845.  
  846.         strcpy(CronTabName, name);
  847.  
  848.         memset((char *) &MyNotifyRequest, 0, sizeof(struct NotifyRequest));
  849.  
  850.         MyNotifyRequest.nr_Name = CronTabName;
  851.         MyNotifyRequest.nr_Flags = NRF_SEND_SIGNAL;
  852.         MyNotifyRequest.nr_stuff.nr_Signal.nr_Task = FindTask(NULL);
  853.         MyNotifyRequest.nr_stuff.nr_Signal.nr_SignalNum = NotifySignal;
  854.  
  855.         if (StartNotify(&MyNotifyRequest) == DOSFALSE) {
  856.             ErrorMsg(MSG_COULDNT_START_NOTIFY);
  857.             UnLock(lock);
  858.             strcpy(CronTabName, "<None>");
  859.             return RC_ERROR;
  860.         }
  861.         NotifyUP = TRUE;
  862.  
  863.         /* again, not really an error */
  864.         ErrorMsg(MSG_CRONTAB_CHANGED);
  865.         Log(MSG_CRONTAB_CHANGED);
  866.  
  867.         ReadCronTab();
  868.         UnLock(lock);
  869.         return RC_OK;
  870.     }
  871.  
  872.     return RC_WARN;
  873. }
  874.  
  875.  
  876. void rx_CloseEventFile(void)
  877. {
  878.     FreeEvents(TRUE);
  879.     EndNotify(&MyNotifyRequest);
  880.     NotifyUP = FALSE;
  881.     strcpy(CronTabName, "<None>");
  882. }
  883.  
  884. int rx_NewLogFile(STRPTR name)
  885. {
  886.     if (strlen(name) + 1 > sizeof(LogFile)) {
  887.         ErrorMsg(MSG_LOGFILE_NAME_TOO_LONG);
  888.         return RC_WARN;
  889.     }
  890.  
  891.     ObtainSemaphore(&logSema->mps_Sema);
  892.     strcpy(LogFile, name);
  893.     ReleaseSemaphore(&logSema->mps_Sema);
  894.  
  895.     return RC_OK;
  896. }
  897.  
  898. void rx_CloseLogFile(void)
  899. {
  900.     ObtainSemaphore(&logSema->mps_Sema);
  901.     LogFile[0] = '\0';
  902.     ReleaseSemaphore(&logSema->mps_Sema);
  903. }
  904.  
  905. STRPTR rx_ShowStatus(void)
  906. {
  907.     MySPrintf(BigBufOne, "%s \"%s\" \"%s\"",
  908.           (Suspended ? "SUSPENDED" : "ACTIVE"), CronTabName,
  909.           (LogFile[0] ? LogFile : (UBYTE *) "<None>"));
  910.  
  911.     return BigBufOne;
  912. }
  913.  
  914. void rx_PurgeRexxEvents(void)
  915. {
  916.     FreeEvents(FALSE);
  917. }
  918.  
  919. int rx_AddEvent(STRPTR event)
  920. {
  921.     struct CyberNode *cn;
  922.  
  923.     if (cn = ParseEvent(event)) {
  924.         AddTail(&EventList, (struct Node *) cn);
  925.         return RC_OK;
  926.     }
  927.     else
  928.         return RC_ERROR;
  929. }
  930.  
  931. int rx_DeleteRexxEvent(STRPTR name)
  932. {
  933.     struct CyberNode *cn;
  934.  
  935.     if ((cn = FindEvent(name)) && !(cn->cn_Flags & CNF_CRONTAB)) {
  936.         DeleteEvent(cn);
  937.         return RC_OK;
  938.     }
  939.  
  940.     return RC_ERROR;
  941. }
  942.  
  943. int rx_DeleteEvent(STRPTR name)
  944. {
  945.     struct CyberNode *cn;
  946.  
  947.     if (cn = FindEvent(name)) {
  948.         DeleteEvent(cn);
  949.         return RC_OK;
  950.     }
  951.  
  952.     return RC_ERROR;
  953. }
  954.  
  955. STRPTR rx_ListEvents(void)
  956. {
  957.     struct CyberNode *cn;
  958.     STRPTR string, string2;
  959.     ULONG num;
  960.  
  961.     num = 0;
  962.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  963.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ)
  964.         num++;
  965.  
  966.     if (num == 0)
  967.         return CreateArgstring("<None>", 6);
  968.  
  969.     if (!(string = CreateArgstring(NULL, num * 11)))
  970.         return NULL;
  971.  
  972.     string2 = string;
  973.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  974.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
  975.         MySPrintf(string2, "0x%08lx ", cn);
  976.         string2 += 11;
  977.     }
  978.  
  979.     *--string2 = '\0';
  980.  
  981.     return string;
  982. }
  983.  
  984. #define rxSE_Buf BigBufOne
  985.  
  986. STRPTR rx_ShowEvent(STRPTR name)
  987. {
  988.     struct CyberNode *cn;
  989.     STRPTR ptr;
  990.     ULONG Bits[2];
  991.  
  992.     if (!(cn = FindEvent(name)))
  993.         return NULL;
  994.  
  995.     MySPrintf(rxSE_Buf, "0x%08lx ", cn);
  996.     ptr = &rxSE_Buf[11];
  997.  
  998.     if (cn->cn_Name) {
  999.         MySPrintf(ptr, ":NAME %s ", cn->cn_Name);
  1000.         ptr += strlen(ptr);
  1001.     }
  1002.  
  1003.     Bits[0] = cn->cn_LoMin, Bits[1] = cn->cn_HiMin;
  1004.     UnParseBits(Bits, ptr, 0, 59);
  1005.     ptr += strlen(ptr);
  1006.     *ptr++ = ' ';
  1007.  
  1008.     Bits[1] = 0;
  1009.  
  1010.     Bits[0] = cn->cn_Hour;
  1011.     UnParseBits(Bits, ptr, 0, 23);
  1012.     ptr += strlen(ptr);
  1013.     *ptr++ = ' ';
  1014.  
  1015.     Bits[0] = cn->cn_Day;
  1016.     UnParseBits(Bits, ptr, 1, 31);
  1017.     ptr += strlen(ptr);
  1018.     *ptr++ = ' ';
  1019.  
  1020.     Bits[0] = cn->cn_Month;
  1021.     UnParseBits(Bits, ptr, 1, 12);
  1022.     ptr += strlen(ptr);
  1023.     *ptr++ = ' ';
  1024.  
  1025.     Bits[0] = cn->cn_DayOfWeek;
  1026.     UnParseBits(Bits, ptr, 0, 6);
  1027.     ptr += strlen(ptr);
  1028.     *ptr++ = ' ';
  1029.  
  1030.     if (cn->cn_Flags & CNF_EXECONE) {
  1031.         strcpy(ptr, ":EXECONCE ");
  1032.         ptr += 10;
  1033.     }
  1034.  
  1035.     if (cn->cn_Flags & CNF_NOLOG) {
  1036.         strcpy(ptr, ":NOLOG ");
  1037.         ptr += 7;
  1038.     }
  1039.  
  1040.     if (cn->cn_Flags & CNF_REXX) {
  1041.         strcpy(ptr, ":REXX ");
  1042.         ptr += 6;
  1043.     }
  1044.     else {
  1045.         if (cn->cn_Stack != DefaultStackSize) {
  1046.             MySPrintf(ptr, ":STACK %ld ", cn->cn_Stack);
  1047.             ptr += strlen(ptr);
  1048.         }
  1049.  
  1050.         if (cn->cn_Priority != DefaultPriority) {
  1051.             MySPrintf(ptr, ":PRI %ld ", cn->cn_Priority);
  1052.             ptr += strlen(ptr);
  1053.         }
  1054.  
  1055.         if (cn->cn_CustomShell) {
  1056.             MySPrintf(ptr, ":CUSTOMSH %s ", cn->cn_CustomShell);
  1057.             ptr += strlen(ptr);
  1058.         }
  1059.         else if (cn->cn_Flags & CNF_SYSSH) {
  1060.             strcpy(ptr, ":SYSSH ");
  1061.             ptr += 7;
  1062.         }
  1063.     }
  1064.  
  1065.     strcpy(ptr, cn->cn_Command);
  1066.     ptr += strlen(ptr);
  1067.     *ptr++ = ' ';
  1068.  
  1069.     if (cn->cn_Args) {
  1070.         strcpy(ptr, cn->cn_Args);
  1071.         ptr += strlen(ptr);
  1072.         *ptr++ = ' ';
  1073.     }
  1074.  
  1075.     if (cn->cn_ReDirIn) {
  1076.         MySPrintf(ptr, "< %s ", cn->cn_ReDirIn);
  1077.         ptr += strlen(ptr);
  1078.     }
  1079.  
  1080.     if (cn->cn_SendToUser && SendMailCmd[0]) {
  1081.         MySPrintf(ptr, ":MAILUSER %s ", cn->cn_SendToUser);
  1082.         ptr += strlen(ptr);
  1083.     }
  1084.     else {
  1085.         if (cn->cn_ReDirOut) {
  1086.             MySPrintf(ptr, "%s %s ", (cn->cn_Flags & CNF_OUTAPP ? ">>" : ">"), cn->cn_ReDirOut);
  1087.             ptr += strlen(ptr);
  1088.         }
  1089.     }
  1090.  
  1091.     if (cn->cn_ReDirErr) {
  1092.         MySPrintf(ptr, "2%s %s ", (cn->cn_Flags & CNF_ERRAPP ? ">>" : ">"), cn->cn_ReDirErr);
  1093.         ptr += strlen(ptr);
  1094.     }
  1095.  
  1096.     if (cn->cn_ObeyQueue) {
  1097.         MySPrintf(ptr, ":OBEYQUEUE %lc ", cn->cn_ObeyQueue + 'a' - 1);
  1098.         ptr += 13;
  1099.     }
  1100.  
  1101.     *--ptr = '\0';
  1102.  
  1103.     return rxSE_Buf;
  1104. }
  1105.  
  1106. int rx_SetQueueMax(STRPTR argline)
  1107. {
  1108.     int queueNum;
  1109.  
  1110.     if (isalpha(*argline)) {
  1111.         queueNum = tolower(*argline) - 'a' + 1;
  1112.  
  1113.         argline++;
  1114.  
  1115.         while (isspace(*argline++)) ;
  1116.  
  1117.         if (*--argline) {
  1118.             jobQueue[queueNum].jq_Max = atol(argline);
  1119.             return RC_OK;
  1120.         }
  1121.     }
  1122.  
  1123.     return RC_ERROR;
  1124. }
  1125.  
  1126. int rx_GetQueueMax(STRPTR argline)
  1127. {
  1128.     int queueNum;
  1129.  
  1130.     if (isalpha(*argline)) {
  1131.         queueNum = tolower(*argline) - 'a' + 1;
  1132.         return (int) jobQueue[queueNum].jq_Max;
  1133.     }
  1134.     else
  1135.         return -1;
  1136. }
  1137.  
  1138. STRPTR rx_EventNextExec(STRPTR name)
  1139. {
  1140.     struct CyberNode *cn;
  1141.     SystemTime_t st;
  1142.  
  1143.     if (!(cn = FindEvent(name)))
  1144.         return NULL;
  1145.  
  1146.     GetSystemTime(&st, FALSE);
  1147.  
  1148.     MySPrintf(BigBufOne, "%ld", EventNextExecutes(cn, &st));
  1149.  
  1150.     return BigBufOne;
  1151. }
  1152.  
  1153. STRPTR rx_NextEventToExec(void)
  1154. {
  1155.     struct CyberNode *cn, *bestcn;
  1156.     ULONG secs, bestsecs;
  1157.     SystemTime_t st;
  1158.  
  1159.     GetSystemTime(&st, FALSE);
  1160.  
  1161.     bestcn = (struct CyberNode *) NULL;
  1162.     bestsecs = -1;
  1163.  
  1164.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  1165.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
  1166.         secs = EventNextExecutes(cn, &st);
  1167.         if (secs && secs < bestsecs) {
  1168.             bestcn = cn;
  1169.             bestsecs = secs;
  1170.         }
  1171.     }
  1172.  
  1173.     if (bestcn) {
  1174.         MySPrintf(BigBufOne, "0x%08lx %ld", bestcn, bestsecs);
  1175.         return BigBufOne;
  1176.     }
  1177.     else
  1178.         return "<None>";
  1179. }
  1180.  
  1181. STRPTR rx_ExpandSSSC(int SSSC)
  1182. {
  1183.     SystemTime_t st;
  1184.  
  1185.     st.st_tvsecs = SSSC;
  1186.  
  1187.     GetSystemTime(&st, TRUE);
  1188.  
  1189.     MySPrintf(BigBufOne, "%ld %ld %ld %ld %ld %ld %ld", st.st_Sec, st.st_Min,
  1190.           st.st_Hour, st.st_Day, st.st_Month, st.st_Year, st.st_DOW);
  1191.  
  1192.     return BigBufOne;
  1193. }
  1194.  
  1195. STRPTR rx_SSSCtoASCII(int SSSC)
  1196. {
  1197.     struct DateTime dat;
  1198.     UBYTE Day[LEN_DATSTRING + 1];
  1199.     UBYTE Date[LEN_DATSTRING + 1];
  1200.     UBYTE Time[LEN_DATSTRING + 1];
  1201.  
  1202.     dat.dat_Stamp.ds_Days = SSSC / SECSINDAY, SSSC %= SECSINDAY;
  1203.     dat.dat_Stamp.ds_Minute = SSSC / SECSINMINUTE, SSSC %= SECSINMINUTE;
  1204.     dat.dat_Stamp.ds_Tick = SSSC * TICKS_PER_SECOND;
  1205.  
  1206.     dat.dat_Format = FORMAT_DOS;
  1207.     dat.dat_Flags = 0;
  1208.     dat.dat_StrDay = Day;
  1209.     dat.dat_StrDate = Date;
  1210.     dat.dat_StrTime = Time;
  1211.  
  1212.     memset(Day, 0, LEN_DATSTRING + 1);
  1213.     memset(Date, 0, LEN_DATSTRING + 1);
  1214.     memset(Time, 0, LEN_DATSTRING + 1);
  1215.     DateToStr(&dat);
  1216.  
  1217.     MySPrintf(BigBufOne, "%s %s %s", Day, Date, Time);
  1218.  
  1219.     return BigBufOne;
  1220. }
  1221.  
  1222. STRPTR WBtoCLIargs(struct WBStartup * WBMsg, STRPTR ArgTemplate)
  1223. {
  1224.     UBYTE *Argline, *ArglineSave, *SourcePtr, *DestPtr;
  1225.     struct Library *IconBase;
  1226.     UBYTE tempChar;
  1227.     BOOL sawEqual;
  1228.     int index;
  1229.     ULONG size;
  1230.     BPTR oldDir;
  1231.     struct DiskObject *dob;
  1232.  
  1233.     if (!(IconBase = OpenLibrary("icon.library", 37)))
  1234.         return NULL;
  1235.  
  1236.     oldDir = CurrentDir(WBMsg->sm_ArgList[0].wa_Lock);
  1237.  
  1238.     if (!(dob = GetDiskObjectNew(WBMsg->sm_ArgList[0].wa_Name))) {
  1239.         CurrentDir(oldDir);
  1240.         CloseLibrary(IconBase);
  1241.         return NULL;
  1242.     }
  1243.  
  1244.     /*
  1245.      * if there are tooltypes then figure out how much memory we need to
  1246.      * allocate to hold them as a command line.  anything that isn't a
  1247.      * legal argument in our command line template is ignored.  This lets
  1248.      * things like DONOTWAIT be used without causing an error at
  1249.      * ReadArgs() time.
  1250.      */
  1251.  
  1252.     if (dob->do_ToolTypes)
  1253.         for (size = index = 0; dob->do_ToolTypes[index]; index++) {
  1254.             SourcePtr = dob->do_ToolTypes[index];
  1255.             DestPtr = BigBufOne;
  1256.  
  1257.             while (tempChar = *SourcePtr++)
  1258.                 if (tempChar == '=')
  1259.                     break;
  1260.                 else
  1261.                     *DestPtr++ = tempChar;
  1262.  
  1263.             *DestPtr = '\0';
  1264.  
  1265.             if (FindArg(ArgTemplate, BigBufOne) != -1)
  1266.                 size += strlen(dob->do_ToolTypes[index]) + 3;
  1267.         }
  1268.     else
  1269.         size = 0;
  1270.  
  1271.     if (Argline = AllocVec(size + 2, MEMF_CLEAR)) {
  1272.         ArglineSave = Argline;
  1273.  
  1274.         if (dob->do_ToolTypes)
  1275.             for (index = 0; dob->do_ToolTypes[index]; index++) {
  1276.                 SourcePtr = dob->do_ToolTypes[index];
  1277.                 DestPtr = BigBufOne;
  1278.                 sawEqual = FALSE;
  1279.  
  1280.                 while (tempChar = *SourcePtr++)
  1281.                     if (tempChar == '=') {
  1282.                         sawEqual = TRUE;
  1283.                         break;
  1284.                     }
  1285.                     else
  1286.                         *DestPtr++ = tempChar;
  1287.  
  1288.                 *DestPtr = '\0';
  1289.  
  1290.                 if (FindArg(ArgTemplate, BigBufOne) != -1) {
  1291.                     CopyMem(BigBufOne, Argline, DestPtr - BigBufOne);
  1292.                     Argline += DestPtr - BigBufOne;
  1293.  
  1294.                     /*
  1295.                      * if we saw an equals sign when we
  1296.                      * broke the argument name out then
  1297.                      * we know we need to copy the
  1298.                      * argument's data over.  if we
  1299.                      * didn't see it then this argument
  1300.                      * is a switch and thus has no more
  1301.                      * data to copy over
  1302.                      */
  1303.  
  1304.                     if (sawEqual) {
  1305.                         *Argline++ = ' ';
  1306.                         *Argline++ = '\"';
  1307.  
  1308.                         while (tempChar = *SourcePtr++)
  1309.                             *Argline++ = tempChar;
  1310.  
  1311.                         *Argline++ = '\"';
  1312.                     }
  1313.  
  1314.                     *Argline++ = ' ';
  1315.                 }
  1316.             }
  1317.  
  1318.         *Argline++ = '\n';
  1319.         *Argline = '\0';
  1320.     }
  1321.     else
  1322.         ArglineSave = NULL;
  1323.  
  1324.     if (dob->do_StackSize)
  1325.         DefaultStackSize = dob->do_StackSize;
  1326.  
  1327.     FreeDiskObject(dob);
  1328.     CurrentDir(oldDir);
  1329.     CloseLibrary(IconBase);
  1330.     return ArglineSave;
  1331. }
  1332.  
  1333. void MyExit(int error)
  1334. {
  1335.     if (OldPriority != -1)
  1336.         SetTaskPri(FindTask(NULL), OldPriority);
  1337.  
  1338.     if (ARexxLibCount)
  1339.         CloseLibrary((struct Library *) RexxSysBase);
  1340.  
  1341.     FreeEvents(TRUE);
  1342.     FreeEvents(FALSE);
  1343.  
  1344.     if (NotifyUP)
  1345.         EndNotify(&MyNotifyRequest);
  1346.  
  1347.     if (NotifySignal != -1)
  1348.         FreeSignal(NotifySignal);
  1349.  
  1350.     if (TimerUP) {
  1351.         if (DoingTimeRequest) {
  1352.             Disable();
  1353.             if (((struct Message *) & TimerIO)->mn_Node.ln_Type != NT_REPLYMSG)
  1354.                 AbortIO((struct IORequest *) & TimerIO);
  1355.             Enable();
  1356.  
  1357.             WaitIO((struct IORequest *) & TimerIO);
  1358.         }
  1359.  
  1360.         CloseDevice((struct IORequest *) & TimerIO);
  1361.     }
  1362.  
  1363.     if (TimerPort)
  1364.         DeletePort(TimerPort);
  1365.  
  1366.     if (RexxPort)
  1367.         DeletePort(RexxPort);
  1368.  
  1369.     if (ArgsPtr)
  1370.         FreeArgs(ArgsPtr);
  1371.  
  1372.     if (MyArgs)
  1373.         FreeDosObject(DOS_RDARGS, MyArgs);
  1374.  
  1375.     if (logSema)
  1376.         FreeMyPublicSemaphore(logSema);
  1377.  
  1378.     if (jobSema)
  1379.         FreeMyPublicSemaphore(jobSema);
  1380.  
  1381.     if (UtilityBase)
  1382.         CloseLibrary(UtilityBase);
  1383.  
  1384.     if (OwnDevUnitBase)
  1385.         CloseLibrary(OwnDevUnitBase);
  1386.  
  1387.     if (WBArgs)
  1388.         FreeVec(WBArgs);
  1389.  
  1390.     if (LocaleBase) {
  1391.         CloseCatalog(MyCatalog);
  1392.         CloseLibrary((struct Library *) LocaleBase);
  1393.     }
  1394.  
  1395.     ((struct Process *) FindTask(NULL))->pr_WindowPtr = OldWindowPtr;
  1396.  
  1397.     XCEXIT(error);
  1398. }
  1399.  
  1400.  /*
  1401.   * this routine will read the crontab file, calling ParseEvent() to create
  1402.   * CyberNodes, and then link them into the event list.
  1403.   * 
  1404.   */
  1405.  
  1406. #define RCT_Buf BigBufOne
  1407.  
  1408. void ReadCronTab(void)
  1409. {
  1410.     BPTR fh;
  1411.     struct CyberNode *cn;
  1412.     ULONG line = 0;
  1413.     LONG error;
  1414.  
  1415.     if (!(fh = Open(CronTabName, MODE_OLDFILE))) {
  1416.         ErrorMsg(MSG_OPENING_CRONTAB, CronTabName);
  1417.         Log(MSG_OPENING_CRONTAB, CronTabName);
  1418.         return;
  1419.     }
  1420.  
  1421.     while (FGets(fh, RCT_Buf, BIG_BUF_SIZE_BASE)) {
  1422.         line++;
  1423.  
  1424.         if (RCT_Buf[0] == '#' || RCT_Buf[0] == '\n')
  1425.             continue;
  1426.  
  1427.         if (cn = ParseEvent(RCT_Buf)) {
  1428.             cn->cn_Flags |= CNF_CRONTAB;
  1429.             AddTail(&EventList, (struct Node *) cn);
  1430.         }
  1431.         else {
  1432.             ErrorMsg(MSG_PARSING, line, CronTabName);
  1433.             Log(MSG_PARSING, line, CronTabName);
  1434.         }
  1435.     }
  1436.  
  1437.     error = IoErr();
  1438.  
  1439.     if (error) {
  1440.         Fault(error, NULL, RCT_Buf, BIG_BUF_SIZE_BASE);
  1441.         ErrorMsg(MSG_IO_FAULT_IN_CRONTAB, error, RCT_Buf, line + 1, CronTabName);
  1442.         Log(MSG_IO_FAULT_IN_CRONTAB, error, RCT_Buf, line + 1, CronTabName);
  1443.     }
  1444.  
  1445.     Close(fh);
  1446. }
  1447.  
  1448.  /*
  1449.   * this routine will parse an ASCII string and make a CyberNode out of it.
  1450.   * it returns NULL if there was an error during the parse.  otherwise it
  1451.   * returns a pointer to the node.
  1452.   * 
  1453.   * note that we do something really sneaky here.  we use the ReadArgs() routine
  1454.   * to do the initial parse!.  This means that the order in which items occur
  1455.   * in a crontab entry can be virtually anything the user desires!
  1456.   */
  1457.  
  1458. #define PE_TEMPLATE "Event/M,</K,>/K,>>/K,2>/K,2>>/K,:NAME/K,:STACK/K/N,:PRI/K/N,:CUSTOMSH/K,:MAILUSER/K,:OBEYQUEUE/K,:SYSSH/S,:REXX/S,:NOLOG/S,:EXECONCE/S"
  1459.  
  1460. enum ParseEventReadArgs {
  1461.     PEARG_EVENT,
  1462.     PEARG_REDIRIN,
  1463.     PEARG_REDIROUT,
  1464.     PEARG_REDIROUT2,
  1465.     PEARG_REDIRERR,
  1466.     PEARG_REDIRERR2,
  1467.     PEARG_REXXNAME,
  1468.     PEARG_STACK,
  1469.     PEARG_PRI,
  1470.     PEARG_CUSTOMSH,
  1471.     PEARG_MAILUSER,
  1472.     PEARG_OBEYQUEUE,
  1473.     PEARG_SYSSH,
  1474.     PEARG_REXX,
  1475.     PEARG_NOLOG,
  1476.     PEARG_EXECONE,
  1477.     PEARG_sizeof
  1478. };
  1479.  
  1480. enum Events {
  1481.     EVENT_MINUTE,
  1482.     EVENT_HOUR,
  1483.     EVENT_DAY,
  1484.     EVENT_MONTH,
  1485.     EVENT_DOW,
  1486.     EVENT_COMMAND,
  1487.     EVENT_ARGS
  1488. };
  1489.  
  1490. #define PE_Buf BigBufTwo
  1491.  
  1492. struct CyberNode *ParseEvent(STRPTR event)
  1493. {
  1494.     struct CyberNode *cn;
  1495.     struct RDArgs *PArgsPtr;
  1496.     struct RDArgs *PMyArgs;
  1497.     char *ArgArray[PEARG_sizeof];
  1498.  
  1499.     register char **EventArgs;
  1500.     register int index;
  1501.     ULONG size;
  1502.     ULONG Bits[2];
  1503.  
  1504.     /* allocate our RDArgs structure */
  1505.     if (!(PMyArgs = (struct RDArgs *) AllocDosObject(DOS_RDARGS, TAG_DONE)))
  1506.         return (struct CyberNode *) NULL;
  1507.  
  1508.     PMyArgs->RDA_Flags |= RDAF_NOPROMPT;
  1509.  
  1510.     /*
  1511.      * set up the buffer for our ReadArgs() call.  We have to copy over
  1512.      * the string and put a new line at the end of it because of a
  1513.      * limitation of ReadArgs().  sigh.
  1514.      */
  1515.  
  1516.     {
  1517.         ULONG length;
  1518.  
  1519.         length = strlen(event);
  1520.         if (length + 2 > sizeof(PE_Buf)) {
  1521.             FreeDosObject(DOS_RDARGS, PMyArgs);
  1522.             return (struct CyberNode *) NULL;
  1523.         }
  1524.  
  1525.         CopyMem(event, PE_Buf, length);
  1526.  
  1527.         PE_Buf[length++] = '\n';
  1528.         PE_Buf[length] = '\0';
  1529.  
  1530.         PMyArgs->RDA_Source.CS_Buffer = PE_Buf;
  1531.         PMyArgs->RDA_Source.CS_Length = length;
  1532.         PMyArgs->RDA_Source.CS_CurChr = 0L;
  1533.     }
  1534.  
  1535.     /*
  1536.      * here we walk through the event line to make sure it isnt all
  1537.      * blank.
  1538.      */
  1539.  
  1540.     while (isspace(*event))
  1541.         event++;
  1542.  
  1543.     if (!*event) {
  1544.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1545.         return (struct CyberNode *) NULL;
  1546.     }
  1547.  
  1548.     memset(ArgArray, 0, sizeof(ArgArray));
  1549.  
  1550.     /* now we call ReadArgs() */
  1551.     PArgsPtr = ReadArgs(PE_TEMPLATE, (LONG *) & ArgArray, PMyArgs);
  1552.  
  1553.     if (!PArgsPtr) {
  1554.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1555.         return (struct CyberNode *) NULL;
  1556.     }
  1557.  
  1558.     /*
  1559.      * if they specified a name to be known as via the rexx port, make
  1560.      * sure it doesn't start with 0x because that's what we use to prefix
  1561.      * a hex number for nodes with no name and we don't want the user
  1562.      * fooling around with names we consider private.
  1563.      */
  1564.  
  1565.     if (ArgArray[PEARG_REXXNAME])
  1566.         if (ArgArray[PEARG_REXXNAME][0] == '0' && tolower(ArgArray[PEARG_REXXNAME][1]) == 'x') {
  1567.             FreeArgs(PArgsPtr);
  1568.             FreeDosObject(DOS_RDARGS, PMyArgs);
  1569.             return (struct CyberNode *) NULL;
  1570.         }
  1571.  
  1572.  
  1573.     /*
  1574.      * ok, ReadArgs has parsed the event for us.  make sure that we have
  1575.      * at least 5 time specs and a command name.
  1576.      */
  1577.     EventArgs = (char **) ArgArray[PEARG_EVENT];
  1578.  
  1579.     for (index = EVENT_MINUTE; index < EVENT_COMMAND; index++, EventArgs++)
  1580.         if (!*EventArgs || !isdigit(*EventArgs[0]) && *EventArgs[0] != '*') {
  1581.             FreeArgs(PArgsPtr);
  1582.             FreeDosObject(DOS_RDARGS, PMyArgs);
  1583.             return (struct CyberNode *) NULL;
  1584.         }
  1585.  
  1586.     /*
  1587.      * we have the five time spec strings.  now check to make sure we
  1588.      * have a command name.  we will also calculate its size as well as
  1589.      * the size of any args for the command while we are at it
  1590.      */
  1591.  
  1592.     if (!*EventArgs) {
  1593.         FreeArgs(PArgsPtr);
  1594.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1595.         return (struct CyberNode *) NULL;
  1596.     }
  1597.  
  1598.     size = strlen(*EventArgs++) + 1;
  1599.  
  1600.     while (*EventArgs)
  1601.         size += strlen(*EventArgs++) + 1;
  1602.  
  1603.     /*
  1604.      * now figure out the memory needed to store the other textual items
  1605.      * for this node
  1606.      */
  1607.  
  1608.     if (ArgArray[PEARG_REDIRIN])
  1609.         size += strlen(ArgArray[PEARG_REDIRIN]) + 1;
  1610.  
  1611.     if (ArgArray[PEARG_REDIROUT])
  1612.         size += strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1613.     if (ArgArray[PEARG_REDIROUT2]) {
  1614.         size += strlen(ArgArray[PEARG_REDIROUT2]) + 1;
  1615.         if (ArgArray[PEARG_REDIROUT])
  1616.             size -= strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1617.     }
  1618.  
  1619.     if (ArgArray[PEARG_REDIRERR])
  1620.         size += strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1621.     if (ArgArray[PEARG_REDIRERR2]) {
  1622.         size += strlen(ArgArray[PEARG_REDIRERR2]) + 1;
  1623.         if (ArgArray[PEARG_REDIRERR])
  1624.             size -= strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1625.     }
  1626.  
  1627.     if (ArgArray[PEARG_REXXNAME])
  1628.         size += strlen(ArgArray[PEARG_REXXNAME]) + 1;
  1629.     if (ArgArray[PEARG_CUSTOMSH])
  1630.         size += strlen(ArgArray[PEARG_CUSTOMSH]) + 1;
  1631.  
  1632.     if (ArgArray[PEARG_MAILUSER])
  1633.         size += strlen(ArgArray[PEARG_MAILUSER]) + 1;
  1634.  
  1635.     if (ArgArray[PEARG_OBEYQUEUE] && !isalpha(ArgArray[PEARG_OBEYQUEUE][0])) {
  1636.         FreeArgs(PArgsPtr);
  1637.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1638.         return (struct CyberNode *) NULL;
  1639.     }
  1640.  
  1641.     if (!(cn = (struct CyberNode *) AllocVec(size + sizeof(struct CyberNode) + 1, MEMF_CLEAR))) {
  1642.         FreeArgs(PArgsPtr);
  1643.         FreeDosObject(DOS_RDARGS, PMyArgs);
  1644.         return (struct CyberNode *) NULL;
  1645.     }
  1646.  
  1647.     /*
  1648.      * now that we have got the memory for the CyberNode start filling it
  1649.      * in.  we start by testing the STACK and PRI fields of the arg list
  1650.      * and use Atol() to get their values if present.  We then test the
  1651.      * REXX and NOLOG flags and use them to set the cn_Flags element.
  1652.      */
  1653.  
  1654.     if (ArgArray[PEARG_STACK])
  1655.         cn->cn_Stack = *((LONG *) ArgArray[PEARG_STACK]);
  1656.     if (cn->cn_Stack < 2048)
  1657.         cn->cn_Stack = DefaultStackSize;
  1658.  
  1659.     if (ArgArray[PEARG_PRI])
  1660.         cn->cn_Priority = *((LONG *) ArgArray[PEARG_PRI]) & 0xFF;
  1661.     else
  1662.         cn->cn_Priority = DefaultPriority;
  1663.  
  1664.     if (ArgArray[PEARG_OBEYQUEUE])
  1665.         cn->cn_ObeyQueue = tolower(ArgArray[PEARG_OBEYQUEUE][0]) - 'a' + 1;
  1666.  
  1667.     if (ArgArray[PEARG_REXX])
  1668.         cn->cn_Flags |= CNF_REXX;
  1669.  
  1670.     if (ArgArray[PEARG_NOLOG])
  1671.         cn->cn_Flags |= CNF_NOLOG;
  1672.  
  1673.     if (ArgArray[PEARG_SYSSH])
  1674.         cn->cn_Flags |= CNF_SYSSH;
  1675.  
  1676.     if (ArgArray[PEARG_EXECONE])
  1677.         cn->cn_Flags |= CNF_EXECONE;
  1678.  
  1679.     /*
  1680.      * now prepare to copy the textual items over into memory behind the
  1681.      * CyberNode
  1682.      */
  1683.  
  1684.     event = (char *) cn + sizeof(struct CyberNode);
  1685.  
  1686.     if (ArgArray[PEARG_REXXNAME]) {
  1687.         cn->cn_Name = event;
  1688.         size = strlen(ArgArray[PEARG_REXXNAME]) + 1;
  1689.         CopyMem(ArgArray[PEARG_REXXNAME], event, size);
  1690.         event += size;
  1691.     }
  1692.     if (ArgArray[PEARG_REDIRIN]) {
  1693.         cn->cn_ReDirIn = event;
  1694.         size = strlen(ArgArray[PEARG_REDIRIN]) + 1;
  1695.         CopyMem(ArgArray[PEARG_REDIRIN], event, size);
  1696.         event += size;
  1697.     }
  1698.     if (ArgArray[PEARG_REDIROUT] && !ArgArray[PEARG_REDIROUT2]) {
  1699.         cn->cn_ReDirOut = event;
  1700.         size = strlen(ArgArray[PEARG_REDIROUT]) + 1;
  1701.         CopyMem(ArgArray[PEARG_REDIROUT], event, size);
  1702.         event += size;
  1703.     }
  1704.     if (ArgArray[PEARG_REDIROUT2]) {
  1705.         cn->cn_ReDirOut = event;
  1706.         size = strlen(ArgArray[PEARG_REDIROUT2]) + 1;
  1707.         CopyMem(ArgArray[PEARG_REDIROUT2], event, size);
  1708.         event += size;
  1709.         cn->cn_Flags |= CNF_OUTAPP;
  1710.     }
  1711.     if (ArgArray[PEARG_REDIRERR] && !ArgArray[PEARG_REDIRERR2]) {
  1712.         cn->cn_ReDirErr = event;
  1713.         size = strlen(ArgArray[PEARG_REDIRERR]) + 1;
  1714.         CopyMem(ArgArray[PEARG_REDIRERR], event, size);
  1715.         event += size;
  1716.     }
  1717.     if (ArgArray[PEARG_REDIRERR2]) {
  1718.         cn->cn_ReDirErr = event;
  1719.         size = strlen(ArgArray[PEARG_REDIRERR2]) + 1;
  1720.         CopyMem(ArgArray[PEARG_REDIRERR2], event, size);
  1721.         event += size;
  1722.         cn->cn_Flags |= CNF_ERRAPP;
  1723.     }
  1724.     if (ArgArray[PEARG_CUSTOMSH]) {
  1725.         cn->cn_CustomShell = event;
  1726.         size = strlen(ArgArray[PEARG_CUSTOMSH]) + 1;
  1727.         CopyMem(ArgArray[PEARG_CUSTOMSH], event, size);
  1728.         event += size;
  1729.     }
  1730.     if (ArgArray[PEARG_MAILUSER]) {
  1731.         cn->cn_SendToUser = event;
  1732.         size = strlen(ArgArray[PEARG_MAILUSER]) + 1;
  1733.         CopyMem(ArgArray[PEARG_MAILUSER], event, size);
  1734.         event += size;
  1735.     }
  1736.  
  1737.     EventArgs = (char **) ArgArray[PEARG_EVENT];
  1738.     cn->cn_Command = event;
  1739.     index = EVENT_COMMAND;
  1740.     size = strlen(EventArgs[index]);
  1741.     CopyMem(EventArgs[index++], event, size);
  1742.     event += size;
  1743.     *event++ = 0;
  1744.  
  1745.     if (EventArgs[index]) {
  1746.         cn->cn_Args = event;
  1747.         while (EventArgs[index]) {
  1748.             size = strlen(EventArgs[index]);
  1749.             CopyMem(EventArgs[index++], event, size);
  1750.             event += size;
  1751.             *event++ = ' ';
  1752.         }
  1753.     }
  1754.     else
  1755.         cn->cn_Args = NULL;
  1756.  
  1757.     *--event = 0;
  1758.  
  1759.     /*
  1760.      * Now we need to convert the ASCII time values into bitmaps to store
  1761.      * in the node.  Note that we do not check to see if the time strings
  1762.      * are within range or not.  We simply logically AND away any invalid
  1763.      * bits and use whats left.
  1764.      */
  1765.  
  1766.     ParseBits(Bits, EventArgs[EVENT_MINUTE]);
  1767.     cn->cn_LoMin = Bits[0], cn->cn_HiMin = Bits[1] & 0xFFFFFFF;
  1768.  
  1769.     ParseBits(Bits, EventArgs[EVENT_HOUR]);
  1770.     cn->cn_Hour = Bits[0] & 0xFFFFFF;
  1771.  
  1772.     ParseBits(Bits, EventArgs[EVENT_DAY]);
  1773.     cn->cn_Day = Bits[0] & 0xFFFFFFFE;
  1774.  
  1775.     ParseBits(Bits, EventArgs[EVENT_MONTH]);
  1776.     cn->cn_Month = Bits[0] & 0x1FFE;
  1777.  
  1778.     ParseBits(Bits, EventArgs[EVENT_DOW]);
  1779.     cn->cn_DayOfWeek = Bits[0] & 0x7F;
  1780.  
  1781.     FreeArgs(PArgsPtr);
  1782.     FreeDosObject(DOS_RDARGS, PMyArgs);
  1783.     return cn;
  1784. }
  1785.  
  1786.  /*
  1787.   * this routine will try to figure out the next time an event would be
  1788.   * executed.
  1789.   */
  1790.  
  1791. ULONG EventNextExecutes(struct CyberNode * cn, SystemTime_t * st)
  1792. {
  1793.     ULONG days, days2;
  1794.     ULONG min, hour, day, month, dow, year;
  1795.     ULONG dayTarget, yearTarget;
  1796.     BOOL isLeap;
  1797.  
  1798.     for (days = -1, year = 1978; year < st->st_Year; year++)
  1799.         days += LEAP(year) ? 366 : 365;
  1800.  
  1801.     yearTarget = year + 8;
  1802.  
  1803.     for (; year < yearTarget; year++) {
  1804.         isLeap = LEAP(year);
  1805.  
  1806.         for (month = (year == st->st_Year) ? st->st_Month : 1; month < 13; month++)
  1807.             if (cn->cn_Month & (1L << month)) {
  1808.                 day = (month == st->st_Month && year == st->st_Year) ? st->st_Day : 1;
  1809.                 dayTarget = DayTable[isLeap][month - 1] + 1;
  1810.  
  1811.                 {
  1812.                     int i;
  1813.  
  1814.                     for (days2 = days, i = 1; i < month; i++)
  1815.                         days2 += DayTable[isLeap][i - 1];
  1816.                 }
  1817.  
  1818.                 dow = (days2 + day) % 7;
  1819.  
  1820.                 for (; day < dayTarget; day++, dow = ++dow % 7)
  1821.                     if ((cn->cn_DayOfWeek & (1L << dow)) && (cn->cn_Day & (1L << day)))
  1822.                         for (hour = (day == st->st_Day && month == st->st_Month && year == st->st_Year) ? st->st_Hour : 0; hour < 24; hour++)
  1823.                             if (cn->cn_Hour & (1L << hour))
  1824.                                 for (min = (hour == st->st_Hour && day == st->st_Day && month == st->st_Month && year == st->st_Year) ? st->st_Min + 1 : 0; min < 60; min++)
  1825.                                     if ((min > 31 && cn->cn_HiMin & (1L << (min - 32))) ||
  1826.                                         (min < 32 && cn->cn_LoMin & (1L << min)))
  1827.                                         return ((days2 + day) * SECSINDAY +
  1828.                                             (hour * SECSINHOUR) +
  1829.                                             (min * SECSINMINUTE));
  1830.             }
  1831.  
  1832.         days += isLeap ? 366 : 365;
  1833.     }
  1834.  
  1835.     /*
  1836.      * if we get down here then we couldn't find anytime within a seven
  1837.      * year span that satisfies the requirements to run this event. we
  1838.      * return zero in this case to let the caller know that no match was
  1839.      * found
  1840.      */
  1841.     return 0;
  1842. }
  1843.  
  1844.  /*
  1845.   * this will take an ASCII time string and convert it into a bitmap for
  1846.   * storage in a CyberNode
  1847.   */
  1848.  
  1849. void ParseBits(ULONG * bits, STRPTR tstr)
  1850. {
  1851.     register char *ptr;
  1852.     int start, end;
  1853.     int save;
  1854.  
  1855.     if (*tstr == '*') {
  1856.         bits[0] = bits[1] = 0xFFFFFFFF;
  1857.         return;
  1858.     }
  1859.     else
  1860.         bits[0] = bits[1] = 0;
  1861.  
  1862.     for (;;) {
  1863.         ptr = tstr;
  1864.         while (isdigit(*ptr))
  1865.             ptr++;
  1866.  
  1867.         save = *ptr, *ptr = NULL;
  1868.         end = start = atol(tstr);
  1869.  
  1870.         if (save == '-') {
  1871.             tstr = ++ptr;
  1872.  
  1873.             while (isdigit(*ptr))
  1874.                 ptr++;
  1875.  
  1876.             save = *ptr, *ptr = NULL;
  1877.             end = atol(tstr);
  1878.         }
  1879.  
  1880.         if (start >= 0 && end >= start)
  1881.             while (start <= end) {
  1882.                 if (start >= 64)
  1883.                     break;
  1884.  
  1885.                 if (start < 32)
  1886.                     bits[0] |= 1L << start;
  1887.                 else
  1888.                     bits[1] |= 1L << (start - 32);
  1889.  
  1890.                 start++;
  1891.             }
  1892.  
  1893.         if (!save)
  1894.             break;
  1895.         else
  1896.             tstr = ptr + 1;
  1897.     }
  1898. }
  1899.  
  1900.  /* convert a bit field back into an ASCII time string */
  1901.  
  1902. void UnParseBits(ULONG * bits, STRPTR ptr, int lowBit, int hiBit)
  1903. {
  1904.     STRPTR tptr;
  1905.     int curBit, startBit;
  1906.     BOOL isOn, lastOn;
  1907.  
  1908.     /* first check to see if everything is specified and return "*" if so */
  1909.     for (curBit = lowBit; curBit <= hiBit; curBit++)
  1910.         if ((curBit < 32 && !(bits[0] & 1L << curBit)) || (curBit > 31 && !(bits[1] & 1L << (curBit - 32))))
  1911.             break;
  1912.  
  1913.     if (curBit == hiBit + 1) {
  1914.         strcpy(ptr, "*");
  1915.         return;
  1916.     }
  1917.  
  1918.     /* it's not "*" so walk through and build things the hard way */
  1919.     tptr = ptr;
  1920.     *tptr = 0;
  1921.     lastOn = FALSE;
  1922.  
  1923.     for (curBit = lowBit; curBit < hiBit + 2; curBit++) {
  1924.         if ((curBit < 32 && (bits[0] & 1L << curBit)) || (curBit > 31 && (bits[1] & 1L << (curBit - 32))))
  1925.             isOn = TRUE;
  1926.         else
  1927.             isOn = FALSE;
  1928.  
  1929.         if (isOn & !lastOn) {
  1930.             MySPrintf(tptr, "%ld", curBit);
  1931.             startBit = curBit;
  1932.         }
  1933.  
  1934.         if (!isOn)
  1935.             if (lastOn && startBit != curBit - 1)
  1936.                 MySPrintf(tptr, "-%ld,", curBit - 1);
  1937.             else if (tptr > ptr && *(tptr - 1) != ',')
  1938.                 strcpy(tptr, ",");
  1939.  
  1940.         tptr += strlen(tptr);
  1941.         lastOn = isOn;
  1942.     }
  1943.  
  1944.     if (tptr == ptr)
  1945.         strcpy(ptr, "*");    /* Uh oh.  Somehow we have a field
  1946.                      * with nothing specified.  Fill it
  1947.                      * in with a "*" */
  1948.  
  1949.     else
  1950.         *--tptr = '\0';
  1951.  
  1952.     return;
  1953. }
  1954.  
  1955.  /* find a specific CyberNode by name */
  1956.  
  1957. struct CyberNode *FindEvent(STRPTR name)
  1958. {
  1959.     struct CyberNode *cn;
  1960.     struct CyberNode *eventAddr;
  1961.  
  1962.     if (!name || name[0] == '\0')
  1963.         return (struct CyberNode *) NULL;
  1964.  
  1965.     if (name[0] == '0' && tolower(name[1]) == 'x')
  1966.         stch_l(&name[2], (long *) &eventAddr);
  1967.     else
  1968.         eventAddr = 0;
  1969.  
  1970.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  1971.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ)
  1972.         if (cn == eventAddr || (cn->cn_Name && stricmp(name, cn->cn_Name) == 0))
  1973.             return cn;
  1974.  
  1975.     return (struct CyberNode *) NULL;
  1976. }
  1977.  
  1978.  /*
  1979.   * this routine will walk through the event list and free all the nodes in
  1980.   * it of a given type.  If called with TRUE it will free crontab entries,
  1981.   * otherwise it will free Rexx entries
  1982.   */
  1983.  
  1984. void FreeEvents(BOOL DoCronTabEntries)
  1985. {
  1986.     register struct CyberNode *cn;
  1987.     register struct CyberNode *tcn;
  1988.     UBYTE Flags;
  1989.  
  1990.     for (cn = (struct CyberNode *) EventList.lh_Head; cn->cn_Node.ln_Succ;
  1991.          cn = (struct CyberNode *) cn->cn_Node.ln_Succ) {
  1992.         Flags = cn->cn_Flags & CNF_CRONTAB;
  1993.  
  1994.         if ((DoCronTabEntries && Flags) || (!DoCronTabEntries && !Flags)) {
  1995.             tcn = (struct CyberNode *) cn->cn_Node.ln_Pred;
  1996.             DeleteEvent(cn);
  1997.             cn = tcn;
  1998.         }
  1999.     }
  2000. }
  2001.  
  2002.  /*
  2003.   * this will delete the resources used for a specific event.  it checks to
  2004.   * see if there are any QueueFIFO messages pending for the specified event
  2005.   * and removes them as needed.
  2006.   */
  2007.  
  2008. void DeleteEvent(struct CyberNode * cn)
  2009. {
  2010.     register struct QueueFIFO *qf;
  2011.     register struct QueueFIFO *tqf;
  2012.  
  2013.     if (cn->cn_DelayedCount) {
  2014.         for (qf = (struct QueueFIFO *) & jobQueue[cn->cn_ObeyQueue].jq_FIFOList;
  2015.              qf->qf_Node.mln_Succ; qf = (struct QueueFIFO *) qf->qf_Node.mln_Succ) {
  2016.             if (qf->qf_CyberNode == cn) {
  2017.                 tqf = (struct QueueFIFO *) qf->qf_Node.mln_Pred;
  2018.                 Remove((struct Node *) qf);
  2019.                 FreeVec(qf);
  2020.                 qf = tqf;
  2021.             }
  2022.         }
  2023.     }
  2024.  
  2025.     Remove((struct Node *) cn);
  2026.     FreeVec(cn);
  2027. }
  2028.  
  2029.  /*
  2030.   * this allocates an output filehandle for us which output will be piped
  2031.   * through to sendmail over
  2032.   */
  2033.  
  2034. #define SSM_Buf BigBufOne
  2035. #define SSM_Buf2 BigBufTwo
  2036.  
  2037. BPTR SetupSendMail(STRPTR cmdName, STRPTR cmdArgs, STRPTR userName)
  2038. {
  2039.     BPTR pfho, pfhi, ofh;
  2040.     UBYTE pipeName[36];
  2041.  
  2042.     struct timeval tr_time;
  2043.  
  2044.     GetSysTime(&tr_time);
  2045.  
  2046.     MySPrintf(pipeName, "PIPE:CyberCron.%08lx.%08lx", tr_time.tv_secs, tr_time.tv_micro);
  2047.  
  2048.     MySPrintf(SSM_Buf2, GetString(MSG_REALNAME_TEMPLATE), CYBERCRON, VersionID);
  2049.     MySPrintf(SSM_Buf, SendMailCmd, "cybercron", SSM_Buf2);
  2050.  
  2051.     ofh = Open("NIL:", MODE_NEWFILE);
  2052.     if (!ofh)
  2053.         return NULL;
  2054.  
  2055.     pfho = Open(pipeName, MODE_NEWFILE);
  2056.     if (!pfho) {
  2057.         Close(ofh);
  2058.         return NULL;
  2059.     }
  2060.  
  2061.     pfhi = Open(pipeName, MODE_OLDFILE);
  2062.     if (!pfhi) {
  2063.         Close(pfho);
  2064.         Close(ofh);
  2065.         return NULL;
  2066.     }
  2067.  
  2068.     if (SystemTags(SSM_Buf, SYS_Input, pfhi, SYS_Output, ofh, SYS_Asynch, TRUE,
  2069.                NP_StackSize, 32768, NP_CopyVars, TRUE, NP_Cli, TRUE, TAG_DONE) == -1) {
  2070.         Close(pfho);
  2071.         Close(pfhi);
  2072.         Close(ofh);
  2073.         return NULL;
  2074.     }
  2075.  
  2076.     FPrintf(pfho, GetString(MSG_TO_SUBJECT), userName, cmdName, (cmdArgs ? " " : NULL), cmdArgs);
  2077.     Flush(pfho);
  2078.  
  2079.     return pfho;
  2080. }
  2081.  
  2082. UBYTE SJ_Buf[1024 + 12];
  2083. UBYTE CN_Buf[sizeof(SJ_Buf)];
  2084.  
  2085.  /* this routine will start up a job using System() */
  2086.  
  2087. void StartSystemJob(struct CyberNode * cn)
  2088. {
  2089.     BPTR ifh, ofh /* , efh */ ;
  2090.     struct SystemECdata *ecdata;
  2091.  
  2092.     struct TagItem tlist[20];
  2093.     int tlistIdx = 0;
  2094.  
  2095.     if (cn->cn_Args) {
  2096.         MySPrintf(SJ_Buf, "\"%s\" %s", cn->cn_Command, cn->cn_Args);
  2097.         MySPrintf(CN_Buf, "\"╗%s½ %s\"", cn->cn_Command, cn->cn_Args);
  2098.     }
  2099.     else {
  2100.         MySPrintf(SJ_Buf, "\"%s\"", cn->cn_Command);
  2101.         MySPrintf(CN_Buf, "\"╗%s½\"", cn->cn_Command);
  2102.     }
  2103.  
  2104.     if (!(ecdata = AllocVec(sizeof(struct SystemECdata), MEMF_CLEAR)))
  2105.         return;
  2106.  
  2107.     ecdata->jobNo = GetJobNum();
  2108.     if (!ecdata->jobNo) {
  2109.         Log(MSG_JOB_TABLE_FULL, CN_Buf);
  2110.         FreeVec(ecdata);
  2111.         return;
  2112.     }
  2113.  
  2114.     if (cn->cn_ReDirIn)
  2115.         ifh = Open(cn->cn_ReDirIn, MODE_OLDFILE);
  2116.     else
  2117.         ifh = Open("NIL:", MODE_OLDFILE);
  2118.  
  2119.     if (!ifh) {
  2120.         Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(MSG_REDIRIN), CN_Buf);
  2121.         FreeJobNum(ecdata->jobNo);
  2122.         FreeVec(ecdata);
  2123.         return;
  2124.     }
  2125.  
  2126.     tlist[tlistIdx].ti_Tag = SYS_Input;
  2127.     tlist[tlistIdx++].ti_Data = (ULONG) ifh;
  2128.  
  2129.     if (cn->cn_SendToUser && SendMailCmd[0])
  2130.         ofh = SetupSendMail(cn->cn_Command, cn->cn_Args, cn->cn_SendToUser);
  2131.     else {
  2132.         if (cn->cn_ReDirOut)
  2133.             ofh = Open(cn->cn_ReDirOut, (cn->cn_Flags & CNF_OUTAPP ? MODE_READWRITE : MODE_NEWFILE));
  2134.         else
  2135.             ofh = Open("NIL:", MODE_NEWFILE);
  2136.  
  2137.         if (ofh && (cn->cn_Flags & CNF_OUTAPP))
  2138.             Seek(ofh, 0, OFFSET_END);
  2139.     }
  2140.  
  2141.     if (!ofh) {
  2142.         Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(MSG_REDIROUT), CN_Buf);
  2143.         Close(ifh);
  2144.         FreeJobNum(ecdata->jobNo);
  2145.         FreeVec(ecdata);
  2146.         return;
  2147.     }
  2148.  
  2149.     tlist[tlistIdx].ti_Tag = SYS_Output;
  2150.     tlist[tlistIdx++].ti_Data = (ULONG) ofh;
  2151.  
  2152.     tlist[tlistIdx].ti_Tag = SYS_Asynch;
  2153.     tlist[tlistIdx++].ti_Data = TRUE;
  2154.  
  2155. /*
  2156.     Sigh..  Randell tells me that StdErr is pretty much unofficially "not for use"
  2157.     under 2.0.  This is here for the day that it can be used.  All that should need
  2158.     to be done is to pull out the comments.  Oh well.
  2159.  
  2160.     Do not uncomment this code.  NP_Error and NP_ErrorClose are __IGNORED__ by the
  2161.     system right now so if you specify stderr redirection, it will open the file and never
  2162.     close it.
  2163.  
  2164.     if (cn->cn_ReDirErr) {
  2165.         efh = Open(cn->cn_ReDirErr, (cn->cn_Flags & CNF_ERRAPP ? MODE_READWRITE : MODE_NEWFILE));
  2166.         if (!efh) {
  2167.             Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(MSG_REDIRERR), CN_Buf);
  2168.             Close(ofh);
  2169.             Close(ifh);
  2170.             FreeJobNum(ecdata->jobNo);
  2171.             FreeVec(ecdata);
  2172.             return;
  2173.         }
  2174.  
  2175.         if (cn->cn_Flags & CNF_ERRAPP)
  2176.             Seek(efh, 0, OFFSET_END);
  2177.  
  2178.         tlist[tlistIdx].ti_Tag = NP_Error;
  2179.         tlist[tlistIdx++].ti_Data = (ULONG)efh;
  2180.         tlist[tlistIdx].ti_Tag = NP_CloseError;
  2181.         tlist[tlistIdx++].ti_Data = TRUE;
  2182.     } else {
  2183.         tlist[tlistIdx].ti_Tag = NP_Error;
  2184.         tlist[tlistIdx++].ti_Data = (ULONG)StdErr;
  2185.         tlist[tlistIdx].ti_Tag = NP_CloseError;
  2186.         tlist[tlistIdx++].ti_Data = FALSE;
  2187.     }
  2188. */
  2189.     tlist[tlistIdx].ti_Tag = NP_StackSize;
  2190.     tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_Stack;
  2191.  
  2192.     tlist[tlistIdx].ti_Tag = NP_Priority;
  2193.     tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_Priority;
  2194.  
  2195.     if (cn->cn_CustomShell) {
  2196.         tlist[tlistIdx].ti_Tag = SYS_CustomShell;
  2197.         tlist[tlistIdx++].ti_Data = (ULONG) cn->cn_CustomShell;
  2198.     }
  2199.     else {
  2200.         tlist[tlistIdx].ti_Tag = SYS_UserShell;
  2201.         tlist[tlistIdx++].ti_Data = ((cn->cn_Flags & CNF_SYSSH) ? FALSE : TRUE);
  2202.     }
  2203.  
  2204.     tlist[tlistIdx].ti_Tag = NP_Cli;
  2205.     tlist[tlistIdx++].ti_Data = TRUE;
  2206.  
  2207.     tlist[tlistIdx].ti_Tag = NP_CopyVars;
  2208.     tlist[tlistIdx++].ti_Data = TRUE;
  2209.  
  2210.     tlist[tlistIdx].ti_Tag = NP_ExitCode;
  2211.     tlist[tlistIdx++].ti_Data = (ULONG) & EndSystemJob;
  2212.  
  2213.     tlist[tlistIdx].ti_Tag = NP_ExitData;
  2214.     tlist[tlistIdx++].ti_Data = (ULONG) ecdata;
  2215.  
  2216.     tlist[tlistIdx].ti_Tag = TAG_DONE;
  2217.     tlist[tlistIdx].ti_Data = 0;
  2218.  
  2219.     ecdata->queueNo = cn->cn_ObeyQueue;
  2220.     ecdata->flags = cn->cn_Flags & CNF_NOLOG;
  2221.  
  2222.     if (SystemTagList(SJ_Buf, tlist) == -1) {
  2223.         Log(MSG_COULDNT_START_SYSTEM_JOB, CN_Buf);
  2224. /*
  2225.         See above for why this is currently commented out.
  2226.  
  2227.         if (cn->cn_ReDirErr)
  2228.             Close(efh);
  2229. */
  2230.         Close(ofh);
  2231.         Close(ifh);
  2232.         FreeJobNum(ecdata->jobNo);
  2233.         FreeVec(ecdata);
  2234.         return;
  2235.     }
  2236.  
  2237.     if (!(cn->cn_Flags & CNF_NOLOG))
  2238.         Log(MSG_JOB_STARTED, ecdata->jobNo, CN_Buf);
  2239.  
  2240.     ObtainSemaphore(&jobSema->mps_Sema);
  2241.     NumSystemJobs++;
  2242.     jobQueue[cn->cn_ObeyQueue].jq_Cur++;
  2243.     ReleaseSemaphore(&jobSema->mps_Sema);
  2244. }
  2245.  
  2246. int __saveds __asm EndSystemJob(register __d0 int rc, register __d1 struct SystemECdata * data)
  2247. {
  2248.     if (!data->flags)
  2249.         Log(MSG_JOB_ENDED, data->jobNo);
  2250.  
  2251.     FreeJobNum(data->jobNo);
  2252.  
  2253.     ObtainSemaphore(&jobSema->mps_Sema);
  2254.     NumSystemJobs--;
  2255.     jobQueue[data->queueNo].jq_Cur--;
  2256.     ReleaseSemaphore(&jobSema->mps_Sema);
  2257.  
  2258.     FreeVec(data);
  2259.  
  2260.     return rc;
  2261. }
  2262.  
  2263. void StartRexxJob(struct CyberNode * cn)
  2264. {
  2265.     struct RexxMsg *msg;
  2266.     struct MsgPort *rexxServerPort;
  2267.     UWORD jobNo;
  2268.     BPTR ifh, ofh;
  2269.  
  2270.     if (cn->cn_Command[0] == '`') {
  2271.         if (cn->cn_Args)
  2272.             MySPrintf(SJ_Buf, "%s %s", &cn->cn_Command[1], cn->cn_Args);
  2273.         else
  2274.             strcpy(SJ_Buf, &cn->cn_Command[1]);
  2275.  
  2276.         MySPrintf(CN_Buf, "\"%s\"", SJ_Buf);
  2277.     }
  2278.     else {
  2279.         strcpy(SJ_Buf, cn->cn_Command);
  2280.         MySPrintf(CN_Buf, "\"╗%s½%s%s\"", cn->cn_Command, (cn->cn_Args ? " " : NULL), cn->cn_Args);
  2281.     }
  2282.  
  2283.     if (GetARexxLib() == NULL) {
  2284.         ErrorMsg(MSG_CANT_START_AREXX_NO_LIB, CN_Buf, RXSNAME);
  2285.         Log(MSG_CANT_START_AREXX_NO_LIB, CN_Buf, RXSNAME);
  2286.         return;
  2287.     }
  2288.  
  2289.     jobNo = GetJobNum();
  2290.     if (!jobNo) {
  2291.         Log(MSG_JOB_TABLE_FULL, CN_Buf);
  2292.         FreeARexxLib();
  2293.         return;
  2294.     }
  2295.  
  2296.     if (cn->cn_ReDirIn)
  2297.         ifh = Open(cn->cn_ReDirIn, MODE_OLDFILE);
  2298.     else
  2299.         ifh = Open("NIL:", MODE_OLDFILE);
  2300.  
  2301.     if (!ifh) {
  2302.         Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(MSG_REDIRIN), CN_Buf);
  2303.         FreeJobNum(jobNo);
  2304.         FreeARexxLib();
  2305.         return;
  2306.     }
  2307.  
  2308.     if (cn->cn_SendToUser && SendMailCmd[0])
  2309.         ofh = SetupSendMail((cn->cn_Command[0] == '`' ? SJ_Buf : cn->cn_Command),
  2310.                     (cn->cn_Command[0] == '`' ? NULL : cn->cn_Args), cn->cn_SendToUser);
  2311.     else {
  2312.         if (cn->cn_ReDirOut)
  2313.             ofh = Open(cn->cn_ReDirOut, (cn->cn_Flags & CNF_OUTAPP ? MODE_READWRITE : MODE_NEWFILE));
  2314.         else
  2315.             ofh = Open("NIL:", MODE_NEWFILE);
  2316.  
  2317.         if (ofh && (cn->cn_Flags & CNF_OUTAPP))
  2318.             Seek(ofh, 0, OFFSET_END);
  2319.     }
  2320.  
  2321.     if (!ofh) {
  2322.         Log(MSG_COULDNT_OPEN_REDIRECTION, GetString(MSG_REDIROUT), CN_Buf);
  2323.         Close(ifh);
  2324.         FreeJobNum(jobNo);
  2325.         FreeARexxLib();
  2326.         return;
  2327.     }
  2328.  
  2329.     if (!(msg = CreateRexxMsg(RexxPort, "rexx", RXSDIR))) {
  2330.         Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(MSG_REXXMSG), CN_Buf);
  2331.         Close(ofh);
  2332.         Close(ifh);
  2333.         FreeJobNum(jobNo);
  2334.         FreeARexxLib();
  2335.         return;
  2336.     }
  2337.  
  2338.     if (!(msg->rm_Args[0] = CreateArgstring(SJ_Buf, strlen(SJ_Buf)))) {
  2339.         Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(MSG_ARGSTRING), CN_Buf);
  2340.         DeleteRexxMsg(msg);
  2341.         Close(ofh);
  2342.         Close(ifh);
  2343.         FreeJobNum(jobNo);
  2344.         FreeARexxLib();
  2345.         return;
  2346.     }
  2347.  
  2348.     if (cn->cn_Args && cn->cn_Command[0] != '`') {
  2349.         if (!(msg->rm_Args[1] = CreateArgstring(cn->cn_Args, strlen(cn->cn_Args)))) {
  2350.             Log(MSG_CANT_CREATE_AREXX_OBJECT, GetString(MSG_ARGSTRING), CN_Buf);
  2351.             DeleteArgstring(msg->rm_Args[0]);
  2352.             DeleteRexxMsg(msg);
  2353.             Close(ofh);
  2354.             Close(ifh);
  2355.             FreeJobNum(jobNo);
  2356.             FreeARexxLib();
  2357.             return;
  2358.         }
  2359.     }
  2360.     else
  2361.         msg->rm_Args[1] = NULL;
  2362.  
  2363.     msg->rm_Action = RXFUNC;
  2364.     if (cn->cn_Command[0] == '`')
  2365.         msg->rm_Action |= RXFF_STRING;
  2366.  
  2367.     if (msg->rm_Args[1])
  2368.         msg->rm_Action |= 1;    /* one argument string passed in */
  2369.  
  2370.     msg->rm_Args[2] = (STRPTR) jobNo;
  2371.     msg->rm_Args[3] = (STRPTR) (cn->cn_Flags & CNF_NOLOG);
  2372.     msg->rm_Args[4] = (STRPTR) cn->cn_ObeyQueue;
  2373.  
  2374.     msg->rm_Stdin = ifh;
  2375.     msg->rm_Stdout = ofh;
  2376.  
  2377.     Forbid();
  2378.     if (rexxServerPort = FindPort(RXSDIR))
  2379.         PutMsg(rexxServerPort, (struct Message *) msg);
  2380.     Permit();
  2381.  
  2382.     if (rexxServerPort) {
  2383.         if (!msg->rm_Args[3])
  2384.             Log(MSG_JOB_STARTED, jobNo, CN_Buf);
  2385.         ObtainSemaphore(&jobSema->mps_Sema);
  2386.         NumARexxJobs++;
  2387.         jobQueue[cn->cn_ObeyQueue].jq_Cur++;
  2388.         ReleaseSemaphore(&jobSema->mps_Sema);
  2389.     }
  2390.     else {
  2391.         Log(MSG_CANT_FIND_REXX_HOST, RXSDIR, CN_Buf);
  2392.         if (msg->rm_Args[1])
  2393.             DeleteArgstring(msg->rm_Args[1]);
  2394.         DeleteArgstring(msg->rm_Args[0]);
  2395.         DeleteRexxMsg(msg);
  2396.         Close(ofh);
  2397.         Close(ifh);
  2398.         FreeJobNum(jobNo);
  2399.     }
  2400.  
  2401.     FreeARexxLib();
  2402. }
  2403.  
  2404.  /*
  2405.   * this routine will attempt to get a job number for us. it returns the job
  2406.   * number or 0 if no jobs are free.
  2407.   */
  2408.  
  2409. UWORD GetJobNum(void)
  2410. {
  2411.     register UWORD job;
  2412.     register int index;
  2413.     register UBYTE mask;
  2414.  
  2415.     ObtainSemaphore(&jobSema->mps_Sema);
  2416.  
  2417.     for (job = 0; job < JOB_TABLE_SIZE * sizeof(UBYTE); job++) {
  2418.         index = job / sizeof(UBYTE);
  2419.         mask = 1L << (job - index * sizeof(UBYTE));
  2420.  
  2421.         if (jobSema->mps_SemaData[index] & mask)
  2422.             continue;
  2423.  
  2424.         jobSema->mps_SemaData[index] |= mask;
  2425.         ReleaseSemaphore(&jobSema->mps_Sema);
  2426.         return (UWORD) (job + 1);
  2427.     }
  2428.  
  2429.     ReleaseSemaphore(&jobSema->mps_Sema);
  2430.     return (UWORD) 0;
  2431. }
  2432.  
  2433.  /* this routine will free a job number previously allocated */
  2434.  
  2435. void FreeJobNum(UWORD job)
  2436. {
  2437.     register int index;
  2438.     register UBYTE mask;
  2439.  
  2440.     if (!job || job >= JOB_TABLE_SIZE * sizeof(UBYTE))
  2441.         return;
  2442.  
  2443.     job--;
  2444.  
  2445.     index = job / sizeof(UBYTE);
  2446.     mask = 1L << (job - index * sizeof(UBYTE));
  2447.  
  2448.     ObtainSemaphore(&jobSema->mps_Sema);
  2449.     jobSema->mps_SemaData[index] &= ~mask;
  2450.     ReleaseSemaphore(&jobSema->mps_Sema);
  2451. }
  2452.  
  2453. void __stdargs Log(ULONG fmtId,...)
  2454. {
  2455.     va_list args;
  2456.     BPTR loghandle;
  2457.     struct DateTime dat;
  2458.     UBYTE Date[LEN_DATSTRING + 1];
  2459.     UBYTE Time[LEN_DATSTRING + 1];
  2460.  
  2461.     DateStamp(&dat.dat_Stamp);
  2462.  
  2463.     dat.dat_Format = FORMAT_DOS;
  2464.     dat.dat_Flags = 0;
  2465.     dat.dat_StrDay = NULL;
  2466.     dat.dat_StrDate = Date;
  2467.     dat.dat_StrTime = Time;
  2468.  
  2469.     memset(Date, 0, LEN_DATSTRING + 1);
  2470.     memset(Time, 0, LEN_DATSTRING + 1);
  2471.     DateToStr(&dat);
  2472.  
  2473.     va_start(args, fmtId);
  2474.  
  2475.     ObtainSemaphore(&logSema->mps_Sema);
  2476.     if (LogFile[0]) {
  2477.         if (OwnDevUnitBase)
  2478.             if (LockDevUnit("LOG-UPDATE.LOCK", 0, CYBERCRON, 0L))
  2479.                 goto dieLog;
  2480.  
  2481.         if (loghandle = Open(LogFile, MODE_READWRITE)) {
  2482.             Seek(loghandle, 0, OFFSET_END);
  2483.  
  2484.             FPrintf(loghandle, "(%s %s) ", Date, Time);
  2485.             VFPrintf(loghandle, GetString(fmtId), (LONG *) args);
  2486.             FPutC(loghandle, '\n');
  2487.             Close(loghandle);
  2488.         }
  2489.  
  2490.         if (OwnDevUnitBase)
  2491.             FreeDevUnit("LOG-UPDATE.LOCK", 0);
  2492.     }
  2493.       dieLog:
  2494.     ReleaseSemaphore(&logSema->mps_Sema);
  2495.  
  2496.     va_end(args);
  2497. }
  2498.  
  2499.  /*
  2500.   * this routine sets up a public semaphore.  If it already exists we just
  2501.   * bump its use count up by one.  Otherwise we have to allocate a new one.
  2502.   * semaDataSize is a number of bytes to allocate with the semaphore that can
  2503.   * be shared by those locking the semaphore.  Let's us have "global" data
  2504.   * between seperate processes.  These routines don't care what is put in
  2505.   * this data space.  It will be cleared to 0s when first allocated, though.
  2506.   */
  2507.  
  2508. struct MyPublicSema *InitMyPublicSemaphore(STRPTR semaName, ULONG semaDataSize)
  2509. {
  2510.     struct MyPublicSema *theSema;
  2511.     ULONG semaNameLen;
  2512.  
  2513.     if (!semaName)
  2514.         return (struct MyPublicSema *) NULL;
  2515.  
  2516.     /*
  2517.      * we want the semaphore's name to be a longword in length, at least
  2518.      * in space allocated for it
  2519.      */
  2520.     semaNameLen = ((strlen(semaName) + 3) / 4) * 4;
  2521.  
  2522.     Forbid();
  2523.     if (theSema = (struct MyPublicSema *) FindSemaphore(semaName))
  2524.         theSema->mps_UseCount++;    /* only safe way to diddle
  2525.                          * with this is under
  2526.                          * Forbid()... sigh */
  2527.     else {
  2528.  
  2529.         /*
  2530.          * we didn't find the semaphore around already so we have to
  2531.          * allocate and initialize it
  2532.          */
  2533.         if (!(theSema = AllocVec(sizeof(struct MyPublicSema) + semaNameLen + semaDataSize, MEMF_CLEAR | MEMF_PUBLIC))) {
  2534.             Permit();
  2535.             return (struct MyPublicSema *) NULL;
  2536.         }
  2537.  
  2538.         strcpy(theSema->mps_SemaName, semaName);
  2539.         theSema->mps_Sema.ss_Link.ln_Name = theSema->mps_SemaName;
  2540.         if (semaDataSize)
  2541.             theSema->mps_SemaData = &theSema->mps_SemaName[semaNameLen];
  2542.         theSema->mps_UseCount = 1;
  2543.  
  2544.         AddSemaphore(&theSema->mps_Sema);
  2545.     }
  2546.  
  2547.     Permit();
  2548.  
  2549.     return theSema;
  2550. }
  2551.  
  2552.  /*
  2553.   * this dealocates what we got from InitMyPublicSemaphore().  If there are
  2554.   * others still using it then just down the use count.  Otherwise remove the
  2555.   * semaphore from the public lists and down the use count.
  2556.   */
  2557.  
  2558. void FreeMyPublicSemaphore(struct MyPublicSema * mySema)
  2559. {
  2560.     if (mySema) {
  2561.         ObtainSemaphore(&mySema->mps_Sema);
  2562.         Forbid();
  2563.         ReleaseSemaphore(&mySema->mps_Sema);
  2564.         if (!--mySema->mps_UseCount) {
  2565.             RemSemaphore(&mySema->mps_Sema);
  2566.             FreeVec(mySema);
  2567.         }
  2568.         Permit();
  2569.     }
  2570. }
  2571.  
  2572.  /*
  2573.   * this function will open the ARexx library for us.  it handles nested
  2574.   * calls to open the library such that we only call OpenLibrary() once. each
  2575.   * time a rexx command is run, we call this routine to open the library and
  2576.   * when the RexxMsg comes back we call FreeARexxLib() to decrement the nest
  2577.   * count.
  2578.   */
  2579.  
  2580. BOOL GetARexxLib(void)
  2581. {
  2582.     if (ARexxLibCount) {
  2583.         ARexxLibCount++;
  2584.         return TRUE;
  2585.     }
  2586.  
  2587.     if (!(RexxSysBase = (struct RxsLib *) OpenLibrary(RXSNAME, 0)))
  2588.         return FALSE;
  2589.  
  2590.     ARexxLibCount = 1;
  2591.     return TRUE;
  2592. }
  2593.  
  2594.  /*
  2595.   * this routine is the opposite of GetARexxLib().  it frees a nested open
  2596.   * count for the ARexx library.  if the count goes to zero then we call
  2597.   * CloseLibrary() to free the library for real.
  2598.   */
  2599.  
  2600. void FreeARexxLib(void)
  2601. {
  2602.     if (!--ARexxLibCount)
  2603.         CloseLibrary((struct Library *) RexxSysBase);
  2604. }
  2605.  
  2606.  /*
  2607.   * we use this function to send error messages to StdErr.  We prefix all
  2608.   * messages with "CyberCron:" since the message might be dumped into a
  2609.   * stream with other people sending output to it
  2610.   */
  2611.  
  2612. void __stdargs ErrorMsg(ULONG fmtId,...)
  2613. {
  2614.     va_list args;
  2615.  
  2616.     struct IntuitionBase *IntuitionBase;
  2617.     ULONG my_IDCMP;
  2618.     struct EasyStruct ezRequest;
  2619.  
  2620.  
  2621.     if (ErrorsToStdErr) {
  2622.         if (StdErr) {
  2623.             FPrintf(StdErr, "%s:  ", CYBERCRON);
  2624.  
  2625.             va_start(args, fmtId);
  2626.             VFPrintf(StdErr, GetString(fmtId), (LONG *) args);
  2627.             FPutC(StdErr, '\n');
  2628.             Flush(StdErr);
  2629.             va_end(args);
  2630.         }
  2631.     }
  2632.     else {
  2633.         /* grab intuition, do an EasyRequest and then free intuition */
  2634.         if (IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 37L)) {
  2635.             ezRequest.es_StructSize = sizeof(struct EasyStruct);
  2636.             ezRequest.es_Flags = 0;
  2637.             ezRequest.es_Title = GetString(MSG_REQWINTITLE);
  2638.             ezRequest.es_TextFormat = GetString(fmtId);
  2639.             ezRequest.es_GadgetFormat = GetString(MSG_CONTINUE_GAD);
  2640.  
  2641.             my_IDCMP = 0L;
  2642.  
  2643.             va_start(args, fmtId);
  2644.             EasyRequestArgs((struct Window *) NULL, &ezRequest, &my_IDCMP, args);
  2645.             va_end(args);
  2646.  
  2647.             CloseLibrary((struct Library *) IntuitionBase);
  2648.         }
  2649.     }
  2650. }
  2651.  
  2652. static void __regargs MySPrintfSupp(void);
  2653.  
  2654. void __stdargs MySPrintf(STRPTR buf, STRPTR fmt,...)
  2655. {
  2656.     va_list args;
  2657.  
  2658.     va_start(args, fmt);
  2659.     RawDoFmt(fmt, (APTR) args, MySPrintfSupp, (APTR) buf);
  2660.     va_end(args);
  2661. }
  2662.  
  2663.  /* this next bit is rather SAS specific and was snitched from Loren Rittle */
  2664.  
  2665. static void __regargs MySPrintfSupp(void)
  2666. {
  2667.     __emit(0x16C0);        /* MOVE.B D0,(A3)+ */
  2668. }
  2669.  
  2670. void GetSystemTime(SystemTime_t * st, BOOL stIsSet)
  2671. {
  2672.     struct timeval tr_time;
  2673.     struct ClockData cd;
  2674.  
  2675.     if (!stIsSet) {
  2676.         GetSysTime(&tr_time);
  2677.         st->st_tvsecs = tr_time.tv_secs;
  2678.     }
  2679.  
  2680.     Amiga2Date(st->st_tvsecs, &cd);
  2681.  
  2682.     st->st_Year = cd.year;
  2683.     st->st_Month = cd.month;
  2684.     st->st_Day = cd.mday;
  2685.  
  2686.     st->st_Hour = cd.hour;
  2687.     st->st_Min = cd.min;
  2688.     st->st_Sec = cd.sec;
  2689.  
  2690.     st->st_DOW = cd.wday;
  2691. }
  2692.