11#! /usr/bin/env python
22
3- # Mirror a remote ftp subtree into a local directory tree.
4- # Basic usage: ftpmirror [options] host remotedir localdir
5- #
6- # XXX To do:
7- # - handle symbolic links
8- # - back up .mirrorinfo before overwriting
9- # - use pickles for .mirrorinfo?
10-
11- import os
12- import sys
13- import time
14- import getopt
15- import string
16- import ftplib
17- from fnmatch import fnmatch
3+ """Mirror a remote ftp subtree into a local directory tree.
184
19- usage_msg = """
205usage: ftpmirror [-v] [-q] [-i] [-m] [-n] [-r] [-s pat]
216 [-l username [-p passwd [-a account]]]
227 hostname [remotedir [localdir]]
2510-i: interactive mode
2611-m: macintosh server (NCSA telnet 2.4) (implies -n -s '*.o')
2712-n: don't log in
28- -r: remove files no longer pertinent
13+ -r: remove local files/directories no longer pertinent
2914-l username [-p passwd [-a account]]: login info (default anonymous ftp)
3015-s pat: skip files matching pattern
3116hostname: remote host
3217remotedir: remote directory (default initial)
3318localdir: local directory (default current)
3419"""
20+
21+ # XXX To do:
22+ # - handle symbolic links
23+ # - back up .mirrorinfo before overwriting
24+
25+ import os
26+ import sys
27+ import time
28+ import getopt
29+ import string
30+ import ftplib
31+ from fnmatch import fnmatch
32+
33+ # Print usage message and exit
3534def usage (* args ):
3635 sys .stdout = sys .stderr
3736 for msg in args : print msg
38- print usage_msg
37+ print __doc__
3938 sys .exit (2 )
4039
4140verbose = 1 # 0 for -q, 2 for -v
@@ -45,6 +44,7 @@ def usage(*args):
4544nologin = 0
4645skippats = ['.' , '..' , '.mirrorinfo' ]
4746
47+ # Main program: parse command line and start processing
4848def main ():
4949 global verbose , interactive , mac , rmok , nologin
5050 try :
@@ -94,6 +94,7 @@ def main():
9494 #
9595 mirrorsubdir (f , localdir )
9696
97+ # Core logic: mirror one subdirectory (recursively)
9798def mirrorsubdir (f , localdir ):
9899 pwd = f .pwd ()
99100 if localdir and not os .path .isdir (localdir ):
@@ -137,7 +138,7 @@ def mirrorsubdir(f, localdir):
137138 continue
138139 if words [- 2 ] == '->' :
139140 if verbose > 1 :
140- print 'Skipping symbolic link %s -> %s' % \
141+ print 'Skipping symbolic link %s -> %s' % \
141142 (words [- 3 ], words [- 1 ])
142143 continue
143144 filename = words [- 1 ]
@@ -259,12 +260,8 @@ def mirrorsubdir(f, localdir):
259260 print 'Local file' , fullname ,
260261 print 'is no longer pertinent'
261262 continue
262- if verbose : print 'Removing local file' , fullname
263- try :
264- os .unlink (fullname )
265- except os .error , msg :
266- print "Can't remove local file %s: %s" % \
267- (fullname , str (msg ))
263+ if verbose : print 'Removing local file/dir' , fullname
264+ remove (fullname )
268265 #
269266 # Recursively mirror subdirectories
270267 for subdir in subdirs :
@@ -294,6 +291,34 @@ def mirrorsubdir(f, localdir):
294291 else :
295292 if verbose > 1 : print 'OK.'
296293
294+ # Helper to remove a file or directory tree
295+ def remove (fullname ):
296+ if os .path .isdir (fullname ) and not os .path .islink (fullname ):
297+ try :
298+ names = os .listdir (fullname )
299+ except os .error :
300+ names = []
301+ ok = 1
302+ for name in names :
303+ if not remove (os .path .join (fullname , name )):
304+ ok = 0
305+ if not ok :
306+ return 0
307+ try :
308+ os .rmdir (fullname )
309+ except os .error , msg :
310+ print "Can't remove local directory %s: %s" % \
311+ (fullname , str (msg ))
312+ return 0
313+ else :
314+ try :
315+ os .unlink (fullname )
316+ except os .error , msg :
317+ print "Can't remove local file %s: %s" % \
318+ (fullname , str (msg ))
319+ return 0
320+ return 1
321+
297322# Wrapper around a file for writing to write a hash sign every block.
298323class LoggingFile :
299324 def __init__ (self , fp , blocksize , outfp ):
0 commit comments