]> git.proxmox.com Git - mirror_edk2.git/blob - AppPkg/Applications/Python/Python-2.7.2/Tools/scripts/ftpmirror.py
EmbeddedPkg: Extend NvVarStoreFormattedLib LIBRARY_CLASS
[mirror_edk2.git] / AppPkg / Applications / Python / Python-2.7.2 / Tools / scripts / ftpmirror.py
1 #! /usr/bin/env python
2
3 """Mirror a remote ftp subtree into a local directory tree.
4
5 usage: ftpmirror [-v] [-q] [-i] [-m] [-n] [-r] [-s pat]
6 [-l username [-p passwd [-a account]]]
7 hostname[:port] [remotedir [localdir]]
8 -v: verbose
9 -q: quiet
10 -i: interactive mode
11 -m: macintosh server (NCSA telnet 2.4) (implies -n -s '*.o')
12 -n: don't log in
13 -r: remove local files/directories no longer pertinent
14 -l username [-p passwd [-a account]]: login info (default .netrc or anonymous)
15 -s pat: skip files matching pattern
16 hostname: remote host w/ optional port separated by ':'
17 remotedir: remote directory (default initial)
18 localdir: local directory (default current)
19 """
20
21 import os
22 import sys
23 import time
24 import getopt
25 import ftplib
26 import netrc
27 from fnmatch import fnmatch
28
29 # Print usage message and exit
30 def usage(*args):
31 sys.stdout = sys.stderr
32 for msg in args: print msg
33 print __doc__
34 sys.exit(2)
35
36 verbose = 1 # 0 for -q, 2 for -v
37 interactive = 0
38 mac = 0
39 rmok = 0
40 nologin = 0
41 skippats = ['.', '..', '.mirrorinfo']
42
43 # Main program: parse command line and start processing
44 def main():
45 global verbose, interactive, mac, rmok, nologin
46 try:
47 opts, args = getopt.getopt(sys.argv[1:], 'a:bil:mnp:qrs:v')
48 except getopt.error, msg:
49 usage(msg)
50 login = ''
51 passwd = ''
52 account = ''
53 if not args: usage('hostname missing')
54 host = args[0]
55 port = 0
56 if ':' in host:
57 host, port = host.split(':', 1)
58 port = int(port)
59 try:
60 auth = netrc.netrc().authenticators(host)
61 if auth is not None:
62 login, account, passwd = auth
63 except (netrc.NetrcParseError, IOError):
64 pass
65 for o, a in opts:
66 if o == '-l': login = a
67 if o == '-p': passwd = a
68 if o == '-a': account = a
69 if o == '-v': verbose = verbose + 1
70 if o == '-q': verbose = 0
71 if o == '-i': interactive = 1
72 if o == '-m': mac = 1; nologin = 1; skippats.append('*.o')
73 if o == '-n': nologin = 1
74 if o == '-r': rmok = 1
75 if o == '-s': skippats.append(a)
76 remotedir = ''
77 localdir = ''
78 if args[1:]:
79 remotedir = args[1]
80 if args[2:]:
81 localdir = args[2]
82 if args[3:]: usage('too many arguments')
83 #
84 f = ftplib.FTP()
85 if verbose: print "Connecting to '%s%s'..." % (host,
86 (port and ":%d"%port or ""))
87 f.connect(host,port)
88 if not nologin:
89 if verbose:
90 print 'Logging in as %r...' % (login or 'anonymous')
91 f.login(login, passwd, account)
92 if verbose: print 'OK.'
93 pwd = f.pwd()
94 if verbose > 1: print 'PWD =', repr(pwd)
95 if remotedir:
96 if verbose > 1: print 'cwd(%s)' % repr(remotedir)
97 f.cwd(remotedir)
98 if verbose > 1: print 'OK.'
99 pwd = f.pwd()
100 if verbose > 1: print 'PWD =', repr(pwd)
101 #
102 mirrorsubdir(f, localdir)
103
104 # Core logic: mirror one subdirectory (recursively)
105 def mirrorsubdir(f, localdir):
106 pwd = f.pwd()
107 if localdir and not os.path.isdir(localdir):
108 if verbose: print 'Creating local directory', repr(localdir)
109 try:
110 makedir(localdir)
111 except os.error, msg:
112 print "Failed to establish local directory", repr(localdir)
113 return
114 infofilename = os.path.join(localdir, '.mirrorinfo')
115 try:
116 text = open(infofilename, 'r').read()
117 except IOError, msg:
118 text = '{}'
119 try:
120 info = eval(text)
121 except (SyntaxError, NameError):
122 print 'Bad mirror info in', repr(infofilename)
123 info = {}
124 subdirs = []
125 listing = []
126 if verbose: print 'Listing remote directory %r...' % (pwd,)
127 f.retrlines('LIST', listing.append)
128 filesfound = []
129 for line in listing:
130 if verbose > 1: print '-->', repr(line)
131 if mac:
132 # Mac listing has just filenames;
133 # trailing / means subdirectory
134 filename = line.strip()
135 mode = '-'
136 if filename[-1:] == '/':
137 filename = filename[:-1]
138 mode = 'd'
139 infostuff = ''
140 else:
141 # Parse, assuming a UNIX listing
142 words = line.split(None, 8)
143 if len(words) < 6:
144 if verbose > 1: print 'Skipping short line'
145 continue
146 filename = words[-1].lstrip()
147 i = filename.find(" -> ")
148 if i >= 0:
149 # words[0] had better start with 'l'...
150 if verbose > 1:
151 print 'Found symbolic link %r' % (filename,)
152 linkto = filename[i+4:]
153 filename = filename[:i]
154 infostuff = words[-5:-1]
155 mode = words[0]
156 skip = 0
157 for pat in skippats:
158 if fnmatch(filename, pat):
159 if verbose > 1:
160 print 'Skip pattern', repr(pat),
161 print 'matches', repr(filename)
162 skip = 1
163 break
164 if skip:
165 continue
166 if mode[0] == 'd':
167 if verbose > 1:
168 print 'Remembering subdirectory', repr(filename)
169 subdirs.append(filename)
170 continue
171 filesfound.append(filename)
172 if info.has_key(filename) and info[filename] == infostuff:
173 if verbose > 1:
174 print 'Already have this version of',repr(filename)
175 continue
176 fullname = os.path.join(localdir, filename)
177 tempname = os.path.join(localdir, '@'+filename)
178 if interactive:
179 doit = askabout('file', filename, pwd)
180 if not doit:
181 if not info.has_key(filename):
182 info[filename] = 'Not retrieved'
183 continue
184 try:
185 os.unlink(tempname)
186 except os.error:
187 pass
188 if mode[0] == 'l':
189 if verbose:
190 print "Creating symlink %r -> %r" % (filename, linkto)
191 try:
192 os.symlink(linkto, tempname)
193 except IOError, msg:
194 print "Can't create %r: %s" % (tempname, msg)
195 continue
196 else:
197 try:
198 fp = open(tempname, 'wb')
199 except IOError, msg:
200 print "Can't create %r: %s" % (tempname, msg)
201 continue
202 if verbose:
203 print 'Retrieving %r from %r as %r...' % (filename, pwd, fullname)
204 if verbose:
205 fp1 = LoggingFile(fp, 1024, sys.stdout)
206 else:
207 fp1 = fp
208 t0 = time.time()
209 try:
210 f.retrbinary('RETR ' + filename,
211 fp1.write, 8*1024)
212 except ftplib.error_perm, msg:
213 print msg
214 t1 = time.time()
215 bytes = fp.tell()
216 fp.close()
217 if fp1 != fp:
218 fp1.close()
219 try:
220 os.unlink(fullname)
221 except os.error:
222 pass # Ignore the error
223 try:
224 os.rename(tempname, fullname)
225 except os.error, msg:
226 print "Can't rename %r to %r: %s" % (tempname, fullname, msg)
227 continue
228 info[filename] = infostuff
229 writedict(info, infofilename)
230 if verbose and mode[0] != 'l':
231 dt = t1 - t0
232 kbytes = bytes / 1024.0
233 print int(round(kbytes)),
234 print 'Kbytes in',
235 print int(round(dt)),
236 print 'seconds',
237 if t1 > t0:
238 print '(~%d Kbytes/sec)' % \
239 int(round(kbytes/dt),)
240 print
241 #
242 # Remove files from info that are no longer remote
243 deletions = 0
244 for filename in info.keys():
245 if filename not in filesfound:
246 if verbose:
247 print "Removing obsolete info entry for",
248 print repr(filename), "in", repr(localdir or ".")
249 del info[filename]
250 deletions = deletions + 1
251 if deletions:
252 writedict(info, infofilename)
253 #
254 # Remove local files that are no longer in the remote directory
255 try:
256 if not localdir: names = os.listdir(os.curdir)
257 else: names = os.listdir(localdir)
258 except os.error:
259 names = []
260 for name in names:
261 if name[0] == '.' or info.has_key(name) or name in subdirs:
262 continue
263 skip = 0
264 for pat in skippats:
265 if fnmatch(name, pat):
266 if verbose > 1:
267 print 'Skip pattern', repr(pat),
268 print 'matches', repr(name)
269 skip = 1
270 break
271 if skip:
272 continue
273 fullname = os.path.join(localdir, name)
274 if not rmok:
275 if verbose:
276 print 'Local file', repr(fullname),
277 print 'is no longer pertinent'
278 continue
279 if verbose: print 'Removing local file/dir', repr(fullname)
280 remove(fullname)
281 #
282 # Recursively mirror subdirectories
283 for subdir in subdirs:
284 if interactive:
285 doit = askabout('subdirectory', subdir, pwd)
286 if not doit: continue
287 if verbose: print 'Processing subdirectory', repr(subdir)
288 localsubdir = os.path.join(localdir, subdir)
289 pwd = f.pwd()
290 if verbose > 1:
291 print 'Remote directory now:', repr(pwd)
292 print 'Remote cwd', repr(subdir)
293 try:
294 f.cwd(subdir)
295 except ftplib.error_perm, msg:
296 print "Can't chdir to", repr(subdir), ":", repr(msg)
297 else:
298 if verbose: print 'Mirroring as', repr(localsubdir)
299 mirrorsubdir(f, localsubdir)
300 if verbose > 1: print 'Remote cwd ..'
301 f.cwd('..')
302 newpwd = f.pwd()
303 if newpwd != pwd:
304 print 'Ended up in wrong directory after cd + cd ..'
305 print 'Giving up now.'
306 break
307 else:
308 if verbose > 1: print 'OK.'
309
310 # Helper to remove a file or directory tree
311 def remove(fullname):
312 if os.path.isdir(fullname) and not os.path.islink(fullname):
313 try:
314 names = os.listdir(fullname)
315 except os.error:
316 names = []
317 ok = 1
318 for name in names:
319 if not remove(os.path.join(fullname, name)):
320 ok = 0
321 if not ok:
322 return 0
323 try:
324 os.rmdir(fullname)
325 except os.error, msg:
326 print "Can't remove local directory %r: %s" % (fullname, msg)
327 return 0
328 else:
329 try:
330 os.unlink(fullname)
331 except os.error, msg:
332 print "Can't remove local file %r: %s" % (fullname, msg)
333 return 0
334 return 1
335
336 # Wrapper around a file for writing to write a hash sign every block.
337 class LoggingFile:
338 def __init__(self, fp, blocksize, outfp):
339 self.fp = fp
340 self.bytes = 0
341 self.hashes = 0
342 self.blocksize = blocksize
343 self.outfp = outfp
344 def write(self, data):
345 self.bytes = self.bytes + len(data)
346 hashes = int(self.bytes) / self.blocksize
347 while hashes > self.hashes:
348 self.outfp.write('#')
349 self.outfp.flush()
350 self.hashes = self.hashes + 1
351 self.fp.write(data)
352 def close(self):
353 self.outfp.write('\n')
354
355 # Ask permission to download a file.
356 def askabout(filetype, filename, pwd):
357 prompt = 'Retrieve %s %s from %s ? [ny] ' % (filetype, filename, pwd)
358 while 1:
359 reply = raw_input(prompt).strip().lower()
360 if reply in ['y', 'ye', 'yes']:
361 return 1
362 if reply in ['', 'n', 'no', 'nop', 'nope']:
363 return 0
364 print 'Please answer yes or no.'
365
366 # Create a directory if it doesn't exist. Recursively create the
367 # parent directory as well if needed.
368 def makedir(pathname):
369 if os.path.isdir(pathname):
370 return
371 dirname = os.path.dirname(pathname)
372 if dirname: makedir(dirname)
373 os.mkdir(pathname, 0777)
374
375 # Write a dictionary to a file in a way that can be read back using
376 # rval() but is still somewhat readable (i.e. not a single long line).
377 # Also creates a backup file.
378 def writedict(dict, filename):
379 dir, fname = os.path.split(filename)
380 tempname = os.path.join(dir, '@' + fname)
381 backup = os.path.join(dir, fname + '~')
382 try:
383 os.unlink(backup)
384 except os.error:
385 pass
386 fp = open(tempname, 'w')
387 fp.write('{\n')
388 for key, value in dict.items():
389 fp.write('%r: %r,\n' % (key, value))
390 fp.write('}\n')
391 fp.close()
392 try:
393 os.rename(filename, backup)
394 except os.error:
395 pass
396 os.rename(tempname, filename)
397
398
399 if __name__ == '__main__':
400 main()