home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / rxrsync.zip / rxrsync.rex < prev    next >
OS/2 REXX Batch file  |  1999-11-10  |  11KB  |  386 lines

  1. /* 10 November 1999. Daniel Hellerstein (danielh@crosslink.net)
  2.  
  3.         Rexx procedures to implement the rsync differencing protocol 
  4.  
  5. These OS/2 "classic" REXX callable procedures implement the Rsync 
  6. "client/server differencing" protocol. They require the RxRsync.DLL 
  7. (version 1.1).
  8.  
  9. Basically, rsync is used to form a "difference" between an "old" and a
  10. "new" version of two files; one can then combine the "old" version
  11. and the "difference" to construct an exact copy of the "new" version.
  12. The following outlines there use (for details on rsync, and how to use 
  13. these procedures, please see rsync.doc).
  14.  
  15. I) Installation
  16.  
  17.   1) Make sure that RxRsync.DLL is in your LIBPATH (say, copy it
  18.      to x:\OS2\DLL, or make sure it's in your current directory).
  19.      You will also need a copy of REXXUTIL, but that's part of
  20.      most OS/2 installations.
  21.      
  22.   2) Write a REXX program, and include a copy of this file (say,
  23.      put it at the end of your file)
  24.  
  25.   3) Call the procedures.
  26.  
  27.  
  28. II) Description of procedures
  29.      
  30. There are three procedures:
  31.  
  32.      rsync_client: creates a synopsis of an old version.
  33.  
  34.        status=rsync_client(oldver_file,synopsis_file,comment,quiet,blocksize)
  35.        quiet, blocksize, and comment are optional parameters
  36.  
  37.        Note that rsync_client has a few user changeable parameters
  38.         
  39.      rsync_server: uses this synopsis, and the new version, to create
  40.                    and rsync difference file
  41.  
  42.           status=rsync_server(synopsis_string,newver_file,diff_file,quiet)
  43.           quiet is an optional parameter
  44.  
  45.      rsync_undiff: uses the difference file, and the old version, to
  46.                    build a copy of the new version
  47.  
  48.          status=rsync_undiff(oldver_file,diff_file,newver_file_dup,quiet)
  49.          quiet is an optional parameters
  50.  
  51.          Note that rsync_undiff has a few user changeable parameters
  52.  
  53. See RSYNCTST.CMD for an example of how to use RxRsync.cmd
  54. */
  55.  
  56. /*******************************************************************/
  57. /*  rsync_client: creates a synopsis of an old version.
  58.  
  59.        status=rsync_client(oldver_file,synopsis_file,comment,quiet,blocksize)
  60.        quiet, blocksize, and comment are optional parameters
  61. */
  62.  
  63. rsync_client:procedure
  64.  
  65. /*********  USER changeable parameters  ************/
  66. /* (larger blocksizes means smaller synopsis, but possibly larger difference */
  67. blocksize=500
  68.  
  69. quiet=0         /*  default status messages: 1-terse, 0=normal */
  70.  
  71.  
  72. /**** END of USER changeable parameters  ************/
  73.  
  74. parse arg afile,outfile,comment,verbo,bsize
  75.  
  76. a=rsync_load_dlls()
  77. if a<>0 then return a
  78.  
  79. if afile='' then return "ERROR no old-version file specified"
  80. if outfile='' then return "ERROR no synopsis-file specified "
  81.  
  82. if verbo<>'' then verbose=verbo
  83. if bsize<>'' then do
  84.    if datatype(bsize)='NUM' then blocksize=bsize
  85. end /* do */
  86.  
  87. select
  88.    when verbose=0 then verbose=1
  89.    when verbose=1 then verbose=0
  90.    otherwise nop
  91. end
  92.  
  93. reportat=verbose
  94. if verbose<2 then reportat=250000
  95.  
  96. crlf='0d0a'x
  97. a=time('r')
  98.  
  99. if comment='' then comment=date('n')||' '||time('n')
  100.  
  101. /* read "Afile" */
  102. aa=translate(stream(afile,'c','open read'))
  103. if  abbrev(aa,'READY')=0 then return "ERROR could not open "afile
  104. isize=stream(afile,'c','query size')
  105. if isize='' | isize=0 then do
  106.     return 'ERROR 'afile " is unaccessible"
  107.     exit
  108. end
  109. astuff=charin(afile,1,isize)
  110. aa=stream(afile,'c','close')
  111.  
  112. amd4=rx_md4(astuff)
  113. if verbose>0 then  do
  114.   say "Rsync Client: read "isize" bytes from "afile
  115. end
  116.  
  117. /* break into chunks of size blocksize, and compute "adler" and md4 checksums */
  118. ifoo=trunc(isize/blocksize+0.999999)
  119.  
  120. /* Structure of client request file
  121.    Comment  -- 80 characters (i.e.; requested file name)
  122.    1 space
  123.    Blocksize  -- 6 digit integer
  124.    1 space
  125.    #Blocks    -- 8 digit character
  126.    1 space
  127.    md4        -- 32 digit md4
  128.    3 spaces
  129.    rsync1||md41||..||rsyncN||md4||N -- rsync and md4 values (machine format)
  130. */
  131.  
  132. ac1=left(comment,80)||' '||left(blocksize,6)||' '||left(ifoo,8)||' '||amd4||'   '
  133. iat=1
  134. do mm=1 to ifoo
  135.   if mm=ifoo then
  136.      ablock=substr(astuff,iat)
  137.   else
  138.      ablock=substr(astuff,iat,blocksize)
  139.   ac1=ac1||x2c(rx_rsync32_md4(ablock))
  140.   iat=iat+blocksize
  141. end
  142. foo=time('e')
  143. if verbose>0 then say '   Done creating hashes for  'ifoo' blocks'
  144.  
  145. foo=sysfiledelete(outfile)
  146. foo=charout(outfile,ac1,1)          /* create the message the client send to the server*/
  147. if foo<>0 then do
  148.   foo=stream(outfile,'c','close')
  149.   return "ERROR problem writing synopsis file "outfile
  150. end /* do */
  151. foo=stream(outfile,'c','close')
  152. b=time('e')
  153. if verbose>0 then 
  154.    say '   Saving synopsis file to 'outfile' [elapsed time='||strip(b,'t','0')
  155. nn=length(ac1)
  156. drop ac1
  157. return 'OK 'nn ' bytes written to synopsis file 'outfile
  158.  
  159.  
  160. /*******************************************************************/
  161. /*     rsync_server: uses this synopsis, and the new version, to create
  162.                    and rsync difference file
  163.  
  164.           status=rsync_server(synopsis_string,newver_file,diff_file,quiet)
  165.           quiet is an optional parameter
  166. *****************************************/
  167.  
  168. rsync_server: procedure
  169.  
  170. parse arg sfile,newverfile,outfile,verbo
  171.  
  172. a=rsync_load_dlls()
  173. if a<>0 then return a
  174.  
  175. if sfile='' then return "ERROR no synopsis file specified "
  176. if newverfile='' then return "ERROR no new_version file specified"
  177. if outfile='' then return "ERROR no difference file specified "
  178.  
  179. if verbo<>'' then verbose=verbo
  180. select
  181.    when verbose=0 then verbose=1
  182.    when verbose=1 then verbose=0
  183.    otherwise nop
  184. end
  185.  
  186. crlf='0d0a'x
  187. a=time('r')
  188.  
  189. b=time('r')
  190.  
  191. /* read "synopsis file" */
  192. aa=translate(stream(sfile,'c','open read'))
  193. if  abbrev(aa,'READY')=0 then return "ERROR could not open "sfile
  194. issize=stream(sfile,'c','query size')
  195. if issize='' | issize=0 then do
  196.     return 'ERROR 'sfile " is unaccessible"
  197.     exit
  198. end
  199. synopsis=charin(sfile,1,issize)
  200. aa=stream(sfile,'c','close')
  201. if verbose>0 then  do
  202.     say "Rsync server: read "issize" bytes in the synopsis file"
  203. end
  204.  
  205. in1=left(synopsis,132)
  206. parse var in1 comment +80 iblock numblocks amd4 gotcts .
  207.  
  208.  
  209. if datatype(iblock)<>'NUM' | datatype(numblocks)<>'NUM' then do
  210.     return 'ERROR not a proper synopsis 'iblock numblocks
  211. end
  212.  
  213. if verbose>0 then do
  214.   say "   Comment: "||left(comment,64)
  215.   say "            (client used blocksize=" iblock ', and sent 'numblocks' blocks '
  216. end
  217.  
  218. if stream(newverfile,'c','query exists')='' then  do
  219. say " sss "newverfile
  220.   return 'ERROR no such new_version file '||newverfile
  221. end
  222.  
  223. if verbose>0 then  do
  224.    nn=stream(newverfile,'c','query size')
  225.     say "   Read "nn" bytes in "newverfile
  226. end
  227.  
  228. /* call the integrated rsync_server procedure */
  229. foo=sysfiledelete(outfile)
  230. if foo>2 then return "ERROR could not delete old version of "outfile
  231.  
  232. status=rx_rsync_server(newverfile,synopsis,outfile)
  233.  
  234. b=time('e')
  235.  
  236. if verbose>0 then do
  237.     nn=stream(outfile,'c','query size')
  238.     say '   Saving difference file to 'outfile '[elapsed time='||strip(b,'t','0')
  239. end
  240.  
  241. return status
  242.  
  243.  
  244. /***************************************************/
  245. /*  rsync_undiff: uses the difference file, and the old version, to
  246.                    build a copy of the new version
  247.  
  248.      status=rsync_undiff(oldver_file,diff_file,newfile,quiet)
  249. **************************************/
  250.  
  251. rsync_undiff:procedure
  252.   parse arg afile,dfile,outfile,verbo
  253.  
  254. a=rsync_load_dlls()
  255. if a<>0 then return a
  256.  
  257. if afile='' then return "ERROR no old_version file specified"
  258. if dfile='' then return "ERROR no difference-file specified "
  259. if newfile='' then return "ERROR no new_version file specified "
  260.  
  261. if verbo<>'' then verbose=verbo
  262. select
  263.    when verbose=0 then verbose=1
  264.    when verbose=1 then verbose=0
  265.    otherwise nop
  266. end
  267.  
  268. crlf='0d0a'x
  269. a=time('r')
  270.  
  271. /* read "diff Afile" */
  272. aa=translate(stream(dfile,'c','open read'))
  273. if  abbrev(aa,'READY')=0 then return "ERROR could not open "dfile
  274. idsize=stream(dfile,'c','query size')
  275. if idsize='' | idsize=0 then do
  276.     return 'ERROR 'dfile " is unaccessible"
  277.     exit
  278. end
  279. s1=charin(dfile,1,idsize)
  280. aa=stream(dfile,'c','close')
  281. if verbose>0 then  do
  282.     say "Rsync undiff: read "idsize" bytes in the difference file"
  283. end
  284.  
  285. /* read "Afile" */
  286. aa=translate(stream(afile,'c','open read'))
  287. if  abbrev(aa,'READY')=0 then return "ERROR could not open "afile
  288. iasize=stream(afile,'c','query size')
  289. if iasize='' | iasize=0 then do
  290.     return 'ERROR 'afile " is unaccessible"
  291.     exit
  292. end
  293. astuff=charin(afile,1,iasize)
  294. aa=stream(afile,'c','close')
  295. if verbose>0 then  do
  296.     say "   "iasize" bytes in the old_version file"
  297. end
  298.  
  299. amd4=rx_md4(astuff)            /* md4 of old version */
  300.  
  301. /* get md4 and blocksize from s1 */
  302. parse var s1 smd4 bsize (crlf) s1
  303. if strip(translate(amd4))=strip(translate(smd4)) then do
  304.    if verbose>0 then
  305.        say "   File has not changed! "
  306.    bstuff=astuff
  307.    signal writeme1    
  308. end 
  309.  
  310. /* start building. Each records starts with a single character identifier,
  311. either a B or a C. Following the identifier is:
  312.    B: a block start: block end number, and then a crlf
  313.    C: a count of bytes (nnn), a ":", a string of length nnn, and a crlf
  314. */
  315. bstuff=''
  316. noted=0
  317. do forever
  318.    if length(s1)=0 | s1='' then leave
  319.    mbl=lengtH(bstuff)
  320.    if  mbl-noted> reportat then do
  321.       if verbose>0 then say "... Rsync client: # characters recovered "mbl
  322.       noted=mbl
  323.    end /* do */
  324.    parse var s1 atype +1 s1  ; atype=translate(atype)
  325.    select
  326.       when atype='C' then do
  327.          parse var s1 nnn ':' s1
  328.          parse var s1 ccs +(nnn) (crlf) s1
  329.          bstuff=bstuff||ccs
  330.       end /* do */
  331.       when atype='B' then do
  332.          parse var s1 idb1 ':' idb2 (crlf)  s1
  333.          i1=((idb1-1)*bsize)+1
  334.          i2=idb2*bsize
  335.          i2=min(i2,iasize)
  336.          bstuff=bstuff||substr(astuff,i1,1+i2-i1)
  337.       end /* do */
  338.       otherwise do
  339.          return "ERROR in differnce file: unknown type= "atype
  340.      end
  341.    end  /* select */
  342. end /* do */
  343.  
  344. /* compute md4 of this constructed file */
  345. b2md4=rx_md4(bstuff)
  346. if strip(translate(b2md4)) <> strip(translate(smd4)) then do
  347.    return "ERROR md4 does not match: "b2md4', 'smd4
  348. end /* do */
  349.  
  350. writeme1:   nop                 /* jump here if no change */
  351. foo=sysfiledelete(outfile)
  352. if foo>2 then return 'ERROR unable to delete output file 'outfile
  353. foo=charout(outfile,bstuff,1)          /* save the computed new file */
  354. foo=stream(outfile,'c','close')
  355. b=time('e')
  356. if verbose>0 then
  357.   say '   Saving duplicate to 'outfile ' [elapsed time='||strip(b,'t','0')
  358.  
  359. nn=length(bstuff)
  360. drop bstuff; drop astuff
  361.  
  362. return 'OK 'nn ' bytes written to  'outfile
  363.  
  364.  
  365. /****************************************************************/
  366. /* read in some useful procedures */
  367. rsync_load_dlls:procedure
  368. if rxfuncquery('rx_md4')=1  then do
  369.   call RXFuncAdd 'RXRsyncLoad', 'RXRSYNC', 'RxRsyncLoad'
  370.   call RxRsyncLoad
  371. end
  372. if rxfuncquery('rx_md4')=1  then do
  373.   return "ERROR could not load  RxRsync.DLL"
  374. end
  375. /* Load up advanced REXX functions */
  376. foo=rxfuncquery('sysloadfuncs')
  377. if foo=1 then do
  378.   call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  379.   call SysLoadFuncs
  380. end
  381. if rxfuncquery('sysfiledelete')=1  then do
  382.   return "ERROR could not load  REXXUTIL.DLL"
  383. end
  384. return 0
  385.  
  386.