alibabas
asked on
VB6 -Access Timed scehduler..
Looking for assistance to write an application in VB 6 with Access 2000 (and/or 97).
The idea is that Access would have at least 2 tables... geo_section_date_time_file _assoc_tbl and the other file_location_tbl (this could be done in 1 table and in more tables - your decide). The first table would have values entered by Geography, Section, Date, Time and File Id. The second would have File name and Local drive location.
The idea is to have a program that runs all the time and checks your system time and date. Depending on the time and date, the application would run a file (EXE/WAV/SQF/etc).. that has to be defaulted to certain file, but allow the user to change.. so say I opt in the database to open up Notepad at 7:00PM on June 2 from Section 3 facility in San Diego, CA .. then on such time (PST) on June 2 (every June 2)... Notepad should come up.. reason for Section and Location, is 1 for time (daylight savings or not) and section is say I want to kick off Notepad for Section 3 and MP3 file for Section 2 at the same time... I should be able too.
This app is to run all the time.. System Tray app would be the best. but I leave that to you.
The application should allow user to set the values in the database.. including file and file type... for this maybe a "wndows dialog box to select the file instead of typing in the name and location of the file.
The last and most important thing is that I need to learn from it.. so if you could comment the code.. I would appreciate that. Also, any references or components added, let me know if comments as well. Will learn and also use to help class learn.
If the code is tight, I will increase the points. I have another idea to build on this application, which is to make it an ASP project.. I hope to use the example and build on it with ASP to make it a web application for my class project.
Looking forward to your ideas...
Thanks.
The idea is that Access would have at least 2 tables... geo_section_date_time_file
The idea is to have a program that runs all the time and checks your system time and date. Depending on the time and date, the application would run a file (EXE/WAV/SQF/etc).. that has to be defaulted to certain file, but allow the user to change.. so say I opt in the database to open up Notepad at 7:00PM on June 2 from Section 3 facility in San Diego, CA .. then on such time (PST) on June 2 (every June 2)... Notepad should come up.. reason for Section and Location, is 1 for time (daylight savings or not) and section is say I want to kick off Notepad for Section 3 and MP3 file for Section 2 at the same time... I should be able too.
This app is to run all the time.. System Tray app would be the best. but I leave that to you.
The application should allow user to set the values in the database.. including file and file type... for this maybe a "wndows dialog box to select the file instead of typing in the name and location of the file.
The last and most important thing is that I need to learn from it.. so if you could comment the code.. I would appreciate that. Also, any references or components added, let me know if comments as well. Will learn and also use to help class learn.
If the code is tight, I will increase the points. I have another idea to build on this application, which is to make it an ASP project.. I hope to use the example and build on it with ASP to make it a web application for my class project.
Looking forward to your ideas...
Thanks.
Source code for an example with VB & scheduler:
http://www.programmersheaven.com/zone1/cat605/29396.htm
http://www.programmersheaven.com/zone1/cat605/29396.htm
ASKER
I went to the site.. but the download requires a password.. which requires me to pay... plus if the owrk is done already... I can't request for the more detailed "commenting" allowing us to learn.
Does Vb have any compnoents that do the schedulers work or can you help me create something like that?
Does Vb have any compnoents that do the schedulers work or can you help me create something like that?
To create your own, you will need several things.
Scheduling Requirement Decisions
1) To what day level do you want to schedule events?
Daily? Day of week? Specific date?
2) What time granularity do you want to specify?
Do you want to specify exact times? Time windows?
3) If an event window is missed, what happens?
Run right away? Wait until next window?
Develop a notation for defining events as required above.
Write interpreter.
---
You'll need an event definition file to identify scheduled events. Something like an INI file or registry key would be handy. A section header (or key) would define the event name and values within the section would give the event parameters. For example:
[EventNameX]
COMMAND=<CommandToRun>
DATE=<DayList>
This specifies the days of the week on which the event is to be executed. <DayList> is a list of 3-character day name abbreviations separated by commas. The recognized abbreviations are: SUN, MON, TUE, WED, THU, FRI, SAT. Day name abbreviations may appear in any order and case is not important.
TIME=<TimeIntervalList>
This specifies one or more time intervals during which the event may be considered for execution (in conjunction with the day(s) specified on the DATE= command). Time intervals are separated by commas and have the format:
<StartHHMM>-<EndHHMM>
Each HHMM represents a 4-digit hour and minute combination based on a 24-hour clock.
To simplify the specification of events that are to be executed hourly, you may define a single window using '*' for both the beginning and end hour.
Note that there is no requirement that an event be completed within the specified interval. Rather, the interval is used only to specify a range of acceptable event begin times.
---
Next you'll need an event history log that records, at least, the most recent execution of any event. Something whose lines look like:
<EventName>=<LastExecution Date as YYYYMMDD><LastExecutionTim e as HHMM>
---
At some regular interval, say once every minute:
* Load the event definition info
(opening it each time means changes take effect immediately)
* Look at each event to see if it qualifies for execution now.
* If it does, look in the event history log to see if the execution window it qualified for has been satisfied yet.
---
And that's pretty much it - a simple, not at all fancy event scheduler.
Scheduling Requirement Decisions
1) To what day level do you want to schedule events?
Daily? Day of week? Specific date?
2) What time granularity do you want to specify?
Do you want to specify exact times? Time windows?
3) If an event window is missed, what happens?
Run right away? Wait until next window?
Develop a notation for defining events as required above.
Write interpreter.
---
You'll need an event definition file to identify scheduled events. Something like an INI file or registry key would be handy. A section header (or key) would define the event name and values within the section would give the event parameters. For example:
[EventNameX]
COMMAND=<CommandToRun>
DATE=<DayList>
This specifies the days of the week on which the event is to be executed. <DayList> is a list of 3-character day name abbreviations separated by commas. The recognized abbreviations are: SUN, MON, TUE, WED, THU, FRI, SAT. Day name abbreviations may appear in any order and case is not important.
TIME=<TimeIntervalList>
This specifies one or more time intervals during which the event may be considered for execution (in conjunction with the day(s) specified on the DATE= command). Time intervals are separated by commas and have the format:
<StartHHMM>-<EndHHMM>
Each HHMM represents a 4-digit hour and minute combination based on a 24-hour clock.
To simplify the specification of events that are to be executed hourly, you may define a single window using '*' for both the beginning and end hour.
Note that there is no requirement that an event be completed within the specified interval. Rather, the interval is used only to specify a range of acceptable event begin times.
---
Next you'll need an event history log that records, at least, the most recent execution of any event. Something whose lines look like:
<EventName>=<LastExecution
---
At some regular interval, say once every minute:
* Load the event definition info
(opening it each time means changes take effect immediately)
* Look at each event to see if it qualifies for execution now.
* If it does, look in the event history log to see if the execution window it qualified for has been satisfied yet.
---
And that's pretty much it - a simple, not at all fancy event scheduler.
Sorry about the password file alibabas, I should have checked.
This one should hopefully work:
http://www.mvps.org/emorcillo/download/cod/schedule.zip
This one should hopefully work:
http://www.mvps.org/emorcillo/download/cod/schedule.zip
ASKER
SNilsson - Is it possible to get code for this as the Schedule.zip file has a DLL which I am not allowed to register at my work pc.. which is where I need it. Is it possible for you to help with the code.. provide snippets or full code?
Cookre - could you elaborate? I am not sure what you were trying to do.. give direction or solution.. please clarify.. and if you can help with code .. that would be great.
Cookre - could you elaborate? I am not sure what you were trying to do.. give direction or solution.. please clarify.. and if you can help with code .. that would be great.
That was a coarse outline for a scheduler I wrote for NetWare servers, but the idea can be implemented on any platform. Before starting any coding, however, you'll have to fully define WHAT it's supposed to do (the coding is just the HOW).
Recapping from above:
Scheduling Requirement Decisions
1) To what day level do you want to schedule events?
Daily? Day of week? Specific date?
Do you want scheduled events to repeat? If so, then such events would have start times
such as MONDAY/23:30, DAILY/16:45, FIRSTOFMONTH/10:00
Single executions might be defined like 2003/12/25-00:01
Other event times might be things like the third Tuesday of each month.
2) What time granularity do you want to specify?
Do you want to specify exact times? Time windows?
Some events may have to be scheduled for precise times, e.g., 23:56.
Others may be more lax - anytime between 23:00 and 23:55.
3) If an event window is missed, what happens?
Run right away? Wait until next window?
Despite all out desires, it may not be possible for something to start at 15:17. The box
may be busy doing something else that has control. That's the main reason for allowing
a start window instead of a hard, fixed time. In either case, however, one needs to specify
what the scheduler should do if an event window is missed.
Basicly, this means you need to define how you want to define a scheduled event. Implicit therein is also the question of interface - text file, GUI, whatever.
Recapping from above:
Scheduling Requirement Decisions
1) To what day level do you want to schedule events?
Daily? Day of week? Specific date?
Do you want scheduled events to repeat? If so, then such events would have start times
such as MONDAY/23:30, DAILY/16:45, FIRSTOFMONTH/10:00
Single executions might be defined like 2003/12/25-00:01
Other event times might be things like the third Tuesday of each month.
2) What time granularity do you want to specify?
Do you want to specify exact times? Time windows?
Some events may have to be scheduled for precise times, e.g., 23:56.
Others may be more lax - anytime between 23:00 and 23:55.
3) If an event window is missed, what happens?
Run right away? Wait until next window?
Despite all out desires, it may not be possible for something to start at 15:17. The box
may be busy doing something else that has control. That's the main reason for allowing
a start window instead of a hard, fixed time. In either case, however, one needs to specify
what the scheduler should do if an event window is missed.
Basicly, this means you need to define how you want to define a scheduled event. Implicit therein is also the question of interface - text file, GUI, whatever.
ASKER
I have tried to answer all your questions in CAPS.. let me know if I need to clarify anything.
Thanks.
Scheduling Requirement Decisions
1) To what day level do you want to schedule events?
Daily? Day of week? Specific date?
****** THE PROGRAM NEEDS TO RUN DAILY ***
Do you want scheduled events to repeat? If so, then such events would have start times
such as MONDAY/23:30, DAILY/16:45, FIRSTOFMONTH/10:00
Single executions might be defined like 2003/12/25-00:01
Other event times might be things like the third Tuesday of each month.
****** DEPENDING ON THE MONTH (NOT JAN TO FEB, BUT COMPANY PERIODS/MONTHS)... EACH DAY COULD HAVE DIFFERENT NUMBER OF EVENTS PLANNED. SO MONTH 3 COULD HAVE 6 EVENTS PER DAY, WHILE MONTH 4-7 HAVE 3 EVENTS, AND THE REST 5 EVENTS.
ALSO THE ISSUE IS SINCE WE ARE TRYING TO ALIGN MONTH 1 TO JAN OVER THE NEXT 3-4 YEARS, (THIS IS NOT ACCOUNTING PRACTICE .. JUST TRYING TO MOVE FROM COMPANY ORIGINATION TIME FROM MID YEAR TO BEGINNING OF ROMAN CALENDER. SO THIS YEAR, MONTH 3 IS JUNE 15 - JULY 14, BUT FOR NEXT YEAR (DEPENDING ON A NUMBER OF DAYS) THE TIME HAS TO BE MOVED BACK. SO IF I PICK 60 AS THE COUNT, THEN MONTH 3 SHOULD REPRESENT APRIL 15 - MAY 14 (ROUGHLY SPEAKING)... BASICALLY TO REALIGN THE SCHEDULER WITH THE MONTH CHANGES.. THIS WAY MNTH HAS 6 EVENTS (LIKE ABOVE).. BUT IT DOES NOT MATTER THAT THIS YEAR IT WAS FROM JUNE AND NEXT YEAR WILL BE APRIL ( THE DATE DIFFERENCE TIME WILL BE HARDCODED, BUT I WILL NEED TO KNOW WHERE TO CHANGE). ******
2) What time granularity do you want to specify?
Do you want to specify exact times? Time windows?
****** EXACT TIMES NEEDED ******
Some events may have to be scheduled for precise times, e.g., 23:56.
Others may be more lax - anytime between 23:00 and 23:55.
3) If an event window is missed, what happens?
Run right away? Wait until next window?
****** IF MISSED, NO PROBLEM, GO TO NEXT ONE... NOT SURE IF IT IS WORTH GOING TO DATABASE FOR NEXT TIME CHECK OR HOLD VALUE IN XML/TXT FILE. OR FLAG TABLE VALUE. ******
Despite all out desires, it may not be possible for something to start at 15:17. The box
may be busy doing something else that has control. That's the main reason for allowing
a start window instead of a hard, fixed time. In either case, however, one needs to specify
what the scheduler should do if an event window is missed.
Basicly, this means you need to define how you want to define a scheduled event. Implicit therein is also the question of interface - text file, GUI, whatever.
****** THE GUI SHOULD BE ABLE TO CHECK DATABASE VALUES AND RELATIONSHIPS.. .BUT BE SIMPLE ... AND UPDATE THE SCHEDULER AND TASKS TO PERFORM... ******
Thanks.
Scheduling Requirement Decisions
1) To what day level do you want to schedule events?
Daily? Day of week? Specific date?
****** THE PROGRAM NEEDS TO RUN DAILY ***
Do you want scheduled events to repeat? If so, then such events would have start times
such as MONDAY/23:30, DAILY/16:45, FIRSTOFMONTH/10:00
Single executions might be defined like 2003/12/25-00:01
Other event times might be things like the third Tuesday of each month.
****** DEPENDING ON THE MONTH (NOT JAN TO FEB, BUT COMPANY PERIODS/MONTHS)... EACH DAY COULD HAVE DIFFERENT NUMBER OF EVENTS PLANNED. SO MONTH 3 COULD HAVE 6 EVENTS PER DAY, WHILE MONTH 4-7 HAVE 3 EVENTS, AND THE REST 5 EVENTS.
ALSO THE ISSUE IS SINCE WE ARE TRYING TO ALIGN MONTH 1 TO JAN OVER THE NEXT 3-4 YEARS, (THIS IS NOT ACCOUNTING PRACTICE .. JUST TRYING TO MOVE FROM COMPANY ORIGINATION TIME FROM MID YEAR TO BEGINNING OF ROMAN CALENDER. SO THIS YEAR, MONTH 3 IS JUNE 15 - JULY 14, BUT FOR NEXT YEAR (DEPENDING ON A NUMBER OF DAYS) THE TIME HAS TO BE MOVED BACK. SO IF I PICK 60 AS THE COUNT, THEN MONTH 3 SHOULD REPRESENT APRIL 15 - MAY 14 (ROUGHLY SPEAKING)... BASICALLY TO REALIGN THE SCHEDULER WITH THE MONTH CHANGES.. THIS WAY MNTH HAS 6 EVENTS (LIKE ABOVE).. BUT IT DOES NOT MATTER THAT THIS YEAR IT WAS FROM JUNE AND NEXT YEAR WILL BE APRIL ( THE DATE DIFFERENCE TIME WILL BE HARDCODED, BUT I WILL NEED TO KNOW WHERE TO CHANGE). ******
2) What time granularity do you want to specify?
Do you want to specify exact times? Time windows?
****** EXACT TIMES NEEDED ******
Some events may have to be scheduled for precise times, e.g., 23:56.
Others may be more lax - anytime between 23:00 and 23:55.
3) If an event window is missed, what happens?
Run right away? Wait until next window?
****** IF MISSED, NO PROBLEM, GO TO NEXT ONE... NOT SURE IF IT IS WORTH GOING TO DATABASE FOR NEXT TIME CHECK OR HOLD VALUE IN XML/TXT FILE. OR FLAG TABLE VALUE. ******
Despite all out desires, it may not be possible for something to start at 15:17. The box
may be busy doing something else that has control. That's the main reason for allowing
a start window instead of a hard, fixed time. In either case, however, one needs to specify
what the scheduler should do if an event window is missed.
Basicly, this means you need to define how you want to define a scheduled event. Implicit therein is also the question of interface - text file, GUI, whatever.
****** THE GUI SHOULD BE ABLE TO CHECK DATABASE VALUES AND RELATIONSHIPS.. .BUT BE SIMPLE ... AND UPDATE THE SCHEDULER AND TASKS TO PERFORM... ******
ASKER
I will only give points for code...
I am running out of time..
Some take on this task. Thanks.
I am running out of time..
Some take on this task. Thanks.
Sorry for the delay - I got sick and was in the hospital.
Given your time contraints, you should consider an off the shelf package.
I would estimate that writting this would take several weeks - and that's presuming a conversion of one that I have for NetWare written in Rexx.
Given your time contraints, you should consider an off the shelf package.
I would estimate that writting this would take several weeks - and that's presuming a conversion of one that I have for NetWare written in Rexx.
ASKER
Cookre ... no problem...
Hope you are feeling well.
As for of the shelf.. what do you recommend?
Thanks.
Hope you are feeling well.
As for of the shelf.. what do you recommend?
Thanks.
ASKER
Guys... I would prefer customized code.. but will look into the packages if anyknows any good cheap ones.
Thanks.
Thanks.
If you can read Rexx, I edited down my scheduler to ~800 lines (removed the NetWare specific things).
Interested?
Interested?
ASKER
Cookre...
I don't know REXX... however if you think the code is easy to read or can be understood by someone who is a vb/asp programmer and who knows bits of Perl/Smalltalk...
again.. i don't know rexx..but am willing to try it.
thanks
I don't know REXX... however if you think the code is easy to read or can be understood by someone who is a vb/asp programmer and who knows bits of Perl/Smalltalk...
again.. i don't know rexx..but am willing to try it.
thanks
I'll post it tonight (US EST) when I get to the box that has it...
ASKER
Cookre...
I did not receive the Rexx code... let me know.
thanks.
I did not receive the Rexx code... let me know.
thanks.
I do apologize - that and several thing simply disappeared from my pea brain. Here it is:
(A lot of code onapplicable to this discussion has been removed)
/* Every WaitTime seconds, the program looks at sched.INI and
** processes each group therein. For each group (command), the
** history file is read to see if its time to execute that command.
** (Commands are identified by the CommandName in the group header.)
**
** If a command is executed, its history record is updated or created.
**
** Each command thats been executed has one history record of the form:
** <CommandName>=<LastExecuti onDate as YYYYMMDD><LastExecutionTim e as HHMM>
*/
arg CmdParm
parse upper value CmdParm with WorkDir "," OptList
StartDate=date("S")
StartTime=time("N")
SchedVer="4.4"
WaitTime=60 /* Sleep interval in seconds */
CR="0d"x
LF="0a"x
/* WorkDir = "N/A" - now taken from command line */
HistFile = WorkDir || "sched.hst"
IniFile = WorkDir || "sched.ini"
LocalIni = WorkDir || "sched.ili"
SchedLog = WorkDir || "sched.log"
rc=stream(SchedLog,"c","qe ")
if rc\=1 then do
rc=stream(SchedLog,"c","cr w")
if rc\=0 then do
msg="Unable to create log file"
call ShowWait
exit
end
rc=stream(SchedLog,"c","cl ose")
end
if 0>stream(SchedLog,"c","ope n") then do
msg="Unable to open log file"
call ShowWait
exit
end
/*
** Initialize environment variables that won't change during execution
*/
FSN=""
FSN83=""
...snip
ShowSolidarity=0
/*
** Wake up every now and then to see what to do
*/
DidALogin=0
do forever
drop IniData
drop ThingToShift
drop BothInis
if (ShowSolidarity>ShowTime) then do
msg="sched v" || SchedVer || " still active from " || WorkDir
call LogIt
ShowSolidarity=0
/* While we're at it, let's keep the logfile size down */
rc=stream(SchedLog,"c","cl ose")
rc=stream(SchedLog,"c","op en")
SchedLogSize=lines(SchedLo g)
if SchedLogSize>2000 then do
/* We check for 2000, but chop to 1000 - this means we're */
/* constantly chopping little amounts */
ChopPoint=SchedLogSize-100 0
/* Skip over all but the last 1000 lines */
do i=1 to ChopPoint
TrashIt=linein(SchedLog)
end
RecentSchedLog.0=0
do i=ChopPoint+1 to SchedLogSize
idx=i-ChopPoint
RecentSchedLog.idx=linein( SchedLog)
RecentSchedLog.0=idx
end
/* Recreate SchedLog with the recent 1000 lines */
rc=stream(SchedLog,"c","cl ose")
rc=stream(SchedLog,"c","cr eate")
AttrRec.MFileAttributesMas k = -1
AttrRec.MFileAttributes = 65536
rc=ChangeDirectoryEntry(Sc hedLog,"At trRec.",2, 0)
do i=1 to RecentSchedLog.0
call lineout SchedLog,RecentSchedLog.i
end
end
end
/*
** Step through INI file for things to do
*/
rc=stream(IniFile,"c","ope n read")
if rc\=0 then do
msg="Unable to open " || IniFile
call LogIt
rc=pause(WaitTime)
ShowSolidarity=ShowSolidar ity+WaitTi me
iterate
end
/*
** Load up a copy of the INI file
*/
IniFileChars=chars(IniFile )
IniData=charin(IniFile,1,I niFileChar s)
rc=stream(IniFile,"c","clo se")
...snip
FileSize=IniFileChars
i=1
/* Add a plain text CRLF at the end in case the last line didn't have one */
IniData=IniData||CR||LF
IniFileChars=IniFileChars+ 2
BothInis.0=0
i=1
do while i<=IniFileChars
/* Concatenate non-line terminators into current line */
BothInis.0=1+BothInis.0
CurIdx=BothInis.0
BothInis.CurIdx=""
CurChar=substr(IniData,i,1 )
do while CurChar\=CR & CurChar\=LF
BothInis.CurIdx=BothInis.C urIdx || CurChar
i=i+1
CurChar=substr(IniData,i,1 )
end
/* We're at the end of a line */
do while CurChar=CR | CurChar=LF
i=i+1
CurChar=substr(IniData,i,1 )
end
end
/*
** Now step through our copy of the INI file
*/
IniFileSize=BothInis.0
IniEof=0
IniLineNo=1
IniLine=translate(BothInis .IniLineNo )
do while IniEof==0
/*
** Initialize group fields
*/
CmdName=""
CmdType=""
CmdUser=""
CmdPassword=""
CmdServer=""
CmdNLM=""
CmdDate=""
CmdTime=""
CmdFrom=""
CmdTo=""
CmdFile=""
CmdRen=""
CmdWaitFile=""
CmdText=""
CmdRunOnce=""
CmdCopyNewOnly=""
call LoadGroup
call ExecuteGroup
if DidALogin\=0 then leave
end
/*
** Wait a bit till next pass
*/
rc=pause(WaitTime)
ShowSolidarity=ShowSolidar ity+WaitTi me
end
msg="End sched"
call LogIt
exit
/************************* **** LoadGroup ************************** ******/
LoadGroup:
/*
** Initialize group fields
** Upon entry, the first line of the group is loaded
*/
call SetENV /* Replace environment vars in IniLine */
CmdName=strip(strip(IniLin e,,"["),," ]")
IniLineNo=IniLineNo+1
IniLine=translate(BothInis .IniLineNo )
/*
** Keep scanning til we hit EOF or the next [...] line
*/
do forever
IniLine=strip(IniLine)
call SetENV
/*
** Tell the caller we got EOF
*/
if IniLineNo>IniFileSize then do
IniEOF=1
leave
end
/*
** Check for start of next group
*/
if substr(IniLine,1,1)=="[" then leave
/*
** Skip blank lines
*/
if 0==length(IniLine) then do
IniLineNo=IniLineNo+1
IniLine=translate(BothInis .IniLineNo )
iterate
end
/*
** Skip comment lines
*/
if left(IniLine,1)==";" then do
IniLineNo=IniLineNo+1
IniLine=translate(BothInis .IniLineNo )
iterate
end
/*
** Its something to look at
*/
EqualPos=pos("=",IniLine)
if 2>EqualPos then do
/* Ignore bad lines for now */
IniLineNo=IniLineNo+1
IniLine=translate(BothInis .IniLineNo )
iterate
end
PartName=left(IniLine,Equa lPos-1)
PartArg=right(IniLine,leng th(IniLine )-EqualPos )
/*
** Now let's see what we got
*/
PartName=translate(PartNam e)
select
when PartName="TYPE" then do
CmdType=PartArg
end
when PartName="DATE" then do
CmdDate=PartArg
end
when PartName="TIME" then do
CmdTime=PartArg
end
when PartName="RUNONCE" then do
CmdRunOnce=PartArg
end
when PartName="COPYNEWONLY" then do
CmdCopyNewOnly=PartArg
end
when PartName="CMD" then do
CmdText=CmdText || PartArg
end
when PartName="FROM" then do
CmdFrom=PartArg
if right(CmdFrom,1)="\" then do
CmdFrom=left(CmdFrom,lengt h(CmdFrom) -1)
end
end
when PartName="TO" then do
CmdTo=PartArg
if right(CmdTo,1)="\" then do
CmdTo=left(CmdTo,length(Cm dTo)-1)
end
end
when PartName="FILE" then do
CmdFile=PartArg
end
when PartName="RENAME" then do
CmdRen=PartArg
end
when PartName="WAITFILE" then do
CmdWaitFile=PartArg
end
otherwise do
msg="Unrecognized line ignored:<" || IniLine || ">"
call LogIt
end
end
/*
** Get next line
*/
IniLineNo=IniLineNo+1
IniLine=translate(BothInis .IniLineNo )
end
return
/************************* * ExecuteGroup ************************** *******/
ExecuteGroup:
/*
** See if this event qualifies
*/
if 0=DoWeRunIt() then return
/*
** The event qualifies for execution, now check the HST file for:
** 1) If it's a RUNONCE and in the HST file, don't run it again
** 2) The call to DoWeRunIt leaves the qualified interval in TimeInterval
** and the qualified day in TODAY. Look thru the HST file for
** an entry with the qualified day and interval whose date is today's.
** 3) If the qualified window was a masked hourly on (*xx-*yy), replace
** the *s in TimeInterval with the current hour. This will give us
** something to put in HST to reflect this instance of the event.
** (If the masked interval were to go in HST, we would only get
** one execution per day.)
*/
if left(TimeInterval,1)="*" then do
/* NOW is set in InWindow */
HH=left(NOW,2)
TimeInterval=HH || substr(TimeInterval,2,3) || HH || right(TimeInterval,2)
end
GroupDate=date("I")
EntryFound=0
HistRecs.0=0
rc=stream(HistFile,"c","op en")
if rc=0 then do
HistLen=lines(HistFile)
if HistLen>0 then do
/* TARGET is the qualified EventName,DayName,Interval */
/* Search for an instance from today while loading HST */
Target=CmdName||","||TODAY ||","||Tim eInterval| |","||Grou pDate
if CmdRunOnce="YES" then Target=CmdName
TargetSize=length(Target)
do HistIdx=1 to HistLen
HistLine=linein(HistFile)
HistRecs.HistIdx=HistLine
CommaPos=pos(",",HistLine)
if CommaPos=0 then iterate
if left(HistLine,TargetSize)\ =Target then iterate
EntryFound=1
end
HistRecs.0=HistLen
end
rc=stream(HistFile,"c","cl ose")
end
/* If we found our target, don't run it again */
if EntryFound>0 then return
/*
** Looks like we gotta do it
*/
msg="Processing <" || CmdName || ">"
call LogIt
/* Build list of servers, if any, this command is to work with */
if CmdServer\="" & "*"=left(CmdServer,1) then do
......
end
else do
/* Server= is either empty or not an indirect file */
ServerList.0=1
ServerList.1=CmdServer
end
/* Save the original TO and FROM strings, since environment variable */
/* substitution changes them (not nice if SERVER=*something). */
HoldCmdTo=CmdTo
HoldCmdFrom=CmdFrom
HoldCmdRen=CmdRen
/* Apply command for each server specified (blank or not) */
do SvrIdx=1 to ServerList.0
CmdServer=ServerList.SvrId x
if length(CmdServer)>1 then do
rc=DoAttach()
if rc\=0 then return
end
select
when translate(CmdType)="CONSOL E" then do
call ExecuteConsoleCmd
end
when translate(CmdType)="COPY" then do
call ExecuteCopyCmd
CmdTo=HoldCmdTo
CmdFrom=HoldCmdFrom
CmdRen=HoldCmdRen
end
otherwise do
msg="Command <" || CmdName || "> has unknown type <" || CmdType || ">"
call LogIt
end
end
if length(CmdServer)>1 then do
call ClearRemote
end
end
rc=NWDSLogout()
/* Update HST file */
rc=stream(HistFile,"c","cr eate")
if rc\=0 then do
msg="Unable to re-create " || HistFile
call LogIt
return
end
if HistRecs.0>0 then do
CmdNameLen=length(CmdName)
do HistIdx=1 to HistRecs.0
/* Dont keep old instance of this event, if any. */
/* Note that we only need the most recent instance */
/* of *xx-*yy events ('cause time don't go backwards) */
if left(HistRecs.HistIdx,CmdN ameLen)=Cm dName then iterate
rc=lineout(HistFile,HistRe cs.HistIdx )
end
end
NewLine=CmdName||","||TODA Y||","||Ti meInterval ||","||Gro upDate
rc=lineout(HistFile,NewLin e)
rc=stream(HistFile,"c","cl ose")
return
/************************* **** DoWeRunIt ************************** ******/
DoWeRunIt:
/* Return 0 to say not to run event */
if length(CmdDate)<3 then do
msg="Invalid DATE=<" || CmdDate || "> from event <" || CmdName || ">"
call LogIt
return 0
end
/*
** Look for WaitFiles
*/
if length(CmdWaitFile)>0 then do
rc=stream(CmdWaitFile,"C", "QE")
if rc\=1 then return 0
end
/* Get today's abbreviation */
TODAY=translate(left(date( "W"),3))
/* See if we run today */
if 0=pos(TODAY,CmdDate) then return 0
/* See if we fall in one of the time windows */
/* First get current time as HHMM */
NOW=time("N")
NOW=left(NOW,2) || substr(NOW,4,2)
/* Now compare NOW against each window */
TimeWindows=CmdTime
TimeInterval=""
ScanDone=0
parse upper value TimeWindows with TimeInterval "," TimeWindows
if length(TimeWindows)<1 then ScanDone=1
if 0\=InWindow(NOW TimeInterval) then return 1
do while ScanDone=0
parse upper value TimeWindows with TimeInterval "," TimeWindows
if length(TimeWindows)<1 then ScanDone=1
if 0\=InWindow(NOW TimeInterval) then return 1
end
/* Guess we're not in one of the windows */
return 0
/************************* InWindow ************************** **/
/* Return 0 if not in window, 1 otherwise */
/* First arg is time to check as HHMM */
/* Second arg is an interval in the for HHMM-HHMM */
/* Special case *mm-*mm allows simple hourly specification */
InWindow:
arg HHMM Interval
HH=left(HHMM,2)
MM=right(HHMM,2)
DashPos=pos("-",Interval)
if DashPos<3 then do
msg="Invalid TIME=" || CmdTime
call LogIt
return 0
end
if length(Interval)<(DashPos+ 3) then do
msg="Bad TIME=" || CmdTime
call LogIt
return 0
end
/* Firm up the interval start */
IntBeg=left(Interval,DashP os-1)
if length(IntBeg)=3 then do
if left(IntBeg,1)\="*" then do
msg="Invalid interval start " || IntBeg
call LogIt
return 0
end
/* This guy is automatically in the current hour */
IntBeg=HH || right(IntBeg,2)
end
/* Do the same thing for interval end */
IntEnd=substr(Interval,Das hPos+1)
if length(IntEnd)=3 then do
if left(IntEnd,1)\="*" then do
msg="Invalid interval end " || IntEnd
call LogIt
return 0
end
/* This guy is automatically in the current hour */
IntEnd=HH || right(IntEnd,2)
end
if IntBeg<=HHMM & HHMM<=IntEnd then return 1
return 0
/************************* ExecuteConsoleCmd ************************** **/
ExecuteConsoleCmd:
......
return
/************************* * ExecuteCopyCmd ************************** ****/
ExecuteCopyCmd:
/* Make sure we have at least one file to copy */
CmdFrom=DynEnv(CmdFrom)
FileMask=CmdFrom||"\"||Cmd File
NumFiles=ListFile(FileMask ,"FileList .")
if NumFiles=0 then do
msg="File(s) not found [" || CmdName || "]: " || FileMask
call LogIt
call ClearRemote
return
end
/* Make sure destination directory exists */
CmdTo=DynEnv(CmdTo)
PathToVerify=CmdTo
rc=VerifyPath()
if rc\=0 then do
msg="Copy aborted - unable to create destination (" || rc || ")"
call LogIt
call ClearRemote
return
end
/* Now we copy each file that matches the mask */
FileIdx=1
do while FileIdx<=NumFiles
curfile=strip(FileList.Fil eIdx)
if length(curfile)>5 then do
if right(curfile,5)="<DIR>" then do
FileIdx=FileIdx+1
iterate
end
end
SourceFile=CmdFrom || "\" || curfile
DestFile =CmdTo || "\" || curfile
if length(CmdRen)>0 then do
/* Change destination filename, if needed */
CmdRen=DynEnv(CmdRen)
DestFile=CmdTo || "\" || CmdRen
end
DestCmd ="CO " || DestFile
/* Handle CopyNewOnly=YES */
if translate(CmdCopyNewOnly)= "YES" & length(CmdRen)=0 then do
/* Only copy the file if it's newer */
IsItOne=ListFile(DestFile, "DestList. ")
if IsItOne=1 then do
/* Save destination date/time stamp */
DestStamp=right(DestList.1 ,19)
/* Get source date/time stamp */
IsItOne=ListFile(SourceFil e,"DestLis t.")
if IsItOne=1 then do
SourceStamp=right(DestList .1,19)
if SourceStamp<=DestStamp then do
msg="[" || CmdName || "] " || SourceFile || " not newer, copy skipped"
call LogIt
FileIdx=FileIdx+1
iterate
end
end
end
end
/* Finally - we can do the copy */
rc=stream(SourceFile,"C",D estCmd)
/* Try up to three times if it failed */
RetryCount=0
do while (rc\=0) & (RetryCount<3)
RetryCount=RetryCount+1
msg="[" || CmdName || "] failed (" || rc || ") Retry " || RetryCount
call LogIt
Dummy=pause(30)
rc=stream(SourceFile,"C",D estCmd)
end
if rc\=0 then do
msg="[" || CmdName || "] copy aborted (" || SourceFile || " to " || DestFile || ") (" || rc || ")"
call LogIt
call ClearRemote
return
end
msg="[" || CmdName || "] - OK"
call LogIt
FileIdx=FileIdx+1
end
return
/************************* ******** LogIt ************************** ******/
Logit:
say date()||" "||time()||" "||msg
call lineout SchedLog,date()||" "||time()||" "||msg
return
/************************* ******** ShowWait ************************** ******/
/* Display a message then wait for a key or until timeout */
ShowWait:
DelayCtr=60
say msg
rc=GetCursor("CursorPos.")
do while DelayCtr>0
rc=SetCursor(CursorPos.row ,CursorPos .column)
say "Press CTRL-C or wait " || DelayCtr || " seconds... "
rc=pause(1)
DelayCtr=DelayCtr-1
end
return
/************************* ****** VerifyPath ************************** ****/
VerifyPath:
/* Create the path in <PathToVerify> if it doesn't exist */
/* Edit locally */
Remainder=PathToVerify
/* Standardize slashes */
Remainder=strip(translate( Remainder, "/","\"))
/* Extract server name */
VerifyServer=translate(lef t(Remainde r,11))
/* Start off by stripping out the drive portion */
parse upper value Remainder with LeftPart ":/" Remainder
PathParts.0=1
PathParts.1=LeftPart || ":"
/* Split out the remaining parts */
do while length(Remainder)\=0
PathParts.0=1+PathParts.0
CurrentPart=PathParts.0
parse upper value Remainder with LeftPart "/" Remainder
PathParts.CurrentPart=Left Part
end
/* Now that we have the parts, find the first missing directory, if any */
/* VerifyPath is called after we've logged into the tree and attached */
/* to the remote server, if any. If the destination directory needs */
/* to be created and it is back on the local server, we have to */
/* switch back to the local server for the MKDIR to work. */
DestOnLocal=0
if length(CmdServer)=11 & (VerifyServer\=translate(C mdServer)) then do
/* We have to temporarily switch back to the local server */
DestOnLocal=1
HoldConn=GetCurrentConnect ion()
HoldFSID=GetCurrentFileSer verID()
rcl=SetCurrentFileServerID (0)
rcl=SetCurrentConnection(0 )
end
/* Start one level below the root of the volume */
CurrPath=PathParts.1
if PathParts.0>1 then do
do idx=2 to PathParts.0
CurrPath=CurrPath || PathParts.idx
/* See if this path exists */
/* We used to create a fixed file name, but found out that */
/* Netware doesn't like a bazillion identical names, sooo */
/* we create a filename CCCCnnnS.SUU where: */
/* CCCC is the city code from the server name */
/* nnn is the right-most three chars from server name */
/* S.SUU are the seconds and miliseconds from the current time */
/* Although this will still have some collisions, it won't be near */
/* as many as with a fixed file name. */
SSDotUU=substr(time("L"),7 ,5)
SvrPart=left(VerifyServer, 4) || right(VerifyServer,3)
FileName=SvrPart || left(SSDotUU,1)
ExtName=substr(SSDotUU,2,1 ) || right(SSDotUU,2)
FileName=FileName || "." || ExtName
FileName=CurrPath || "\" || FileName
rcl=stream(FileName,"c","c reate")
if rcl\=0 then do
/* Nope, let's make it */
rcl=stream(CurrPath,"c","m kdir")
if rcl\=0 then do
msg="Unable to create <" || CurrPath || "> (" || rcl || ")"
call LogIt
if DestOnLocal=1 then do
/* Switch back to the remote, if needed */
rcl=SetCurrentFileServerID (HoldFSID)
rcl=SetCurrentConnection(H oldConn)
DestOnLocal=0
end
return 1
end
end
/* Close the name so we don't interfere with a later pass */
rcl=stream(FileName,"c","c lose")
rcl=stream(FileName,"c","d elete")
CurrPath=CurrPath || "\"
end
end
return 0
/************************* ****** SetENV ************************** ****/
SetENV:
/*
** Replace environment variables in IniLine
**
** Recognizes: FSN - File Server Name
** FSN83 - File Server Name in 8.3 format
**
**
** Scan INILine for all instances of known %env% and replace with
** appropriate value. Note our presumption that INILine has already
** been converted to upper case.
*/
/* Look for FileServerName */
vpos=pos("%FSN%",IniLine)
do while vpos>0
/* We have a hit, find it's start */
tpos=pos("%FSN%",IniLine)
/* Extract whatever is to the left of %FSN% */
LeftPart=""
if tpos>1 then LeftPart=left(INILine,tpos -1)
/* Now extract whatever is to the right of %FSN% */
RightPart=""
if tpos<(length(IniLine)-4) then RightPart=right(INILine,1+ (length(IN ILine)-(tp os+5)))
/* Reconstruct INILine, using value for FSN */
INILine=Leftpart || FSN || RightPart
vpos=pos("%FSN%",IniLine)
end
/* Look for File Server Name in 8.3 format */
vpos=pos("%FSN83%",IniLine )
do while vpos>0
tpos=pos("%FSN83%",IniLine )
LeftPart=""
if tpos>1 then LeftPart=left(INILine,tpos -1)
RightPart=""
if tpos<(length(IniLine)-6) then RightPart=right(INILine,1+ (length(IN ILine)-(tp os+7)))
INILine=Leftpart || FSN83 || RightPart
vpos=pos("%FSN83%",IniLine )
end
return
/************************* ****** DynENV ************************** ****/
/* Replace dynamic environment variables in input string */
DynENV:
arg StringIn
/* Replace %*% with remote server name */
vpos=pos("%*%",StringIn)
do while vpos>0
tpos=pos("%*%",StringIn)
LeftPart=""
if tpos>1 then LeftPart=left(StringIn,tpo s-1)
RightPart=""
if tpos<(length(StringIn)-2) then RightPart=right(StringIn,1 +(length(S tringIn)-( tpos+3)))
StringIn=Leftpart || CmdServer || RightPart
vpos=pos("%*%",StringIn)
end
/* Replace %*83% with remote server name in 8.3 format */
vpos=pos("%*83%",StringIn)
CmdServer83=left(CmdServer ,8) || "." || right(CmdServer,3)
do while vpos>0
tpos=pos("%*83%",StringIn)
LeftPart=""
if tpos>1 then LeftPart=left(StringIn,tpo s-1)
RightPart=""
if tpos<(length(StringIn)-2) then RightPart=right(StringIn,1 +(length(S tringIn)-( tpos+5)))
StringIn=Leftpart || CmdServer83 || RightPart
vpos=pos("%*83%",StringIn)
end
return StringIn
(A lot of code onapplicable to this discussion has been removed)
/* Every WaitTime seconds, the program looks at sched.INI and
** processes each group therein. For each group (command), the
** history file is read to see if its time to execute that command.
** (Commands are identified by the CommandName in the group header.)
**
** If a command is executed, its history record is updated or created.
**
** Each command thats been executed has one history record of the form:
** <CommandName>=<LastExecuti
*/
arg CmdParm
parse upper value CmdParm with WorkDir "," OptList
StartDate=date("S")
StartTime=time("N")
SchedVer="4.4"
WaitTime=60 /* Sleep interval in seconds */
CR="0d"x
LF="0a"x
/* WorkDir = "N/A" - now taken from command line */
HistFile = WorkDir || "sched.hst"
IniFile = WorkDir || "sched.ini"
LocalIni = WorkDir || "sched.ili"
SchedLog = WorkDir || "sched.log"
rc=stream(SchedLog,"c","qe
if rc\=1 then do
rc=stream(SchedLog,"c","cr
if rc\=0 then do
msg="Unable to create log file"
call ShowWait
exit
end
rc=stream(SchedLog,"c","cl
end
if 0>stream(SchedLog,"c","ope
msg="Unable to open log file"
call ShowWait
exit
end
/*
** Initialize environment variables that won't change during execution
*/
FSN=""
FSN83=""
...snip
ShowSolidarity=0
/*
** Wake up every now and then to see what to do
*/
DidALogin=0
do forever
drop IniData
drop ThingToShift
drop BothInis
if (ShowSolidarity>ShowTime) then do
msg="sched v" || SchedVer || " still active from " || WorkDir
call LogIt
ShowSolidarity=0
/* While we're at it, let's keep the logfile size down */
rc=stream(SchedLog,"c","cl
rc=stream(SchedLog,"c","op
SchedLogSize=lines(SchedLo
if SchedLogSize>2000 then do
/* We check for 2000, but chop to 1000 - this means we're */
/* constantly chopping little amounts */
ChopPoint=SchedLogSize-100
/* Skip over all but the last 1000 lines */
do i=1 to ChopPoint
TrashIt=linein(SchedLog)
end
RecentSchedLog.0=0
do i=ChopPoint+1 to SchedLogSize
idx=i-ChopPoint
RecentSchedLog.idx=linein(
RecentSchedLog.0=idx
end
/* Recreate SchedLog with the recent 1000 lines */
rc=stream(SchedLog,"c","cl
rc=stream(SchedLog,"c","cr
AttrRec.MFileAttributesMas
AttrRec.MFileAttributes = 65536
rc=ChangeDirectoryEntry(Sc
do i=1 to RecentSchedLog.0
call lineout SchedLog,RecentSchedLog.i
end
end
end
/*
** Step through INI file for things to do
*/
rc=stream(IniFile,"c","ope
if rc\=0 then do
msg="Unable to open " || IniFile
call LogIt
rc=pause(WaitTime)
ShowSolidarity=ShowSolidar
iterate
end
/*
** Load up a copy of the INI file
*/
IniFileChars=chars(IniFile
IniData=charin(IniFile,1,I
rc=stream(IniFile,"c","clo
...snip
FileSize=IniFileChars
i=1
/* Add a plain text CRLF at the end in case the last line didn't have one */
IniData=IniData||CR||LF
IniFileChars=IniFileChars+
BothInis.0=0
i=1
do while i<=IniFileChars
/* Concatenate non-line terminators into current line */
BothInis.0=1+BothInis.0
CurIdx=BothInis.0
BothInis.CurIdx=""
CurChar=substr(IniData,i,1
do while CurChar\=CR & CurChar\=LF
BothInis.CurIdx=BothInis.C
i=i+1
CurChar=substr(IniData,i,1
end
/* We're at the end of a line */
do while CurChar=CR | CurChar=LF
i=i+1
CurChar=substr(IniData,i,1
end
end
/*
** Now step through our copy of the INI file
*/
IniFileSize=BothInis.0
IniEof=0
IniLineNo=1
IniLine=translate(BothInis
do while IniEof==0
/*
** Initialize group fields
*/
CmdName=""
CmdType=""
CmdUser=""
CmdPassword=""
CmdServer=""
CmdNLM=""
CmdDate=""
CmdTime=""
CmdFrom=""
CmdTo=""
CmdFile=""
CmdRen=""
CmdWaitFile=""
CmdText=""
CmdRunOnce=""
CmdCopyNewOnly=""
call LoadGroup
call ExecuteGroup
if DidALogin\=0 then leave
end
/*
** Wait a bit till next pass
*/
rc=pause(WaitTime)
ShowSolidarity=ShowSolidar
end
msg="End sched"
call LogIt
exit
/*************************
LoadGroup:
/*
** Initialize group fields
** Upon entry, the first line of the group is loaded
*/
call SetENV /* Replace environment vars in IniLine */
CmdName=strip(strip(IniLin
IniLineNo=IniLineNo+1
IniLine=translate(BothInis
/*
** Keep scanning til we hit EOF or the next [...] line
*/
do forever
IniLine=strip(IniLine)
call SetENV
/*
** Tell the caller we got EOF
*/
if IniLineNo>IniFileSize then do
IniEOF=1
leave
end
/*
** Check for start of next group
*/
if substr(IniLine,1,1)=="[" then leave
/*
** Skip blank lines
*/
if 0==length(IniLine) then do
IniLineNo=IniLineNo+1
IniLine=translate(BothInis
iterate
end
/*
** Skip comment lines
*/
if left(IniLine,1)==";" then do
IniLineNo=IniLineNo+1
IniLine=translate(BothInis
iterate
end
/*
** Its something to look at
*/
EqualPos=pos("=",IniLine)
if 2>EqualPos then do
/* Ignore bad lines for now */
IniLineNo=IniLineNo+1
IniLine=translate(BothInis
iterate
end
PartName=left(IniLine,Equa
PartArg=right(IniLine,leng
/*
** Now let's see what we got
*/
PartName=translate(PartNam
select
when PartName="TYPE" then do
CmdType=PartArg
end
when PartName="DATE" then do
CmdDate=PartArg
end
when PartName="TIME" then do
CmdTime=PartArg
end
when PartName="RUNONCE" then do
CmdRunOnce=PartArg
end
when PartName="COPYNEWONLY" then do
CmdCopyNewOnly=PartArg
end
when PartName="CMD" then do
CmdText=CmdText || PartArg
end
when PartName="FROM" then do
CmdFrom=PartArg
if right(CmdFrom,1)="\" then do
CmdFrom=left(CmdFrom,lengt
end
end
when PartName="TO" then do
CmdTo=PartArg
if right(CmdTo,1)="\" then do
CmdTo=left(CmdTo,length(Cm
end
end
when PartName="FILE" then do
CmdFile=PartArg
end
when PartName="RENAME" then do
CmdRen=PartArg
end
when PartName="WAITFILE" then do
CmdWaitFile=PartArg
end
otherwise do
msg="Unrecognized line ignored:<" || IniLine || ">"
call LogIt
end
end
/*
** Get next line
*/
IniLineNo=IniLineNo+1
IniLine=translate(BothInis
end
return
/*************************
ExecuteGroup:
/*
** See if this event qualifies
*/
if 0=DoWeRunIt() then return
/*
** The event qualifies for execution, now check the HST file for:
** 1) If it's a RUNONCE and in the HST file, don't run it again
** 2) The call to DoWeRunIt leaves the qualified interval in TimeInterval
** and the qualified day in TODAY. Look thru the HST file for
** an entry with the qualified day and interval whose date is today's.
** 3) If the qualified window was a masked hourly on (*xx-*yy), replace
** the *s in TimeInterval with the current hour. This will give us
** something to put in HST to reflect this instance of the event.
** (If the masked interval were to go in HST, we would only get
** one execution per day.)
*/
if left(TimeInterval,1)="*" then do
/* NOW is set in InWindow */
HH=left(NOW,2)
TimeInterval=HH || substr(TimeInterval,2,3) || HH || right(TimeInterval,2)
end
GroupDate=date("I")
EntryFound=0
HistRecs.0=0
rc=stream(HistFile,"c","op
if rc=0 then do
HistLen=lines(HistFile)
if HistLen>0 then do
/* TARGET is the qualified EventName,DayName,Interval
/* Search for an instance from today while loading HST */
Target=CmdName||","||TODAY
if CmdRunOnce="YES" then Target=CmdName
TargetSize=length(Target)
do HistIdx=1 to HistLen
HistLine=linein(HistFile)
HistRecs.HistIdx=HistLine
CommaPos=pos(",",HistLine)
if CommaPos=0 then iterate
if left(HistLine,TargetSize)\
EntryFound=1
end
HistRecs.0=HistLen
end
rc=stream(HistFile,"c","cl
end
/* If we found our target, don't run it again */
if EntryFound>0 then return
/*
** Looks like we gotta do it
*/
msg="Processing <" || CmdName || ">"
call LogIt
/* Build list of servers, if any, this command is to work with */
if CmdServer\="" & "*"=left(CmdServer,1) then do
......
end
else do
/* Server= is either empty or not an indirect file */
ServerList.0=1
ServerList.1=CmdServer
end
/* Save the original TO and FROM strings, since environment variable */
/* substitution changes them (not nice if SERVER=*something). */
HoldCmdTo=CmdTo
HoldCmdFrom=CmdFrom
HoldCmdRen=CmdRen
/* Apply command for each server specified (blank or not) */
do SvrIdx=1 to ServerList.0
CmdServer=ServerList.SvrId
if length(CmdServer)>1 then do
rc=DoAttach()
if rc\=0 then return
end
select
when translate(CmdType)="CONSOL
call ExecuteConsoleCmd
end
when translate(CmdType)="COPY" then do
call ExecuteCopyCmd
CmdTo=HoldCmdTo
CmdFrom=HoldCmdFrom
CmdRen=HoldCmdRen
end
otherwise do
msg="Command <" || CmdName || "> has unknown type <" || CmdType || ">"
call LogIt
end
end
if length(CmdServer)>1 then do
call ClearRemote
end
end
rc=NWDSLogout()
/* Update HST file */
rc=stream(HistFile,"c","cr
if rc\=0 then do
msg="Unable to re-create " || HistFile
call LogIt
return
end
if HistRecs.0>0 then do
CmdNameLen=length(CmdName)
do HistIdx=1 to HistRecs.0
/* Dont keep old instance of this event, if any. */
/* Note that we only need the most recent instance */
/* of *xx-*yy events ('cause time don't go backwards) */
if left(HistRecs.HistIdx,CmdN
rc=lineout(HistFile,HistRe
end
end
NewLine=CmdName||","||TODA
rc=lineout(HistFile,NewLin
rc=stream(HistFile,"c","cl
return
/*************************
DoWeRunIt:
/* Return 0 to say not to run event */
if length(CmdDate)<3 then do
msg="Invalid DATE=<" || CmdDate || "> from event <" || CmdName || ">"
call LogIt
return 0
end
/*
** Look for WaitFiles
*/
if length(CmdWaitFile)>0 then do
rc=stream(CmdWaitFile,"C",
if rc\=1 then return 0
end
/* Get today's abbreviation */
TODAY=translate(left(date(
/* See if we run today */
if 0=pos(TODAY,CmdDate) then return 0
/* See if we fall in one of the time windows */
/* First get current time as HHMM */
NOW=time("N")
NOW=left(NOW,2) || substr(NOW,4,2)
/* Now compare NOW against each window */
TimeWindows=CmdTime
TimeInterval=""
ScanDone=0
parse upper value TimeWindows with TimeInterval "," TimeWindows
if length(TimeWindows)<1 then ScanDone=1
if 0\=InWindow(NOW TimeInterval) then return 1
do while ScanDone=0
parse upper value TimeWindows with TimeInterval "," TimeWindows
if length(TimeWindows)<1 then ScanDone=1
if 0\=InWindow(NOW TimeInterval) then return 1
end
/* Guess we're not in one of the windows */
return 0
/*************************
/* Return 0 if not in window, 1 otherwise */
/* First arg is time to check as HHMM */
/* Second arg is an interval in the for HHMM-HHMM */
/* Special case *mm-*mm allows simple hourly specification */
InWindow:
arg HHMM Interval
HH=left(HHMM,2)
MM=right(HHMM,2)
DashPos=pos("-",Interval)
if DashPos<3 then do
msg="Invalid TIME=" || CmdTime
call LogIt
return 0
end
if length(Interval)<(DashPos+
msg="Bad TIME=" || CmdTime
call LogIt
return 0
end
/* Firm up the interval start */
IntBeg=left(Interval,DashP
if length(IntBeg)=3 then do
if left(IntBeg,1)\="*" then do
msg="Invalid interval start " || IntBeg
call LogIt
return 0
end
/* This guy is automatically in the current hour */
IntBeg=HH || right(IntBeg,2)
end
/* Do the same thing for interval end */
IntEnd=substr(Interval,Das
if length(IntEnd)=3 then do
if left(IntEnd,1)\="*" then do
msg="Invalid interval end " || IntEnd
call LogIt
return 0
end
/* This guy is automatically in the current hour */
IntEnd=HH || right(IntEnd,2)
end
if IntBeg<=HHMM & HHMM<=IntEnd then return 1
return 0
/*************************
ExecuteConsoleCmd:
......
return
/*************************
ExecuteCopyCmd:
/* Make sure we have at least one file to copy */
CmdFrom=DynEnv(CmdFrom)
FileMask=CmdFrom||"\"||Cmd
NumFiles=ListFile(FileMask
if NumFiles=0 then do
msg="File(s) not found [" || CmdName || "]: " || FileMask
call LogIt
call ClearRemote
return
end
/* Make sure destination directory exists */
CmdTo=DynEnv(CmdTo)
PathToVerify=CmdTo
rc=VerifyPath()
if rc\=0 then do
msg="Copy aborted - unable to create destination (" || rc || ")"
call LogIt
call ClearRemote
return
end
/* Now we copy each file that matches the mask */
FileIdx=1
do while FileIdx<=NumFiles
curfile=strip(FileList.Fil
if length(curfile)>5 then do
if right(curfile,5)="<DIR>" then do
FileIdx=FileIdx+1
iterate
end
end
SourceFile=CmdFrom || "\" || curfile
DestFile =CmdTo || "\" || curfile
if length(CmdRen)>0 then do
/* Change destination filename, if needed */
CmdRen=DynEnv(CmdRen)
DestFile=CmdTo || "\" || CmdRen
end
DestCmd ="CO " || DestFile
/* Handle CopyNewOnly=YES */
if translate(CmdCopyNewOnly)=
/* Only copy the file if it's newer */
IsItOne=ListFile(DestFile,
if IsItOne=1 then do
/* Save destination date/time stamp */
DestStamp=right(DestList.1
/* Get source date/time stamp */
IsItOne=ListFile(SourceFil
if IsItOne=1 then do
SourceStamp=right(DestList
if SourceStamp<=DestStamp then do
msg="[" || CmdName || "] " || SourceFile || " not newer, copy skipped"
call LogIt
FileIdx=FileIdx+1
iterate
end
end
end
end
/* Finally - we can do the copy */
rc=stream(SourceFile,"C",D
/* Try up to three times if it failed */
RetryCount=0
do while (rc\=0) & (RetryCount<3)
RetryCount=RetryCount+1
msg="[" || CmdName || "] failed (" || rc || ") Retry " || RetryCount
call LogIt
Dummy=pause(30)
rc=stream(SourceFile,"C",D
end
if rc\=0 then do
msg="[" || CmdName || "] copy aborted (" || SourceFile || " to " || DestFile || ") (" || rc || ")"
call LogIt
call ClearRemote
return
end
msg="[" || CmdName || "] - OK"
call LogIt
FileIdx=FileIdx+1
end
return
/*************************
Logit:
say date()||" "||time()||" "||msg
call lineout SchedLog,date()||" "||time()||" "||msg
return
/*************************
/* Display a message then wait for a key or until timeout */
ShowWait:
DelayCtr=60
say msg
rc=GetCursor("CursorPos.")
do while DelayCtr>0
rc=SetCursor(CursorPos.row
say "Press CTRL-C or wait " || DelayCtr || " seconds... "
rc=pause(1)
DelayCtr=DelayCtr-1
end
return
/*************************
VerifyPath:
/* Create the path in <PathToVerify> if it doesn't exist */
/* Edit locally */
Remainder=PathToVerify
/* Standardize slashes */
Remainder=strip(translate(
/* Extract server name */
VerifyServer=translate(lef
/* Start off by stripping out the drive portion */
parse upper value Remainder with LeftPart ":/" Remainder
PathParts.0=1
PathParts.1=LeftPart || ":"
/* Split out the remaining parts */
do while length(Remainder)\=0
PathParts.0=1+PathParts.0
CurrentPart=PathParts.0
parse upper value Remainder with LeftPart "/" Remainder
PathParts.CurrentPart=Left
end
/* Now that we have the parts, find the first missing directory, if any */
/* VerifyPath is called after we've logged into the tree and attached */
/* to the remote server, if any. If the destination directory needs */
/* to be created and it is back on the local server, we have to */
/* switch back to the local server for the MKDIR to work. */
DestOnLocal=0
if length(CmdServer)=11 & (VerifyServer\=translate(C
/* We have to temporarily switch back to the local server */
DestOnLocal=1
HoldConn=GetCurrentConnect
HoldFSID=GetCurrentFileSer
rcl=SetCurrentFileServerID
rcl=SetCurrentConnection(0
end
/* Start one level below the root of the volume */
CurrPath=PathParts.1
if PathParts.0>1 then do
do idx=2 to PathParts.0
CurrPath=CurrPath || PathParts.idx
/* See if this path exists */
/* We used to create a fixed file name, but found out that */
/* Netware doesn't like a bazillion identical names, sooo */
/* we create a filename CCCCnnnS.SUU where: */
/* CCCC is the city code from the server name */
/* nnn is the right-most three chars from server name */
/* S.SUU are the seconds and miliseconds from the current time */
/* Although this will still have some collisions, it won't be near */
/* as many as with a fixed file name. */
SSDotUU=substr(time("L"),7
SvrPart=left(VerifyServer,
FileName=SvrPart || left(SSDotUU,1)
ExtName=substr(SSDotUU,2,1
FileName=FileName || "." || ExtName
FileName=CurrPath || "\" || FileName
rcl=stream(FileName,"c","c
if rcl\=0 then do
/* Nope, let's make it */
rcl=stream(CurrPath,"c","m
if rcl\=0 then do
msg="Unable to create <" || CurrPath || "> (" || rcl || ")"
call LogIt
if DestOnLocal=1 then do
/* Switch back to the remote, if needed */
rcl=SetCurrentFileServerID
rcl=SetCurrentConnection(H
DestOnLocal=0
end
return 1
end
end
/* Close the name so we don't interfere with a later pass */
rcl=stream(FileName,"c","c
rcl=stream(FileName,"c","d
CurrPath=CurrPath || "\"
end
end
return 0
/*************************
SetENV:
/*
** Replace environment variables in IniLine
**
** Recognizes: FSN - File Server Name
** FSN83 - File Server Name in 8.3 format
**
**
** Scan INILine for all instances of known %env% and replace with
** appropriate value. Note our presumption that INILine has already
** been converted to upper case.
*/
/* Look for FileServerName */
vpos=pos("%FSN%",IniLine)
do while vpos>0
/* We have a hit, find it's start */
tpos=pos("%FSN%",IniLine)
/* Extract whatever is to the left of %FSN% */
LeftPart=""
if tpos>1 then LeftPart=left(INILine,tpos
/* Now extract whatever is to the right of %FSN% */
RightPart=""
if tpos<(length(IniLine)-4) then RightPart=right(INILine,1+
/* Reconstruct INILine, using value for FSN */
INILine=Leftpart || FSN || RightPart
vpos=pos("%FSN%",IniLine)
end
/* Look for File Server Name in 8.3 format */
vpos=pos("%FSN83%",IniLine
do while vpos>0
tpos=pos("%FSN83%",IniLine
LeftPart=""
if tpos>1 then LeftPart=left(INILine,tpos
RightPart=""
if tpos<(length(IniLine)-6) then RightPart=right(INILine,1+
INILine=Leftpart || FSN83 || RightPart
vpos=pos("%FSN83%",IniLine
end
return
/*************************
/* Replace dynamic environment variables in input string */
DynENV:
arg StringIn
/* Replace %*% with remote server name */
vpos=pos("%*%",StringIn)
do while vpos>0
tpos=pos("%*%",StringIn)
LeftPart=""
if tpos>1 then LeftPart=left(StringIn,tpo
RightPart=""
if tpos<(length(StringIn)-2) then RightPart=right(StringIn,1
StringIn=Leftpart || CmdServer || RightPart
vpos=pos("%*%",StringIn)
end
/* Replace %*83% with remote server name in 8.3 format */
vpos=pos("%*83%",StringIn)
CmdServer83=left(CmdServer
do while vpos>0
tpos=pos("%*83%",StringIn)
LeftPart=""
if tpos>1 then LeftPart=left(StringIn,tpo
RightPart=""
if tpos<(length(StringIn)-2) then RightPart=right(StringIn,1
StringIn=Leftpart || CmdServer83 || RightPart
vpos=pos("%*83%",StringIn)
end
return StringIn
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
How's it going?
Hope you get the points and an A for this Cookre (if the questioneer is still around that is) , you deserve it for all the work you put in to it.
ASKER
Cookre.... Sorry for the delay.. I was pulled away for something else..
so I did not get a chance to try your code.. actually didn't get a chance to re-engineer it to VB code. Would still prefer this is VB code if you can.. this way, I can dwalk thru the code in debug mode and learn that way.. Debugging code is better than reading code (for me at least). From my end... you have got the points and an "A"... just hoping for VB code or more time on my hands to re-engineer the code.. that is why I am leaving this question open for a bit longer. Will give it a few more weeks and than award the points. Hope you don't mind!
SNilsson - Thank you for givign him credit.. and I definitely plan to give him the points and even if he can't help me with VB code.. he will get an A.. and if someone else comes by now and puts up VB code that works the way I need... Cookre will still get points..
so I did not get a chance to try your code.. actually didn't get a chance to re-engineer it to VB code. Would still prefer this is VB code if you can.. this way, I can dwalk thru the code in debug mode and learn that way.. Debugging code is better than reading code (for me at least). From my end... you have got the points and an "A"... just hoping for VB code or more time on my hands to re-engineer the code.. that is why I am leaving this question open for a bit longer. Will give it a few more weeks and than award the points. Hope you don't mind!
SNilsson - Thank you for givign him credit.. and I definitely plan to give him the points and even if he can't help me with VB code.. he will get an A.. and if someone else comes by now and puts up VB code that works the way I need... Cookre will still get points..
ASKER
Cookre.. .thank you for your help.. Sorry for delay.. I got busy with something else..
But here are you points and Grade A..
Also.. if you get a chance.. can you try to convert the code to VB?
Thanks.
But here are you points and Grade A..
Also.. if you get a chance.. can you try to convert the code to VB?
Thanks.
As it turns out, I just started working on a service that will contain several of these event scheduling features. Alas, it'll be in C, but, if you're still interested, post something here around mid-February and I'll put up the C code.
ASKER
Cookre...
Did you ever get to do the code that you spoke about?
I know it has been a long time.. but thought to try.
Did you ever get to do the code that you spoke about?
I know it has been a long time.. but thought to try.
Sure did, but it's c#.NET.
I'll post it, but I've got to pull out some security specific things first.
I'll post it, but I've got to pull out some security specific things first.
This sound like a job for scheduler.