Guido van Rossum | c636014 | 1990-10-13 19:23:40 +0000 | [diff] [blame] | 1 | # Module 'cmp' |
| 2 | |
| 3 | # Efficiently compare files, boolean outcome only (equal / not equal). |
| 4 | |
| 5 | # Tricks (used in this order): |
| 6 | # - Files with identical type, size & mtime are assumed to be clones |
| 7 | # - Files with different type or size cannot be identical |
| 8 | # - We keep a cache of outcomes of earlier comparisons |
| 9 | # - We don't fork a process to run 'cmp' but read the files ourselves |
| 10 | |
| 11 | import posix |
| 12 | |
| 13 | cache = {} |
| 14 | |
| 15 | def cmp(f1, f2): # Compare two files, use the cache if possible. |
| 16 | # Return 1 for identical files, 0 for different. |
| 17 | # Raise exceptions if either file could not be statted, read, etc. |
| 18 | s1, s2 = sig(posix.stat(f1)), sig(posix.stat(f2)) |
| 19 | if s1[0] <> 8 or s2[0] <> 8: |
| 20 | # Either is a not a plain file -- always report as different |
| 21 | return 0 |
| 22 | if s1 = s2: |
| 23 | # type, size & mtime match -- report same |
| 24 | return 1 |
| 25 | if s1[:2] <> s2[:2]: # Types or sizes differ, don't bother |
| 26 | # types or sizes differ -- report different |
| 27 | return 0 |
| 28 | # same type and size -- look in the cache |
| 29 | key = f1 + ' ' + f2 |
| 30 | try: |
| 31 | cs1, cs2, outcome = cache[key] |
| 32 | # cache hit |
| 33 | if s1 = cs1 and s2 = cs2: |
| 34 | # cached signatures match |
| 35 | return outcome |
| 36 | # stale cached signature(s) |
| 37 | except RuntimeError: |
| 38 | # cache miss |
| 39 | pass |
| 40 | # really compare |
| 41 | outcome = do_cmp(f1, f2) |
| 42 | cache[key] = s1, s2, outcome |
| 43 | return outcome |
| 44 | |
| 45 | def sig(st): # Return signature (i.e., type, size, mtime) from raw stat data |
| 46 | # 0-5: st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid |
| 47 | # 6-9: st_size, st_atime, st_mtime, st_ctime |
| 48 | type = st[0] / 4096 |
| 49 | size = st[6] |
| 50 | mtime = st[8] |
| 51 | return type, size, mtime |
| 52 | |
| 53 | def do_cmp(f1, f2): # Compare two files, really |
| 54 | bufsize = 8096 # Could be tuned |
| 55 | fp1 = open(f1, 'r') |
| 56 | fp2 = open(f2, 'r') |
| 57 | while 1: |
| 58 | b1 = fp1.read(bufsize) |
| 59 | b2 = fp2.read(bufsize) |
| 60 | if b1 <> b2: return 0 |
| 61 | if not b1: return 1 |