home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / cenvi23.zip / CRON.CMM < prev    next >
Text File  |  1996-02-08  |  20KB  |  677 lines

  1. /*
  2.  * Cron.cmm
  3.  *
  4.  * This script is designed to be left running all the time. At times specified
  5.  * in the crontab file, other scripts and programs are run.
  6.  *
  7.  * CRONTAB format is each line as such:
  8.  *      MINUTE HOUR DAY_OF_MONTH MONTH DAY_OF_WEEK Command
  9.  *
  10.  * All fields are 0-based, except day of month. So the possible values are 0 to
  11.  * 1 less than you would expect. Sunday is 0 as is January.
  12.  *
  13.  * You can actually specify numbers in the field, or put in a * to match
  14.  * anything. You can use Words for month or day, use the first three letters.
  15.  * You can use numeric ranges or lists of numbers, such as:
  16.  *
  17.  * 1-4,5,6,8-10
  18.  *
  19.  *
  20.  * Ranges (and * which is consider min-max) can have a step, such as
  21.  * "* / 5" which means every five units. Spaces are not allowed.
  22.  *
  23.  * The day of month and day of week will both set off a match, so the entry
  24.  * will be run if EITHER matches. Use a dash in either field to match nothing.
  25.  *
  26.  *
  27.  * This version is designed to work on any CEnvi platform. If you come across
  28.  * something that does not work, please report it to us.
  29.  *
  30.  * Also note that this program uses some of the .CMM scripts distributed with
  31.  * CEnvi. If it cannot find them, some file types will not work. Currently
  32.  * supported file types and systems are:
  33.  *
  34.  *
  35.  * .NCF, .NLM           Netware
  36.  * .BAT                 All (Netware uses a batch interpreter script)
  37.  * .CMD                 OS/2
  38.  * .COM,.EXE            All except Netware
  39.  * .CMM                 All
  40.  *
  41.  * Any other extension will be done in the most generic way for that system.
  42.  * Hopefully, it will work.
  43.  *
  44.  * If you preceed the particular command with a '=', it is done synchronously
  45.  * as appropriate, usually on the same screen as cron. Don't let such
  46.  * applications hang or ask for input, because it is unlikely anyone will
  47.  * be around to do it. In this case, cron will be stuck waiting.
  48.  *
  49.  * The default is to launch the job asynchronously.
  50.  */
  51.  
  52. // The cron.tab's default location
  53. if( defined(_NWNLM_) )
  54.   {
  55.     crontab = "sys:/cron.tab";
  56.   } else {
  57.     crontab = "c:\\cron.tab";
  58.   }
  59.  
  60. seconds = 1;
  61.  
  62. // The last datestamp of the cron.tab file
  63. last_time = 0;
  64.  
  65. /* ---------------------------------------------------------------------- */
  66.  
  67. /*   Here is a C definition of the structure we are using, for easy of
  68.  *   modification
  69.  *
  70.  *   struct cron_entry {
  71.  *     char *command;
  72.  *     BYTE minute[60];           // We use flags for each of these fields
  73.  *     BYTE hour[24];
  74.  *     BYTE day[31];
  75.  *     BYTE month[12];
  76.  *     BYTE weekday[7];
  77.  *   };
  78.  */
  79.  
  80. /* ---------------------------------------------------------------------- */
  81.  
  82. /*
  83.  * Process a single numeric field, filling in the entries of the array
  84.  * to match. Return 0 on success, 1 on failure. Eat up all white space
  85.  * after the field.
  86.  */
  87. process_field(line,entry_array,max,text_strings,offset)
  88. {
  89. // By default, nothing is turned on.
  90.   for( i=0;i<max;i++ ) entry_array[i] = 0;
  91.  
  92.   comma_ok = 0;
  93.   while( line[0] && !isspace(line[0]) )
  94.     {
  95.       if( comma_ok==1 && line[0]==',' ) line++;
  96.       comma_ok = 1;
  97.  
  98.       if( line[0]=='-' ) { line++; continue; }
  99.       if( isdigit(line[0]) || line[0]=='*' )
  100.         {
  101.           first = 0; end = max-1; step = 1;
  102.  
  103. // First get a possible range
  104.           if( isdigit(line[0]) )
  105.             {
  106.               while( isdigit(line[0]) )
  107.                 { first = 10*first + line[0]-'0'; line++; }
  108.  
  109.               if( line[0]=='-' )
  110.                 {
  111.                   line++;
  112.                   end = 0;
  113.                   while( isdigit(line[0]) )
  114.                     { end = 10*end + line[0]-'0'; line++; }
  115.                 } else end = first;
  116.             } 
  117.           else line++;
  118.  
  119.           if( end<offset || end>=max+offset )
  120.             {
  121.               printf("Value is out of the valid range of %d-%d.\n",offset,max-1+offset);
  122.               return 1;
  123.             }
  124.  
  125. // And there may be a step value
  126.           if( line[0]=='/' )
  127.             {
  128.               line++;
  129.               step = 0;
  130.               while( isdigit(line[0]) )
  131.                 { step = 10*step + line[0]-'0'; line++; }
  132.             }
  133.  
  134. // Finally, set all those entries on.
  135.           for( i=first;i<=end;i+=step ) entry_array[i-offset] = 1;
  136.         } else {
  137.           if( text_strings==NULL || !isalpha(line[0]) ) return 1;
  138.           for( i=0;i<=GetArraySpan(text_strings);i++ )
  139.             if( !strnicmp(text_strings[i],line,3) )
  140.               {
  141.                 entry_array[i-offset] = 1;
  142.  
  143.                 line+=3; break;
  144.               }
  145.           if( i>GetArraySpan(text_strings) )
  146.             {
  147.               printf("Unrecognized text name for this field.\n");
  148.               return 1;
  149.             }
  150.         }
  151.     }
  152.  
  153.   while( isspace(line[0]) ) line++;
  154.  
  155.   return 0;
  156. }
  157.  
  158.  
  159. months =  
  160.   { "jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec" };
  161. days =
  162.   { "sun","mon","tue","wed","thu","fri","sat" };
  163.  
  164. /*
  165.  * Process a single line from the cron.tab file and return an entry structure
  166.  * describing it. If the line is blank, a comment, or illegal return NULL;
  167.  */
  168. process_cron_line(line,line_number)
  169. {
  170.   while( isspace(line[0]) ) line++;
  171.  
  172. // Ignore comments or blank lines.
  173.   if( line[0]=='\n' || line[0]=='\0' || line[0]=='#' ) return NULL;
  174.  
  175. // The line's format is described in the comments at the top of this file.
  176.   if( process_field(line,entry.minute,60,NULL,0) )
  177.     { printf("Illegal entry field 1 on line %d.\n",line_number); return NULL; }
  178.   if( process_field(line,entry.hour,24,NULL,0) )
  179.     { printf("Illegal entry field 2 on line %d.\n",line_number); return NULL; }
  180.   if( process_field(line,entry.day,32,NULL,1) )
  181.     { printf("Illegal entry field 3 on line %d.\n",line_number); return NULL; }
  182.   if( process_field(line,entry.month,12,months,0) )
  183.     { printf("Illegal entry field 4 on line %d.\n",line_number); return NULL; }
  184.   if( process_field(line,entry.weekday,7,days,0) )
  185.     { printf("Illegal entry field 5 on line %d.\n",line_number); return NULL; }
  186.  
  187.   strcpy(entry.command,line);
  188.   s = strlen(entry.command) - 1;
  189.   if( entry.command[s]=='\n' ) entry.command[s] = '\0';
  190.  
  191.   return entry;
  192. }
  193.  
  194. /* ---------------------------------------------------------------------- */
  195.  
  196. print_at_then_return(col,row,text)
  197. {
  198.   pos = ScreenCursor();
  199.   ScreenCursor(col,row);
  200.   printf("%s",text); fflush(stdout);
  201.   ScreenCursor(pos.col,pos.row);
  202. }
  203.  
  204. /* ---------------------------------------------------------------------- */
  205.  
  206. /*
  207.  * Read the cron.tab file in, creating a cron_entry structure. This structure
  208.  * is returned. Return NULL if unable to read in the cron.tab or if the
  209.  * cron.tab is empty.
  210.  */
  211. read_crontab()
  212. {
  213.   sprintf(buffer,"Reading CRONTAB...",timebuf);
  214.   print_at_then_return(60,0,buffer);
  215.   Undefine(entries); entry_count = 0; line_number = 0;
  216.   buffer = ""; SetArraySpan(buffer,512);
  217.  
  218.   if( (fp = fopen(crontab,"r"))==NULL )
  219.     {
  220.       return NULL;
  221.     }
  222.   while( !feof(fp) )
  223.     {
  224.       line_number++;
  225.       if( fgets(buffer,512,fp)==NULL ) break;
  226.       entry = process_cron_line(buffer,line_number);
  227.       if( entry!=NULL ) entries[entry_count++] = entry;
  228.     }
  229.   fclose(fp);
  230.  
  231.   file = Directory(crontab);
  232.   if( file==NULL )
  233.     {
  234.       printf("Unable to get timestamp from crontab file.\n");
  235.       return NULL;
  236.     }
  237.   last_time = file[0].Write;
  238.  
  239.   return defined(entries)?entries:NULL;
  240. }
  241.  
  242.  
  243. /*
  244.  * The particular entry should be executed now using the system-dependant
  245.  * way of executing them. The file's type is checked and used.
  246.  */
  247. cron_execute(command)
  248. {
  249.   exec = "";
  250. // For cool printing of date-time.
  251.   timebuf = ""; SetArraySpan(timebuf,256);
  252.   strftime(timebuf,256,"%m/%d/%y %H:%M:%S",localtime(time()));
  253.  
  254.  
  255.   sprintf(buffer2,"LAST CRON: [%s] %s",timebuf,command);
  256.   sprintf(buffer,"%-80s",buffer2);
  257.   print_at_then_return(0,2,buffer);
  258.  
  259.   sync = 0;
  260.   if( command[0]=='=' ) { command++; sync = 1; }
  261.  
  262.   if(  (end = strchr(command,' '))==NULL )
  263.     end = command + strlen(command) - 4;
  264.   else
  265.     end -= 4;
  266.  
  267. //
  268. // Each type of executable that we know about is done for every system as
  269. // well as we can.
  270. //
  271.   if( defined(_NWNLM_) && !strnicmp(end,".NCF",4) )
  272.     {
  273.       system(command);
  274.       return;
  275.     }
  276.  
  277.   if( defined(_NWNLM_) && !strnicmp(end,".NLM",4) )
  278.     {
  279.       spawn(P_NOWAIT,command);
  280.       return;
  281.     }
  282.  
  283.   if( !strnicmp(end,".CMM",4) )
  284.     {
  285.       if( sync )
  286.         {
  287.           Interpret(command,INTERP_FILE|INTERP_NOINHERIT_LOCAL|
  288.                     INTERP_NOINHERIT_GLOBAL);
  289.           return;
  290.         }
  291.       if( defined(_NWNLM_) )
  292.         {
  293.           sprintf(exec,"load cenvi %s",command);
  294.           system(exec);
  295.           return;
  296.         }
  297.       if( defined(_WINDOWS_) )
  298.         {
  299.           sprintf(exec,"cenviw %s",command);
  300.           spawn(P_NOWAIT,exec);
  301.           return;
  302.         }
  303.       if( defined(_DOS_) )
  304.         {
  305.           sprintf(exec,"cenvid %s",command);
  306.           spawn(P_SWAP,exec);
  307.           return;
  308.         }
  309.       if( defined(_DOS32_) )
  310.         {
  311.           sprintf(exec,"cenvid32 %s",command);
  312.           spawn(P_NOWAIT,exec);
  313.           return;
  314.         }
  315.       if( defined(_OS2_) )
  316.         {
  317.           sprintf(exec,"cenvi2 %s",command);
  318.           spawn(P_NOWAIT,exec);
  319.           return;
  320.         }
  321.       if( defined(_NTCON_) || defined(_95CON_) )
  322.         {
  323.           sprintf(exec,"cenvint %s",command);
  324.           spawn(P_NOWAIT,exec);
  325.           return;
  326.         }
  327.       if( defined(_NTWIN_) || defined(_95WIN_) )
  328.         {
  329.           sprintf(exec,"cenviwnt %s",command);
  330.           spawn(P_NOWAIT,exec);
  331.           return;
  332.         }
  333.  
  334. // Hmmm, an unrecognized version of CEnvi. Well, this is the most generic
  335. // way I can launch such a script.
  336.       sprintf(exec,"cenvi %s",command);
  337.       spawn(P_NOWAIT,exec);
  338.       return;
  339.     }
  340.  
  341.   if( !defined(_NWNLM_) && (!strnicmp(end,".EXE",4) || !strnicmp(end,".COM",4)) )
  342.     {
  343.       if( defined(_OS2_) )
  344.         {
  345.           if( sync )
  346.             spawn(P_WAIT,command);
  347.           else
  348.             system("start /c %s",command);
  349.           return;
  350.         }
  351.  
  352.       spawn(sync?P_WAIT:P_NOWAIT,command);
  353.       return;
  354.     }
  355.  
  356.   if( defined(_OS2_) && !strnicmp(end,".CMD",4) )
  357.     {
  358.       spawn(sync?P_WAIT:P_NOWAIT,command);
  359.       return;
  360.     }
  361.  
  362.   if( !strnicmp(end,".BAT",4) )
  363.     {
  364.       if( defined(_NWNLM_) )
  365.         {
  366.           if( sync )
  367.             {
  368.               sprintf(exec,"batch %s",command);
  369.               Interpret(exec,INTERP_FILE|INTERP_NOINHERIT_LOCAL|
  370.                         INTERP_NOINHERIT_GLOBAL);
  371.             } else {
  372.               sprintf(exec,"load cenvi batch %s",command);
  373.               spawn(P_NOWAIT,exec);
  374.             }
  375.           return;
  376.         }
  377.       if( defined(_DOS_) )
  378.         spawn(sync?P_WAIT:P_SWAP,command);
  379.       else
  380.         spawn(sync?P_WAIT:P_NOWAIT,command);
  381.       return;
  382.     }
  383.  
  384.   if( defined(_NWNLM_) )
  385.     {
  386.       spawn(P_NOWAIT,command);
  387.       return;
  388.     }
  389.  
  390.  
  391.   if( defined(_OS2_) )
  392.     {
  393.       sprintf(exec,"start cmd.exe /c %s",command);
  394.     } else {
  395.       if ( !strnicmp("start ",command,6) )
  396.         sprintf(exec,"%s",command);
  397.       else
  398.         sprintf(exec,"start command.com /c %s",command);
  399.     }
  400.   system(exec);
  401. }
  402.  
  403.  
  404. /*
  405.  * Go through each entry and see if it should be executed at this time.
  406.  */
  407. cron_checks(entries)
  408. {
  409.   current_time = localtime(time());
  410.   doit = 0;
  411.  
  412.   for( i=0;entries && i<=GetArraySpan(entries);i++ )
  413.     {
  414.       if( entries[i].minute[current_time.tm_min]  &&
  415.           entries[i].hour[current_time.tm_hour] &&
  416.           entries[i].month[current_time.tm_mon]  &&
  417. // Remember, the two day fields both can match
  418.           (entries[i].day[current_time.tm_mday-1]  ||
  419.            entries[i].weekday[current_time.tm_wday] )
  420.         )
  421. // Needed to stop CEnvi from passing the variable by reference.
  422.         {
  423.           cron_execute(=entries[i].command);
  424.           doit = 1;
  425.         }
  426.     }
  427.  
  428.   if( doit) update_list(entries);
  429. }
  430.  
  431.  
  432. /*
  433.  * Check if the crontab file has been changed
  434.  */
  435. cron_changed()
  436. {
  437.   file = Directory(crontab);
  438.   if( file==NULL ) return 0;
  439.  
  440.   return last_time!=file[0].Write;
  441. }
  442.  
  443.  
  444. /*
  445.  * Keeps a running clock on the screen.
  446.  */
  447. update_display()
  448. {
  449.   timebuf = ""; SetArraySpan(timebuf,256);
  450.   strftime(timebuf,256,"%m/%d/%y %H:%M:%S",localtime(time()));
  451.  
  452.  
  453.   sprintf(buffer,"%18s",timebuf);
  454.   print_at_then_return(60,0,buffer);
  455. }
  456.  
  457. /* ---------------------------------------------------------------------- */
  458.  
  459. num_days = { 31,28,31,30,31,30,31,31,30,31,30,31 };
  460.  
  461.  
  462. /*
  463.  * Given an entry, figure out the next time it will be run. Return a structure
  464.  * describing that time (something to be printed). Return NULL if it
  465.  * will never again be run.
  466.  */
  467. next_time(entry)
  468. {
  469.   current_time = localtime(time());
  470.  
  471.  
  472. // first, determine the next month it is going to happen. It could either
  473. // be this month from now to the end of the month, or one of the next months
  474. // any time during the month. If we wrap around months, we must also increment
  475. // the year.
  476.  
  477. // Note, 0 and 13 are both this month. However, 0 is limited by this day & time
  478. // forward only.
  479.   for( month=0;month<13;month++ )
  480.     {
  481.       try_month = (current_time.tm_mon+month)%12;
  482. // We wrapped into next year.
  483.       if( month+current_time.tm_mon==12 ) current_time.tm_year++;
  484. // First, if cannot execute this month, we continue;
  485.       if( entry.month[try_month]==0 ) continue;
  486.  
  487.  
  488. // We find the next day that matched. Note, if the month==0, we do no wrapping
  489.       start_day = (month==0)?(current_time.tm_mday-1):0;
  490. // We ignore leap years, tough.
  491.       for( day=start_day;day<num_days[try_month];day++ )
  492.         {
  493.           tmp.tm_sec = 0; tmp.tm_min = 0; tmp.tm_hour = 0;
  494.           tmp.tm_mon = try_month; tmp.tm_mday = day+1;
  495. // rough approximation of DST
  496.           tmp.tm_isdst = (try_month>2 && try_month<10)
  497.           tmp.tm_year = current_time.tm_year;
  498.  
  499.           new = localtime(mktime(tmp));
  500.  
  501.           if( (entry.day[day]==0) && (entry.weekday[new.tm_wday]==0) )
  502.             continue;
  503.  
  504. // Ok, this month and day is the next possible choice. Let's find a time that will
  505. // work. Again, if month==0, it must be later than now.
  506.           start_hour = 0;
  507.           if( month==0 && day==start_day ) start_hour = current_time.tm_hour;
  508.           for( hour=start_hour;hour<24;hour++ )
  509.             {
  510.               if( entry.hour[hour]==0 ) continue;
  511.  
  512. // Finally, the minute: there must be some better way to do all this...
  513.               start_min = 0;
  514. // Start one minute after now - the 'now' minute has already been done.
  515.               if( month==0 && day==start_day && hour==start_hour )
  516.                 start_min = current_time.tm_min+1;
  517.               for( minute = start_min;minute<60;minute++ )
  518.                 {
  519.                   if( entry.minute[minute] )
  520.                     {
  521. // FOUND IT!!!!
  522.                       current_time.tm_mon = try_month;
  523.                       current_time.tm_mday = day+1;
  524.                       current_time.tm_hour = hour;
  525.                       current_time.tm_min = minute;
  526.                       return current_time;
  527.                     }
  528.                 }
  529.             }
  530.         }
  531.     }
  532.  
  533.  
  534. // We couldn't find a match, return never execute again. With the entry format
  535. // I believe this really means it can never execute period.
  536.   return NULL;
  537. }
  538.  
  539.  
  540.  
  541. datesort(elem1,elem2)
  542. {
  543.   if( elem1.tm_year<elem2.tm_year ) return -1;
  544.   if( elem1.tm_year>elem2.tm_year ) return 1;
  545.   if( elem1.tm_mon<elem2.tm_mon ) return -1;
  546.   if( elem1.tm_mon>elem2.tm_mon ) return 1;
  547.   if( elem1.tm_mday<elem2.tm_mday ) return -1;
  548.   if( elem1.tm_mday>elem2.tm_mday ) return 1;
  549.   if( elem1.tm_hour<elem2.tm_hour ) return -1;
  550.   if( elem1.tm_hour>elem2.tm_hour ) return 1;
  551.   if( elem1.tm_min<elem2.tm_min ) return -1;
  552.   if( elem1.tm_min>elem2.tm_min ) return 1;
  553.   return 0;
  554. }
  555.  
  556.  
  557.  
  558. /*
  559.  * Keep a list of all upcoming events on the screen
  560.  */
  561. update_list(entries)
  562. {
  563.   ScreenCursor(1,7); printf("Upcoming Events:\n");
  564.  
  565.   j = 0;
  566.  
  567.   undefine(next);
  568.  
  569. // First we build a table of the next time these entries will be going off
  570. // along with a text representation of such.
  571.   for( i=0;entries && i<=GetArraySpan(entries);i++ )
  572.     {
  573.       ret = next_time(entries[i]);
  574.       if( ret!=NULL )
  575.         {
  576. // rough approximation of DST
  577.           ret.tm_isdst = (ret.tm_mon>2 && ret.tm_mon<10)
  578.           next[j] = ret = localtime(mktime(ret));
  579.           next[j++].command = entries[i].command;
  580.         }
  581.     }
  582.   if( defined(next) ) qsort(next,"datesort");
  583.  
  584.   for( i=0;i<10;i++ )
  585.     {
  586.       ScreenCursor(0,9+i);
  587.       if( i<j )
  588.         {
  589.           strftime(buf,"%a, %b %d, %Y at %I:%M %p   ",next[i]);
  590.           strcat(buf,next[i].command);
  591.           printf("%-79s\n",buf);
  592.         } else {
  593.           printf("                                                                               \n");
  594.         }
  595.     }
  596.  
  597.  
  598.   ScreenCursor(0,19);
  599.   printf("-------------------------------------------------------------------------------\n");
  600. }
  601.  
  602. /* ---------------------------------------------------------------------- */
  603.  
  604. /*
  605.  * Edit the crontab file using an appropriate editor for the system.
  606.  */
  607. invoke_editor()
  608. {
  609.   the_editor = "notepad";
  610.   if( defined(_DOS_) || defined(_DOS32_) )
  611.     {
  612.       mode = defined(_DOS32_) ? P_WAIT : P_SWAP; the_editor = "edit";
  613.     } else
  614.       mode = P_NOWAIT;
  615.  
  616.   if( defined(_OS2_) ) the_editor = "e";
  617.   if( defined(_NWNLM_) ) the_editor = "edit";
  618.   if( defined(EDITOR) ) the_editor = EDITOR;
  619.   spawn(mode,the_editor,crontab);
  620. }
  621.  
  622. /* ---------------------------------------------------------------------- */
  623.  
  624. main(argc,argv)
  625. {
  626.   ScreenClear();
  627.   printf("CMM Cron, Version 1.1.\n");
  628.   printf("  [Q] Quit  [E] Edit Crontab\n\n");
  629.  
  630.  
  631. // You can specify the crontab as the first argument.
  632.   if( argc>=2 ) crontab = argv[1];
  633.  
  634.  
  635.   Undefine(entries);
  636.   entries = read_crontab();
  637.  
  638.   printf("-------------------------------------------------------------------------------\n");
  639.   printf("Events will be processed as necessary. You can go away now. To update the\n");
  640.   printf("CRON.TAB at any time, simply edit and save it. Cron will automatically load it.\n");
  641.   printf("-------------------------------------------------------------------------------\n");
  642.   update_list(entries);
  643.  
  644.  
  645.   while( 1 )
  646.     {
  647. // Get current time, determine how many seconds until the next minute.
  648. // Use 61 instead of 60 to make sure we have flipped over to the next
  649. // minute.
  650.       counter = 61 - localtime(time()).tm_sec;
  651.  
  652. // Wait one minute
  653.       while( counter>0 )
  654.         {
  655.           if( kbhit() )
  656.             switch( toupper(getch()) )
  657.               {
  658.               case 'Q': exit(0);
  659.               case 'E': invoke_editor(); break;
  660.               }
  661.           update_display();                             // Prints a timer.
  662.           Suspend(seconds * 1000);                      // wait some seconds.
  663.           counter -= seconds;                           // Note that we waited.
  664.         }
  665.  
  666.       if( cron_changed() )
  667.         {
  668.           printf("Crontab file has changed.\n");
  669.           Undefine(entries);
  670.           entries = read_crontab();
  671.           update_list(entries);
  672.         }
  673.  
  674.       cron_checks(entries);
  675.     }
  676. }
  677.