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