home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 35 Internet
/
35-Internet.zip
/
rxmir13.zip
/
rxMirror.cmd
< prev
next >
Wrap
OS/2 REXX Batch file
|
1999-02-21
|
50KB
|
1,318 lines
/* rxMirror v1.3, an automated FTP mirroring tool: main REXX script
* Based on the work by Sergey Ayukov <asv@ayukov.com>
* Released as FREEWARE, see license.doc for details
*/
say "rxMirror v1.3 - an automated FTP mirroring tool in REXX"
say "Copyright (C) Sergey Ayukov 1995,1996,1998 <asv@ayukov.com>."
say "Copyright (C) Teemu Mannermaa 1998-1999 <wicked@clinet.fi>."
say
/* Loading necessary DLL files */
if rxFuncQuery("ftpLoadFuncs") then do
say "Loading rxFtp extensions..."
call rxFuncAdd "ftpLoadFuncs","rxFtp","ftpLoadFuncs"
if result \= "0" then do
say "Error loading rxFtp.dll"
exit 3
end
call ftpLoadFuncs "Quiet"
say
end
if rxFuncQuery("sysLoadFuncs") then do
say "Loading rexxUtil extensions..."
call rxFuncAdd "sysLoadFuncs","rexxUtil","sysLoadFuncs"
if result \= "0" then do
say "Error loading rexxUtil.dll"
exit 3
end
call sysLoadFuncs
say
end
if rxFuncQuery("rx2LoadFuncs") then do
say "Loading rx2Util extensions..."
call rxFuncAdd "rx2LoadFuncs","rx2Util","rx2LoadFuncs"
if result \= "0" then do
say "Error loading rx2Util.dll"
exit 3
end
call rx2LoadFuncs
say
end
/* Set up configuration */
glb. = "" /* Global data */
cfg. = "" /* Configuration data */
sts. = "" /* Status data */
parse source . . glb.cmddir .
glb.cmddir = filespec("drive", glb.cmddir)||tolower(filespec("path", glb.cmddir))
glb.curdir = directory()'\'
glb.optfile = glb.curdir".mirror.options"
glb.crashdir = ""
glb.maxtimlim = 59+(60*23)
cfg.serverhost = ""
cfg.startdir = ""
cfg.localdir = glb.curdir
cfg.user = "anonymous"
cfg.passwd = ""
cfg.loglvl = ""
cfg.mainlog = glb.curdir".mirror.mainlog"
cfg.primarylog = glb.curdir".mirror.files"
cfg.loclog = ".mirror.log"
cfg.inlog = ".mirror.in"
cfg.outlog = ".mirror.out"
cfg.scrlog = glb.curdir".mirror.scrlog"
cfg.limit.kbytes = 25000
cfg.limit.nfiles = 300
cfg.limit.dirmins = -1
cfg.limit.totmins = 60
cfg.recovery = 0
cfg.skip = ".mirror.skip"
cfg.skipglb = glb.curdir".mirror.skip"
cfg.inc = ".mirror.incl"
cfg.incglb = glb.curdir".mirror.incl"
cfg.reflect = ".mirror.reflect"
cfg.ftpserver = 0
cfg.trueremove = 0
cfg.ignoretimestamp = 0
cfg.savedirlist = ""
cfg.relocate = ".mirror.relocate"
cfg.linkedfiles = 0
cfg.linklist = ".mirror.links"
cfg.idlelim = -1
/* Handle options from file/command line */
parse arg cmdarg
if wordpos("-?", cmdarg) | wordpos("-H", translate(cmdarg)) \= 0 then do
call usage
exit 2
end
if stream(glb.optfile, 'c', "query exists") \= "" then do
say "Reading options from '"glb.optfile"'"
do while lines(glb.optfile) <> 0
lin = linein(glb.optfile)
if lin = "" | left(lin,1)=";" then iterate
lin = strip(lin)
say "Assigning option: "lin
interpret "cfg."lin
end
call stream glb.optfile, "C", "CLOSE"
say
end
call prcopt(cmdarg)
/* Remove screen output log, if it exists/is configured */
if pos("S",cfg.loglvl) \= 0 then
if stream(cfg.scrlog, "c", "query exist") \= "" then
call sysFileDelete cfg.scrlog
/* Validate configuration */
if cfg.linkedfiles < 0 & cfg.linkedfiles > 2 then do
call saylog "Invalid link processing option "cfg.linkedfiles", assuming 0"
cfg.linkedfiles = 0
end
if cfg.serverhost = "" then do
call saylog "Server to mirror is not defined"
call usage
exit 1
end
if cfg.startdir = "" then do
call saylog "Directory to mirror is not defined"
call usage
exit 1
end
if cfg.passwd = "" then do
call saylog "Password is not defined"
call usage
exit 1
end
call saylog "Mirroring directory: "cfg.startdir
call saylog "To local directory : "cfg.localdir
call saylog "From server : "cfg.serverhost
call saylog "Userid : "cfg.user
call saylog "Password : "cfg.passwd
call saylog
if cfg.passwd = "*" then do
say "Enter your password for" cfg.user"@"cfg.serverhost
say "(will not appear when typed, empty line will abort):
"
parse pull cfg.passwd
say '
'
if cfg.passwd = "" then exit 1
end
/* Initialize statistics */
sts.fildl = 0
sts.filrm = 0
sts.bytdl = 0
sts.bytrm = 0
/* Attempting crash recovery if necessary */
signal off notready
if cfg.recovery then do
do while lines(cfg.mainlog) <> 0
lin = linein(cfg.mainlog)
if substr(lin,22,3) = "dir" then do
completed = 0
crash = substr(lin,34)
end
else if substr(lin,22,3) = ">>>" then completed = 1
end
call stream cfg.recovery, "C", "CLOSE"
cfg.recovery = 0
if completed = 0 then do
call saylog "Crash condition detected; last directory is: "crash
glb.crashdir = crash
cfg.recovery = 1
end
end
/* Logging into remote host */
signal on halt name abort
if pos("M", cfg.loglvl) \=0 then do
call logwrite cfg.mainlog "<<<<<<<<<--------------------------------------------"
call logwrite cfg.mainlog "host :" cfg.serverhost
end
call ftpSetUser cfg.serverhost, cfg.user, cfg.passwd
if result \= 1 then call perror "Bad userid/password"
i = 1
do forever
call saylog "Trying to login to '"cfg.serverhost"', try #"i" ("1*i-1" minutes passed)"
call ftpPwd remotedir
if result = 0 then leave
if ftperrno = "FTPLOGIN" then do
if i > 5 then call perror "Login failed "i" times"
call saylog "Server is busy; waiting for 1 minutes"
call sysSleep 60*1
i = i + 1
iterate
end
call perror "Login failed"
end
if cfg.limit.totmins > 0 then do
sts.tottimlim = time('M') + cfg.limit.totmins
/* *** ERROR *** */
/*if sts.tottimlim > glb.maxtimlim then sts.tottimlim = sts.tottimlim - glb.maxtimlim*/
end
call chkdir cfg.localdir
call chgdir cfg.localdir
call processdir cfg.startdir
call chgdir glb.curdir
if cfg.limit.totmins > 0 then
if time('M') > (sts.tottimlim-1) then do
call saylog "Total minute limit "cfg.limit.totmins" exceeded by "time('M')-sts.tottimlim" minutes, terminating"
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "Total minute limit "cfg.limit.totmins" exceeded, terminating"
end
if pos("M", cfg.loglvl) \=0 then do
call logwrite cfg.mainlog "IN " right(sts.fildl,5) "file(s)," right(nicenum(sts.bytdl),11) "bytes"
call logwrite cfg.mainlog "OUT " right(sts.filrm,5) "file(s)," right(nicenum(sts.bytrm),11) "bytes"
call logwrite cfg.mainlog ">>>>>>>>>--------------------------------------------"
end
call saylog
call saylog "Totals:"
call saylog "IN " right(sts.fildl,5) "file(s)," right(nicenum(sts.bytdl),11) "bytes"
call saylog "OUT " right(sts.filrm,5) "file(s)," right(nicenum(sts.bytrm),11) "bytes"
call ftpLogoff
exit 0
/* Performs mirroring for specified directory and subdirectories */
processdir: procedure expose glb. cfg. sts. ftperrno
parse arg dir0
/* Set up current directory */
call saylog
call saylog "Changing remote directory to "dir0
call ftpChDir dir0
if result \= 0 then do
call saylog "*** WARNING: cannot chdir to "dir0
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "!cannot chdir to '"dir0"'"
if ftperrno \= "FTPCOMMAND" then
call perror "Cannot do chdir to "dir0
return
end
call ftpPwd remotedir
/* Get remote directory listing */
call getinclist
call getskiplist
call getreloclist
call saylog "Getting remote file list"
call ftpDir ".", "file."
if result \= 0 then call perror "Error getting remote file list"
cyear = substr(date('S'), 1, 4)
cmonth = substr(date('S'), 5, 2)
drop remdir.
drop remfil.
drop remfilind.
remfil.num = 0
remfil.bytes = 0
remdir.num = 0
if cfg.savedirlist <> "" then call sysFileDelete cfg.savedirlist
call saylog "Processing remote file list with "file.0" entries"
if cfg.idlelim > 0 then call time 'R'
do i=1 to file.0
if (cfg.ftpserver = 0) | (cfg.ftpserver = 3) then do
parse value file.i with rflags . . . rlen rmonth rday rtime rname . rlink
end
if cfg.ftpserver = 1 then do
parse value file.i with rflags . . rlen rmonth rday rtime rname . rlink
end
if cfg.ftpserver = 2 then do
parse value file.i with rlen 19 . 30 rflags 36 rmonth'-'rday'-'ryear rtime rname
if strip(rflags)='' then rflags="-"
end
if cfg.savedirlist <> "" then call lineout cfg.savedirlist, file.i
rname = strip(rname)
if isdir(rlen rflags rname) then do
remdir.num = remdir.num + 1
tmp = remdir.num
remdir.tmp.dname = rname
remdir.tmp.len = rlen
remdir.tmp.flags = rflags
end
if (rworth(rlen rflags rname) | islink(rlen rflags rname rlink)) then do
remfil.bytes = remfil.bytes + rlen
remfil.num = remfil.num + 1
tmp = remfil.num
if (cfg.ftpserver = 0) | (cfg.ftpserver = 1) | (cfg.ftpserver = 3) then do
rmonth = month2dec(rmonth)
if pos(":", rtime) \= 0 then
if rmonth > cmonth then ryear = cyear-1
else ryear = cyear
else do
ryear = rtime
rtime = "00:00"
end
end
if ryear < 1900 then ryear = ryear + 1900
remfil.tmp.fname = rname
remfil.tmp.len = rlen
remfil.tmp.flags = rflags
remfil.tmp.year = ryear
remfil.tmp.month = rmonth
remfil.tmp.day = rday
remfil.tmp.time = rtime
call checkrelocate tmp
remfil.tmp.linkdir = ""
remfil.tmp.linkfil = ""
if islink(rlen rflags rname rlink) then call processlink tmp rlink
indkey=crtkey(remfil.tmp.fname)
remfilind.indkey=tmp
end
if sendnoop(time('E') i" of "file.0" entries processed")=0 then call time 'R'
end
if cfg.savedirlist <> "" then call stream cfg.savedirlist, "C", "CLOSE"
drop file.
/* Check error recovery conditions */
skipto = ""
if cfg.recovery then do
if left(glb.crashdir,length(dir0)) \= dir0 then
call perror "Illegal crash recovery attempt; "glb.crashdir dir0
skipto = substr(glb.crashdir,length(dir0)+2)
sp = pos("/",skipto)
if sp \= 0 then skipto = left(skipto,sp-1)
call saylog "Skipping to '"skipto"'"
end
/* Process this directory */
call dirmirror dir0
/* Process subdirs */
do i=1 to remdir.num
if cfg.limit.totmins > 0 then
if time('M') > (sts.tottimlim-1) then leave i
if \cfg.recovery then do
call chkdir remdir.i.dname
call chgdir remdir.i.dname
if result = "" then iterate i
call processdir dir0'/'remdir.i.dname
call chgdir ".."
end
else do
if remdir.i.dname = skipto then do
call chkdir remdir.i.dname
call chgdir remdir.i.dname
if result = "" then iterate i
call processdir dir0'/'remdir.i.dname
call chgdir ".."
end
end
end i
if sp = 0 then cfg.recovery = 0
return
/* Mirroring of current dir on remote into current dir on local */
dirmirror: procedure expose glb. ftperrno remfil. remfilind. cfg. sts. reloc.
parse arg dir
call ftpSetBinary "Binary"
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "directory: "dir
call saylog right(remfil.num,5)" remote files ("nicenum(remfil.bytes)" bytes)"
call getlocalfilelist /* Build list of local files */
/* Check out if there's some file on remote and not on local disk */
call saylog "Checking/retrieving remote files, please wait..."
files_dl = 0
bytes_dl = 0
if cfg.limit.dirmins > 0 then do
curtimlim = time('M') + cfg.limit.dirmins
if curtimlim > glb.maxtimlim then curtimlim = curtimlim - glb.maxtimlim
end
if cfg.idlelim > 0 then call time 'R'
do i=1 to remfil.num
found=0
if locfil.num \= 0 then do
indkey=crtkey(remfil.i.locfname)
if symbol('locfilind.indkey') = "VAR" then do
j=locfilind.indkey
if match(i j) then found=1
end
end
if found=0 then do
if retrieve(i) = 0 then do
files_dl = files_dl + 1
bytes_dl = bytes_dl + remfil.i.len
if pos("P", cfg.loglvl) \=0 then do
if remfil.i.linkfil \= "" then linkspec = " -> "remfil.i.linkdir||remfil.i.linkfil
else linkspec = ""
call logwrite cfg.primarylog "+ "right(nicenum(remfil.i.len),11)" "dir"/"remfil.i.fname||linkspec
end
end
if cfg.idlelim > 0 then call time 'R'
end
if cfg.limit.nfiles > 0 then
if files_dl >= cfg.limit.nfiles then do
call saylog "File limit "cfg.limit.nfiles" reached, leaving dir"
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "File limit "cfg.limit.nfiles" reached, leaving dir"
leave i
end
if cfg.limit.kbytes > 0 then
if bytes_dl/1024 >= cfg.limit.kbytes then do
call saylog "KByte limit "cfg.limit.kbytes" exceeded ("bytes_dl/1024"), leaving dir"
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "KByte limit "cfg.limit.kbytes" exceeded ("bytes_dl/1024"), leaving dir"
leave i
end
if cfg.limit.dirmins > 0 then
if time('M') > (curtimlim-1) then do
call saylog "Minute limit "cfg.limit.dirmins" exceeded by "time('M')-curtimlim" minutes, leaving dir"
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "Minute limit "cfg.limit.dirmins" exceeded, leaving dir"
leave i
end
if cfg.limit.totmins > 0 then
if time('M') > (sts.tottimlim-1) then do
call saylog "Total minute limit "cfg.limit.totmins" exceeded by "time('M')-sts.tottimlim" minutes, leaving dir"
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "Total minute limit "cfg.limit.totmins" exceeded, leaving dir"
leave i
end
if found=1 then if sendnoop(time('E') i" of "remfil.num" files checked")=0 then call time 'R'
end i
if files_dl \= 0 | bytes_dl \= 0 then do
if pos("L", cfg.loglvl) \=0 then
call logwrite cfg.loclog "IN " right(files_dl,5)" file(s), "right(nicenum(bytes_dl),11)" bytes"
call saylog "Retrieved "files_dl" file(s), "nicenum(bytes_dl)" bytes"
call getlocalfilelist /* Rebuild local list to accomodate changes */
sts.fildl = sts.fildl + files_dl
sts.bytdl = sts.bytdl + bytes_dl
end
/* Check out if there's some file on local disk and not on remote */
call saylog "Checking/removing local files, please wait..."
files_rm = 0
bytes_rm = 0
remdircheck = 0
if cfg.idlelim > 0 then call time 'R'
do j=1 to locfil.curdnum + locfil.relcnum
found=0
indkey=crtkey(locfil.j.remfname)
if symbol('remfilind.indkey') = "VAR" then do
i=remfilind.indkey
if translate(strip(remfil.i.locfname,"T","."))=translate(locfil.j.fname) then
found=1
end
if found=0 then do
if \cfg.trueremove then do
if \remdircheck then call chkdir ".removed"
call saylog "Removing local file '"locfil.j.locdir""locfil.j.fname"' ("locfil.j.len" bytes) as obsolete"
call sysFileDelete '.removed\'locfil.j.fname
'@move' '"'locfil.j.locdir||locfil.j.fname'" ".removed\'locfil.j.fname'" >nul'
remdircheck = 1
end
else call sysFileDelete locfil.j.locdir||locfil.j.fname
if pos("F", cfg.loglvl) \=0 then
call logwrite cfg.outlog right(locfil.j.len,10) locfil.j.locdir""locfil.j.fname
if pos("P", cfg.loglvl) \=0 then
call logwrite cfg.primarylog "- "right(nicenum(locfil.j.len),11)" "dir"/"locfil.j.remfname
files_rm = files_rm + 1
bytes_rm = bytes_rm + locfil.j.len
end
if found=1 then if sendnoop(time('E') j" of "locfil.curdnum+locfil.relcnum" files checked")=0 then call time 'R'
end j
if files_rm \= 0 | bytes_rm \= 0 then do
if pos("L", cfg.loglvl) \=0 then
call logwrite cfg.loclog "OUT " right(files_rm,5)" file(s), "right(nicenum(bytes_rm),11)" bytes"
call saylog "Removed "files_rm" file(s), "nicenum(bytes_rm)" bytes"
sts.filrm = sts.filrm + files_rm
sts.bytrm = sts.bytrm + bytes_rm
end
return
/* Send NOOP to make some noice */
sendnoop: procedure expose glb. cfg.
parse arg curtim msg
retcde = -1
if cfg.idlelim<0 then return retcde
if (cfg.idlelim=0) | (trunc(curtim)>=cfg.idlelim) then do
call saylog "Sending NOOP, "msg
call ftpQuote "noop" /* To make sure we stay online */
if cfg.idlelim > 0 then retcde=0
end
return retcde
/* Determine whether remote file worth to mirror */
rworth: procedure expose glb. skp. inc.
parse arg len flags fname
if fname = "" then return 0
if left(fname,1) = "." then return 0
if left(flags,1) \= "-" then return 0
if length(fname) < 1 then return 0
if words(flags) > 1 then return 0
if pos(".bad",fname) \= 0 then return 0
if pos(".core",fname) \= 0 then return 0
if pos(".try",fname) \= 0 then return 0
if fname = "descript.ion" then return 0
if \isvalid(fname) then return 0
found = 0
do i = 1 to inc.num
if wildcardmatch(inc.i fname) then do
found = 1
leave
end
end
if \found then return 0
do i = 1 to skp.num
if wildcardmatch(skp.i fname) then return 0
end
return 1
/* Determine whether local file worth to mirror */
lworth: procedure expose glb.
parse arg fname len
if left(fname,1) = "." then return 0
if length(fname) < 1 then return 0
if \isvalid(fname) then return 0
if fname = "descript.ion" then return 0
return 1
/* Do two files match? */
match: procedure expose glb. remfil. locfil. cfg.
parse arg i j
if translate(strip(remfil.i.locfname,"T",".")) \= translate(locfil.j.fname) then return 0
if remfil.i.len \= locfil.j.len then return 0
if cfg.ignoretimestamp then return 1
/*say "remote ["remotetimestamp(i)"] local ["locfil.j.date"] ("substr(remotetimestamp(i),8)")"
say "->"left(remotetimestamp(i),7)"<- ->"left(locfil.j.date,7)"<-"*/
if substr(remotetimestamp(i), 8) = "00:00" then do
if left(remotetimestamp(i), 7) \= left(locfil.j.date, 7) then return 0
end
else if remotetimestamp(i) \= locfil.j.date then return 0
return 1
/* Fetching file from remote */
retrieve: procedure expose glb. cfg. remfil.
parse arg index
filename = remfil.index.fname
tofilnam = remfil.index.locdir||remfil.index.locfname
call saylog "Retrieving file '"filename"' to '"tofilnam"' ("nicenum(remfil.index.len)" bytes)"
elapsedtime = 0.000000
linkspec = ""
if remfil.index.linkfil \= "" then do
linktodir = remfil.index.linkdir
linktofil = remfil.index.linkfil
call saylog " as a link to local file '"linktodir||linktofil"'"
linkspec = " -> "||linktodir||linktofil
call sysFileDelete tofilnam
call stream tofilnam, "C", "OPEN"
call stream tofilnam, "C", "CLOSE"
if cfg.linklist \= "" then do
call lineout cfg.linklist, tofilnam||' 'linktodir||linktofil
call stream cfg.linklist, "C", "CLOSE"
end
end
else do
call time 'R'
call ftpGet tofilnam, filename
elapsedtime = time('E')
if result \= 0 then do
call saylog "*** WARNING: Unable to download file '"filename"'; ftperrno is" ftperrno
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "!Unable to download '"filename"'"
if ftperrno \= "FTPCOMMAND" then
call perror "File fetching failed on "filename
ftperrno = ""
return 1
end
end
if pos("F", cfg.loglvl) \=0 then
call logwrite cfg.inlog right(remfil.index.len,8)' 'right(calccps(elapsedtime remfil.index.len),6)' cps 'right(nicedltime(elapsedtime),11)' 'remfil.index.locdir||remfil.index.locfname||linkspec
if length(remfil.index.time) = 4 then do
remfil.index.time = "0"||remfil.index.time
end
hour = substr(remfil.index.time, 1, 2)
min = substr(remfil.index.time, 4, 2)
call rx2SetFileTimestamp tofilnam, remfil.index.day, remfil.index.month, remfil.index.year, hour, min, 0
if result \= 0 then call saylog " Unable to set timestamp to "remfil.index.day"."remfil.index.month"."remfil.index.year" "hour":"min":00 (rc is "result")"
call saylog ' 'right(nicedltime(elapsedtime),11)' (hh:mm:ss.ms), 'right(calccps(elapsedtime remfil.index.len),6)' cps'
return 0
/* Write a string to log file */
logwrite: procedure expose glb.
parse arg logname str
call stream logname, "C", "OPEN WRITE"
call stream logname, "C", "SEEK <0"
call lineout logname, right(date(),11) time()' 'str
call stream logname, "C", "CLOSE"
return 1
/* Process errors */
perror: procedure expose glb. cfg. ftperrno
parse arg errstr
call saylog errstr", ftp error code is" ftperrno
if pos("M", cfg.loglvl) \=0 then
call logwrite cfg.mainlog "ERR: "errstr", ftp error code is "ftperrno
call chgdir glb.curdir
exit 1
/* Write a string to log file and to screen */
saylog: procedure expose glb. cfg.
parse arg str
say str
if pos("S", cfg.loglvl) \=0 then
call logwrite cfg.scrlog str
return 1
/* Nice digit output */
nicenum: procedure expose glb.
parse arg num
bil = num%1000000000
mil = num%1000000 - bil*1000
th = num%1000 - mil*1000 - bil*1000000
ed = num - th*1000 - mil*1000000 - bil*1000000000
out = ""
if bil \= 0 then out = out""bil" "
if mil \= 0 then
if bil \= 0 then out = out""right(mil,3,'0')" "
else out = out""mil" "
else
if bil \= 0 then out = out""right(mil,3,'0')" "
if th \= 0 then
if bil \= 0 | mil \= 0 then out = out""right(th,3,'0')" "
else out = out""th" "
else
if bil \= 0 | mil \= 0 then out = out""right(th,3,'0')" "
if bil \= 0 | mil \= 0 | th \= 0 then out = out""right(ed,3,'0')
else out = out""ed
return out
/* Format nice download time */
nicedltime: procedure expose glb.
parse arg dltime .
msec = substr(dltime,pos(".",dltime)+1,2)
secs = trunc(dltime)
mins = 0
hours = 0
if secs > 59 then do
mins = secs%60
secs = secs-mins*60
end
if mins > 59 then do
hours = mins%60
mins = mins-hours*60
end
retvar = secs||"."||msec
if mins > 0 then do
if length(retvar) < 5 then retvar = "0"retvar
retvar = mins||":"||retvar
end
if hours > 0 then do
if length(retvar) < 8 then retvar = "0"retvar
retvar = hours||":"||retvar
end
return retvar
/* Calculate CPS */
calccps: procedure expose glb.
parse arg dltime dllen .
if dltime = 0 then retvar = 0
else retvar = trunc(dllen/dltime)
return retvar
/* User abort */
abort:
if pos("M", cfg.loglvl) \=0 then do
call saylog "User abort detected, terminating"
call logwrite cfg.mainlog "ERR: User abort detected"
end
call chgdir glb.curdir
call ftpLogoff
exit 1
/* Get local file list (including reflections and relocation) */
getlocalfilelist: procedure expose glb. locfil. locfilind. cfg. reloc.
drop locfil.
drop locfilind.
locfil.num = 0
locfil.bytes = 0
call getcurdfilelist
call getrelocations
call getreflections
call saylog right(locfil.num,5)" local files ("nicenum(locfil.bytes)" bytes)"
if locfil.curdnum > 0 then
call saylog right(locfil.curdnum,5)" in current dir ("nicenum(locfil.curdbytes)" bytes)"
if locfil.reflnum > 0 then
call saylog right(locfil.reflnum,5)" reflected ("nicenum(locfil.reflbytes)" bytes)"
if locfil.relcnum > 0 then
call saylog right(locfil.relcnum,5)" relocated ("nicenum(locfil.relcbytes)" bytes)"
return
/* Getting file list for current directory */
getcurdfilelist: procedure expose glb. locfil. locfilind. reloc.
locfil.curdnum = 0
locfil.curdbytes = 0
cutpos = length(directory())+2
call sysFileTree "*", "file.", "F"
do i=1 to file.0
parse value file.i with dat tim llen . lname
lname = substr(strip(lname), cutpos)
if lworth(lname llen) then do
locfil.curdnum = locfil.curdnum + 1
locfil.curdbytes = locfil.curdbytes + llen
locfil.num = locfil.num + 1
locfil.bytes = locfil.bytes + llen
tmp = locfil.num
locfil.tmp.fname = lname
locfil.tmp.len = llen
locfil.tmp.date = maketimestamp(dat tim)
locfil.tmp.locdir = ""
locfil.tmp.remfname = lname
indkey=crtkey(locfil.tmp.fname)
locfilind.indkey=tmp
/* Check for rename */
do r = 1 to reloc.0
do n = 1 to reloc.r.inc.0
if wildcardmatch(reloc.r.inc.n locfil.tmp.fname) then do
do s = 1 to reloc.r.skp.0
if wildcardmatch(reloc.r.skp.s locfil.tmp.fname) then iterate
end
if reloc.r.cnvproc \= "" then do
reloc.r.cnvproc locfil.tmp.fname "unpack"
if queued() \= 0 then parse pull locfil.tmp.remfname
end
end
end
end
end
end
drop file.
return
/* Getting reflections list */
getreflections: procedure expose glb. locfil. locfilind. cfg.
locfil.reflnum = 0
locfil.reflbytes = 0
do while lines(cfg.reflect) <> 0
lin = linein(cfg.reflect)
if lin = "" | left(lin,1) = ";" then iterate
lin = strip(strip(lin),"T","\")
call saylog "Reflecting "lin
call sysFileTree lin"\*", "file.", "F"
do i=1 to file.0
parse value file.i with dat tim llen . lname
lname = filespec("NAME", lname)
if lworth(lname llen) then do
locfil.reflnum = locfil.reflnum + 1
locfil.reflbytes = locfil.reflbytes + llen
locfil.num = locfil.num + 1
locfil.bytes = locfil.bytes + llen
tmp = locfil.num
locfil.tmp.fname = lname
locfil.tmp.len = llen
locfil.tmp.date = maketimestamp(dat tim)
locfil.tmp.locdir = lin"\"
locfil.tmp.remfname = lname
indkey=crtkey(locfil.tmp.fname)
locfilind.indkey=tmp
end
end
drop file.
end
call stream cfg.reflect, "C", "CLOSE"
return
/* Getting relocations list */
getrelocations: procedure expose glb. locfil. locfilind. cfg. reloc.
locfil.relcnum = 0
locfil.relcbytes = 0
do r = 1 to reloc.0
if reloc.r.locdir = "" then iterate
call sysFileTree reloc.r.locdir"*", "file.", "F"
do i=1 to file.0
parse value file.i with dat tim llen . lname
lname = filespec("NAME", lname)
if lworth(lname llen) then do
locfil.relcnum = locfil.relcnum + 1
locfil.relcbytes = locfil.relcbytes + llen
locfil.num = locfil.num + 1
locfil.bytes = locfil.bytes + llen
tmp = locfil.num
locfil.tmp.fname = lname
locfil.tmp.len = llen
locfil.tmp.date = maketimestamp(dat tim)
locfil.tmp.locdir = reloc.r.locdir
locfil.tmp.remfname = lname
if reloc.r.cnvproc \= "" then do
reloc.r.cnvproc lname "unpack"
if queued() \= 0 then parse pull locfil.tmp.remfname
end
indkey=crtkey(locfil.tmp.fname)
locfilind.indkey=tmp
end
end
drop file.
end
return
/* Getting skiplist */
getskiplist: procedure expose glb. cfg. skp.
tmpnum = 0
do while lines(cfg.skipglb) <> 0
lin = linein(cfg.skipglb)
if lin = "" | left(lin,1)=";" then iterate
lin = strip(strip(lin),"T","\")
tmpnum = tmpnum + 1
skp.tmpnum = lin
call saylog "Added global skip to skiplist: "skp.tmpnum
end
call stream cfg.skipglb, "C", "CLOSE"
do while lines(cfg.skip) <> 0
lin = linein(cfg.skip)
if lin = "" | left(lin,1) = ";" then iterate
lin = strip(strip(lin),"T","\")
tmpnum = tmpnum + 1
skp.tmpnum = lin
call saylog "Added local skip to skiplist: "skp.tmpnum
end
call stream cfg.skip, "C", "CLOSE"
skp.num = tmpnum
return
/* Getting include list */
getinclist: procedure expose glb. cfg. inc.
tmpnum = 0
do while lines(cfg.incglb) <> 0
lin = linein(cfg.incglb)
if lin = "" | left(lin,1)=";" then iterate
lin = strip(strip(lin),"T","\")
tmpnum = tmpnum + 1
inc.tmpnum = lin
call saylog "Added global include: "inc.tmpnum
end
call stream cfg.incglb, "C", "CLOSE"
do while lines(cfg.inc) <> 0
lin = linein(cfg.inc)
if lin = "" | left(lin,1) = ";" then iterate
lin = strip(strip(lin),"T","\")
tmpnum = tmpnum + 1
inc.tmpnum = lin
call saylog "Added local include: "inc.tmpnum
end
call stream cfg.inc, "C", "CLOSE"
inc.num = tmpnum
if inc.num = 0 then do /* Default is to include everything */
inc.num = 1
inc.1 = "*"
end
return
/* Getting relocation list */
getreloclist: procedure expose glb. cfg. reloc.
drop reloc.
tmpnum = 0
do while lines(cfg.relocate) <> 0
lin = linein(cfg.relocate)
if lin = "" | left(lin,1) = ";" then iterate
parse var lin todir conv inclist skplist .
todir = strip(strip(todir),"T","\")
tmpnum = tmpnum + 1
if todir \= "." then reloc.tmpnum.locdir = todir"\"
else reloc.tmpnum.locdir = ""
if (conv = "") | (translate(conv) = "*NONE") then reloc.tmpnum.cnvproc=""
else interpret "reloc.tmpnum.cnvproc = "conv
call saylog "Adding relocation to: "reloc.tmpnum.locdir
if reloc.tmpnum.cnvproc \= "" then
call saylog " with rename proc: "reloc.tmpnum.cnvproc
incnum = 0
if inclist = "" then inclist = reloc.tmpnum.locdir||cfg.inc
call saylog " with include list: "inclist
do while lines(inclist) <> 0
lin = linein(inclist)
if lin = "" | left(lin,1) = ";" then iterate
lin = strip(strip(lin),"T","\")
incnum = incnum + 1
reloc.tmpnum.inc.incnum = lin
call saylog " added relocation include: "reloc.tmpnum.inc.incnum
end
call stream inclist, "C", "CLOSE"
reloc.tmpnum.inc.0 = incnum
skpnum = 0
if skplist = "" then skplist = reloc.tmpnum.locdir||cfg.skip
call saylog " with skip list: "skplist
do while lines(skplist) <> 0
lin = linein(skplist)
if lin = "" | left(lin,1) = ";" then iterate
lin = strip(strip(lin),"T","\")
skpnum = skpnum + 1
reloc.tmpnum.skp.skpnum = lin
call saylog " added relocation skip: "reloc.tmpnum.skp.skpnum
end
call stream skplist, "C", "CLOSE"
reloc.tmpnum.skp.0 = skpnum
end
call stream cfg.relocate, "C", "CLOSE"
reloc.0 = tmpnum
return
/* Getting relocation list for link target */
getlinkreloclist: procedure expose glb. cfg. remfil. linkreloc.
parse arg index
tmpnum = 0
do while lines(remfil.index.linkdir||cfg.relocate) <> 0
lin = linein(remfil.index.linkdir||cfg.relocate)
if lin = "" | left(lin,1) = ";" then iterate
parse var lin todir conv inclist skplist .
todir = strip(strip(todir),"T","\")
tmpnum = tmpnum + 1
if todir\="." then linkreloc.tmpnum.locdir=remfil.index.linkdir||todir"\"
else linkreloc.tmpnum.locdir = remfil.index.linkdir
if (conv = "")|(translate(conv)="*NONE") then linkreloc.tmpnum.cnvproc=""
else interpret "linkreloc.tmpnum.cnvproc = "conv
call saylog "Found link target relocation to: "linkreloc.tmpnum.locdir
if linkreloc.tmpnum.cnvproc \= "" then
call saylog " with rename proc: "linkreloc.tmpnum.cnvproc
incnum = 0
if inclist = "" then inclist = linkreloc.tmpnum.locdir||cfg.inc
else inclist = remfil.index.linkdir||inclist
call saylog " with include list: "inclist
do while lines(inclist) <> 0
lin = linein(inclist)
if lin = "" | left(lin,1) = ";" then iterate
lin = strip(strip(lin),"T","\")
incnum = incnum + 1
linkreloc.tmpnum.inc.incnum = lin
call saylog " added relocation include: "linkreloc.tmpnum.inc.incnum
end
call stream inclist, "C", "CLOSE"
linkreloc.tmpnum.inc.0 = incnum
skpnum = 0
if skplist = "" then skplist = linkreloc.tmpnum.locdir||cfg.skip
else skplist = remfil.index.linkdir||skplist
call saylog " with skip list: "skplist
do while lines(skplist) <> 0
lin = linein(skplist)
if lin = "" | left(lin,1) = ";" then iterate
lin = strip(strip(lin),"T","\")
skpnum = skpnum + 1
linkreloc.tmpnum.skp.skpnum = lin
call saylog " added relocation skip: "linkreloc.tmpnum.skp.skpnum
end
call stream skplist, "C", "CLOSE"
linkreloc.tmpnum.skp.0 = skpnum
end
call stream remfil.index.linkdir||cfg.relocate, "C", "CLOSE"
linkreloc.0 = tmpnum
return
/* Check is there relocation for this file */
checkrelocate: procedure expose glb. remfil. reloc.
parse arg index
remfil.index.locdir = ""
remfil.index.locfname = remfil.index.fname
do r = 1 to reloc.0
do i = 1 to reloc.r.inc.0
if wildcardmatch(reloc.r.inc.i remfil.index.fname) then do
do s = 1 to reloc.r.skp.0
if wildcardmatch(reloc.r.skp.s remfil.index.fname) then return
end
remfil.index.locdir = reloc.r.locdir
if reloc.r.cnvproc \= "" then do
reloc.r.cnvproc remfil.index.fname "pack"
if queued() \= 0 then parse pull remfil.index.locfname
end
end
end
end
return
/* Check for link target relocation */
checklinkreloc: procedure expose glb. cfg. remfil.
parse arg index
call getlinkreloclist index
do r = 1 to linkreloc.0
do i = 1 to linkreloc.r.inc.0
if wildcardmatch(linkreloc.r.inc.i remfil.index.linkfil) then do
do s = 1 to linkreloc.r.skp.0
if wildcardmatch(linkreloc.r.skp.s remfil.index.linkfil) then return
end
remfil.index.linkdir = linkreloc.r.locdir
if linkreloc.r.cnvproc \= "" then do
linkreloc.r.cnvproc remfil.index.linkfil "pack"
if queued() \= 0 then parse pull remfil.index.linkfil
end
end
end
end
return
/* Change directory */
chgdir: procedure expose glb.
parse arg dirname
realdir = translate(dirname,'\','/')
if right(realdir,1) = '\' then realdir=substr(realdir,1,length(realdir)-1)
call directory realdir
return
/* Checking dir presence */
chkdir: procedure expose glb.
parse arg chkdir
dirname = translate(chkdir,'\','/')
if right(dirname,1) = '\' then dirname=substr(dirname,1,length(dirname)-1)
call sysFileTree dirname, "dir.", "D"
if dir.0 = 0 then call sysMkDir dirname
return
/* Process linked remote files */
processlink: procedure expose glb. remfil. cfg.
parse arg fnum rlink .
call saylog
call saylog "Remote file '"remfil.fnum.fname"' is a link to '"rlink"'"
if cfg.linkedfiles = 0 then return /* Just to be sure */
if cfg.linkedfiles = 2 then do
locdir = translate(rlink,'\','/')
lpos = lastpos('\', locdir)
locfil = substr(locdir,lpos+1)
locdir = substr(locdir,1,lpos)
remfil.fnum.linkdir = locdir
remfil.fnum.linkfil = locfil
call checklinkreloc fnum
call saylog "Checking local file "remfil.fnum.linkfil" at directory "remfil.fnum.linkdir
call sysFileTree remfil.fnum.linkdir||remfil.fnum.linkfil, "file.", "F"
if file.0 = 1 then do
parse value file.1 with ldat ltim . . lname
locdir = strip(translate(lname,'\','/'))
lpos = lastpos('\', locdir)
locdir = substr(locdir,1,lpos)
remfil.fnum.linkdir = locdir
remfil.bytes = remfil.bytes - remfil.fnum.len
remfil.fnum.len = 0
parse value ldat with rmonth'/'rday'/'ryear
parse value ltim with rhour':'rmin
if right(rmin,1) = "p" then rhour = rhour + 12
if rhour = 24 then rhour = 12
if length(rhour) < 2 then rhour = "0"rhour
rmin = substr(rmin,1,2)
remfil.fnum.year = "19"||ryear
remfil.fnum.month = rmonth
remfil.fnum.day = rday
remfil.fnum.time = rhour":"rmin
call saylog "Local file found in directory '"remfil.fnum.linkdir"' ("remfil.fnum.day"."remfil.fnum.month"."remfil.fnum.year" "remfil.fnum.time")"
call saylog
return
end
call saylog "Local file not found, transfering link as normal file"
remfil.fnum.linkdir = ""
remfil.fnum.linkfil = ""
end
call ftpDir rlink, "file."
if file.0 \= 1 then do
call saylog "Failed to get link info, "file.0" remote files found"
remfil.bytes = remfil.bytes - remfil.fnum.len
remfil.num = remfil.num - 1
return
end
if cfg.ftpserver = 0 then parse value file.1 with . . . . lnklen .
else parse value file.1 with . . . lnklen .
remfil.bytes = remfil.bytes - remfil.fnum.len + lnklen
remfil.fnum.len = lnklen
call saylog
return
/* Is this a link or not, 0 = no; 1 = yes */
islink: procedure expose glb. skp. inc. cfg.
parse arg len flags fname link
if cfg.linkedfiles = 0 then return 0
if fname = "" then return 0
if fname = "descript.ion" then return 0
if left(fname,1) = "." then return 0
if left(flags,1) \= "l" then return 0
if length(fname) < 1 then return 0
if words(flags) > 1 then return 0
if pos(".bad",fname) \= 0 then return 0
if pos(".core",fname) \= 0 then return 0
if pos(".try",fname) \= 0 then return 0
if \isvalid(fname) then return 0
if link = "" then return 0
found = 0
do i = 1 to inc.num
if wildcardmatch(inc.i fname) then do
found = 1
leave
end
end
if \found then return 0
do i = 1 to skp.num
if wildcardmatch(skp.i fname) then return 0
end
return 1
/* Determine is this directory or not */
isdir: procedure expose glb. skp. inc. cfg.
parse arg len flags dname
if cfg.ftpserver = 2 then do /* OS/2 servers */
if word(flags,1) = "DIR" then return 1
else return 0
end
if flags = "" then return 0
if substr(dname,1,1) = "." then return 0
if cfg.ftpserver \= 2 & substr(flags,1,1) \= "d" then return 0
if words(flags) > 1 then return 0
if \isvalid(dname) then return 0
found = 0
do i = 1 to inc.num
if wildcardmatch(inc.i dname) then do
found = 1
leave
end
end
if \found then return 0
do i = 1 to skp.num
if wildcardmatch(skp.i dname) then return 0
end
return 1
/* Determine is filename valid or not */
isvalid: procedure expose glb.
parse arg fname
if fname = "" then return 0
if words(fname) > 1 then return 0
if verify(fname,"!#$%&'()+,-.01234567890;=@"||"ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_`"||"abcdefghijklmnopqrstuvwxyz{}~") \= 0 then return 0
return 1
/* Converts month in string representation into numeric form, i.e. Nov -> 11 */
month2dec: procedure expose glb.
parse arg month
select
when month = "Jan" then return 1
when month = "Feb" then return 2
when month = "Mar" then return 3
when month = "Apr" then return 4
when month = "May" then return 5
when month = "Jun" then return 6
when month = "Jul" then return 7
when month = "Aug" then return 8
when month = "Sep" then return 9
when month = "Oct" then return 10
when month = "Nov" then return 11
when month = "Dec" then return 12
otherwise call saylog "Wrong month "month"; assuming January"
end
return 1
/* Building date/time info for local file */
maketimestamp: procedure expose glb.
parse arg dt tm
year = substr(right(dt,8),7,2)
month = substr(right(dt,8),1,2)
day = substr(right(dt,8),4,2)
hour = substr(right(tm,6),1,2)
min = substr(right(tm,6),4,2)
if (substr(right(tm,6),6,1) = "p" & hour \= 12) then hour = hour+12
if substr(month,1,1) = " " then month = "0"substr(month,2,1)
if substr(hour,1,1) = " " then hour = "0"substr(hour,2,1)
return "A"year||month||day||hour":"min
/* Converting remote file date/time information */
remotetimestamp: procedure expose glb. remfil.
parse arg ind
rday = remfil.ind.day
if length(rday) = 1 then rday = "0"rday
if substr(rday,1,1) = " " then rday = "0"substr(rday,2,1)
rmonth = remfil.ind.month
if length(rmonth) = 1 then rmonth = "0"rmonth
if substr(rmonth,1,1) = " " then rmonth = "0"substr(rmonth,2,1)
rtime = remfil.ind.time
if length(rtime) = 4 then rtime = "0"rtime
if substr(rtime,1,1) = " " then rtime = "0"substr(rtime,2,1)
return "A"substr(remfil.ind.year,3)||rmonth||rday||rtime
/* Create index key */
crtkey: procedure expose glb.
parse arg val
return c2x(translate(strip(val,"T",".")))
/* Returns 1 if matched, 0 if not */
wildcardmatch: procedure expose glb.
parse arg wildcard test
if pos("*", substr(wildcard, pos("*", wildcard)+1)) \= 0 then do
call saylog "Two or more asterisks in wildcard string; terminating"
exit 2
end
if substr(wildcard, pos("*",wildcard)+1, 1) = "?" then do
call saylog "Question mark right after asterisk in wildcard string; terminating"
exit 2
end
it = 1
iw = 1
do forever
p = compare(substr(wildcard,iw), substr(test,it))
/*say "comparing["substr(wildcard,iw)"] and ["substr(test,it)"]; result is" p*/
if p = 0 then return 1
else do
w = substr(wildcard,iw+p-1,1)
if w = "?" then do /* "?" encountered */
if substr(test,it,1) = "" then return 0
iw = iw + p
it = it + p
iterate
end
else if w = "*" then do /* "*" encountered */
wp = substr(wildcard,iw+p)
/*say "wp is ["wp"]"*/
if wp = "" then return 1
iw = iw + p
nq = pos("?", wp)
/*say "nq is" nq*/
if nq = 0 then ss = wp
else ss = substr(wp, 1, nq-1)
/*say "searching ["ss"] in ["substr(test,it+p-1)"]"*/
it = it + p + pos(ss, substr(test,it+p)) - 1
end
else return 0 /* mismatch ! */
end
end
/* Convert string to lower case */
tolower: procedure expose glb.
parse arg str
cnvstr = str
tblo = xrange('a','z')
tbli = translate(tblo)
cnvstr = translate(cnvstr, tblo||'ÖÄÅ', tbli||'öäå')
return cnvstr
/* Process arguments passed to REXX on command line */
prcopt: procedure expose glb. cfg.
parse arg cmdarg
optchr = "-"
posarg = ""
do i=1 to words(cmdarg)
curword = word(cmdarg,i)
if pos(left(curword,1), optchr) \= 0 then do
curopt = translate(substr(curword,2))
select
when (curopt='?') | (curopt='H') then do
call usage
exit 2
end
when (curopt='S') | (curopt='D') | (curopt='U') | (curopt='P') then do
i = i+1
if (i > words(cmdarg)) | (pos(left(word(cmdarg,i),1), optchr) \= 0) then do
say "Command line position "i-1": option value missing for '"curopt"'"
call usage
exit 2
end
select
when curopt='S' then cfg.serverhost = word(cmdarg,i)
when curopt='D' then cfg.startdir = word(cmdarg,i)
when curopt='U' then cfg.user = word(cmdarg,i)
when curopt='P' then cfg.passwd = word(cmdarg,i)
end
end
when curopt='L' then do
if pos("S",cfg.loglvl) = 0 then cfg.loglvl = cfg.loglvl||'S'
if (i+1 <= words(cmdarg)) & (pos(left(word(cmdarg,i+1),1), optchr) = 0) then do
i = i+1
cfg.scrlog = word(cmdarg,i)
end
end
otherwise do
say "Command line position "i": unsupported option '"curopt"'"
call usage
exit 2
end
end
iterate
end
posarg = posarg||' '||curword
end
posarg = strip(posarg)
if posarg \= "" then do
parse var posarg host dir userid password extra
if host \= "" then cfg.serverhost = host
if dir \= "" then cfg.startdir = dir
if userid \= "" then cfg.user = userid
if password \= "" then cfg.passwd = password
if extra \= "" then do
say "Too many positional arguments, "words(posarg)" found (max 4): '"posarg"'"
call usage
exit 2
end
end
return
/* Usage of rxMirror: command line options */
usage: procedure expose glb.
say "Usage: rxMirror [-?h] [[-s] hostname] [[-d] startdir] [[-u] userid]"
say " [[-p] passwd] [-l [scrlog]]"
say
say " ? or h - display this help and exit"
say " s hostname - hostname or IP number of the FTP server, no default"
say " d startdir - directory from which mirroring starts;"
say " rxMirror will recurse into subdirs, no default"
say " u userid - login id on the remote server, default is anonymous"
say " p passwd - password on the ftp server, default is * (for prompt)"
say " l scrlog - screen output log. By default logging is disabled."
say " Default logname is '.mirror.scrlog' in current dir."
say
say "NOTE: For backward compatibility, some option tags are optional."
return