#!/usr/bin/python3 -s

import sys
import collections
import getopt

import pycdlib

def hexdump(st):
    return ':'.join(x.encode('hex') for x in st)

def obj_cmp_int(obj1, obj2, name, field):
    val1 = getattr(obj1, field)
    val2 = getattr(obj2, field)
    if val1 != val2:
        print("%s#1 %s (0x%x) != %s#2 %s (0x%x)" % (name, field, val1, name, field, val2))

def obj_cmp_str(obj1, obj2, name, field):
    val1 = getattr(obj1, field)
    val2 = getattr(obj2, field)
    if val1 != val2:
        print("%s#1 %s (%s) != %s#2 %s (%s)" % (name, field, hexdump(val1), name, field, hexdump(val2)))

def cmp_if_both_not_none(obj1, obj2, name, field, cmpfunc):
    val1 = getattr(obj1, field)
    val2 = getattr(obj2, field)
    if val1 is not None and val2 is None:
        print("%s#1 has %s while %s#2 does not" % (name, field, name))
    elif val1 is None and val2 is not None:
        print("%s#1 has no %s while %s#2 does" % (name, field, name))
    elif val1 is not None and val2 is not None:
        cmpfunc(val1, val2, name)

def pvd_cmp(obj1, obj2):
    obj_cmp_int(obj1, obj2, 'PVD', 'descriptor_type')
    obj_cmp_int(obj1, obj2, 'PVD', 'identifier')
    obj_cmp_int(obj1, obj2, 'PVD', 'version')
    obj_cmp_str(obj1, obj2, 'PVD', 'system_identifier')
    obj_cmp_str(obj1, obj2, 'PVD', 'volume_identifier')
    obj_cmp_int(obj1, obj2, 'PVD', 'space_size')
    obj_cmp_int(obj1, obj2, 'PVD', 'set_size')
    obj_cmp_int(obj1, obj2, 'PVD', 'seqnum')
    obj_cmp_int(obj1, obj2, 'PVD', 'log_block_size')
    obj_cmp_int(obj1, obj2, 'PVD', 'path_tbl_size')
    obj_cmp_int(obj1, obj2, 'PVD', 'path_table_location_le')
    obj_cmp_int(obj1, obj2, 'PVD', 'path_table_location_be')
    obj_cmp_int(obj1, obj2, 'PVD', 'optional_path_table_location_le')
    obj_cmp_int(obj1, obj2, 'PVD', 'optional_path_table_location_be')
    dir_record_cmp(obj1.root_dir_record, obj2.root_dir_record, 'Root Dir Record')
    obj_cmp_str(obj1, obj2, 'PVD', 'volume_set_identifier')
    ident_cmp(obj1.publisher_identifier, obj2.publisher_identifier, 'Publisher Identifier')
    ident_cmp(obj1.preparer_identifier, obj2.preparer_identifier, 'Preparer Identifier')
    ident_cmp(obj1.application_identifier, obj2.application_identifier, 'Application Identifier')
    obj_cmp_str(obj1, obj2, 'PVD', 'copyright_file_identifier')
    obj_cmp_str(obj1, obj2, 'PVD', 'abstract_file_identifier')
    obj_cmp_str(obj1, obj2, 'PVD', 'bibliographic_file_identifier')
    if compare_date:
        vol_date_cmp(obj1.volume_creation_date, obj2.volume_creation_date, 'Volume Creation Date')
        vol_date_cmp(obj1.volume_modification_date, obj2.volume_modification_date, 'Volume Modification Date')
        vol_date_cmp(obj1.volume_expiration_date, obj2.volume_expiration_date, 'Volume Expiration Date')
        vol_date_cmp(obj1.volume_effective_date, obj2.volume_effective_date, 'Volume Effective Date')
    obj_cmp_int(obj1, obj2, 'PVD', 'file_structure_version')
    obj_cmp_str(obj1, obj2, 'PVD', 'application_use')
    for index,ptr in enumerate(obj1.path_table_records):
        obj_cmp_int(obj1.path_table_records[index], obj2.path_table_records[index], 'PVD Path Table Records', 'len_di')
        obj_cmp_int(obj1.path_table_records[index], obj2.path_table_records[index], 'PVD Path Table Records', 'xattr_length')
        obj_cmp_int(obj1.path_table_records[index], obj2.path_table_records[index], 'PVD Path Table Records', 'extent_location')
        obj_cmp_int(obj1.path_table_records[index], obj2.path_table_records[index], 'PVD Path Table Records', 'parent_directory_num')
        obj_cmp_str(obj1.path_table_records[index], obj2.path_table_records[index], 'PVD Path Table Records', 'directory_identifier')
        obj_cmp_int(obj1.path_table_records[index], obj2.path_table_records[index], 'PVD Path Table Records', 'directory_num')

def dir_record_date_cmp(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, 'Dir Record date years_since_1900', 'years_since_1900')
    obj_cmp_int(obj1, obj2, 'Dir Record date month', 'month')
    obj_cmp_int(obj1, obj2, 'Dir Record date day_of_month', 'day_of_month')
    obj_cmp_int(obj1, obj2, 'Dir Record date hour', 'hour')
    obj_cmp_int(obj1, obj2, 'Dir Record date minute', 'minute')
    obj_cmp_int(obj1, obj2, 'Dir Record date second', 'second')
    obj_cmp_int(obj1, obj2, 'Dir Record date gmtoffset', 'gmtoffset')

def rr_cmp_sp(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, 'RR SP Bytes to Skip', 'bytes_to_skip')

def rr_cmp_rr(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, "RR RR Flags", 'rr_flags')

def rr_cmp_ce(obj1, obj2, name):
    cont_entry1 = obj1.continuation_entry
    cont_entry2 = obj2.continuation_entry
    obj_cmp_int(cont_entry1, cont_entry2, 'RR CE extent', 'orig_extent_loc')
    obj_cmp_int(cont_entry1, cont_entry2, 'RR CE offset', 'continue_offset')
    obj_cmp_int(cont_entry1, cont_entry2, 'RR CE length', 'continue_length')
    rr_cmp(cont_entry1, cont_entry2, name)

def rr_cmp_px(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, "RR PX File Mode", 'posix_file_mode')
    obj_cmp_int(obj1, obj2, "RR PX File links", 'posix_file_links')
    obj_cmp_int(obj1, obj2, "RR PX User id", 'posix_user_id')
    obj_cmp_int(obj1, obj2, "RR PX Group id", 'posix_group_id')
    obj_cmp_int(obj1, obj2, "RR PX Serial Number", 'posix_serial_number')

def rr_cmp_er(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, "RR ER Ext ID", 'ext_id')
    obj_cmp_int(obj1, obj2, "RR ER Ext Des", 'ext_des')
    obj_cmp_int(obj1, obj2, "RR ER Ext SRC", 'ext_src')

def rr_cmp_es(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, "RR ES sequence", 'extension_sequence')

def rr_cmp_pn(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, "RR PN dev_high", 'dev_t_high')
    obj_cmp_int(obj1, obj2, "RR PN dev_low", 'dev_t_low')

def rr_cmp_nm(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, "RR NM flags", 'posix_name_flags')
    obj_cmp_str(obj1, obj2, "RR NM name", 'posix_name')

def rr_cmp_cl(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, 'RR CL log_block', 'child_log_block_num')

def rr_cmp_pl(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, 'RR PL log_block', 'parent_log_block_num')

def rr_cmp_tf(obj1, obj2, name):
    cmpfunc = dir_record_date_cmp
    if obj1.time_flags & (1 << 7) and not obj2.time_flags & (1 << 7):
        print("RR TF obj1 flags are for vol times, obj2 are for dir record times")
        return
    elif not obj1.time_flags & (1 << 7) and obj2.time_flags & (1 << 7):
        print("RR_TF obj1 flags are for dir record times, obj2 are for vol times")
        return
    elif obj1.time_flags & (1 << 7) and obj2.time_flags & (1 << 7):
        cmpfunc = vol_date_cmp

    cmp_if_both_not_none(obj1, obj2, 'RR TF creation time', 'creation_time', cmpfunc)
    cmp_if_both_not_none(obj1, obj2, 'RR TF access time', 'access_time', cmpfunc)

def rr_cmp_sf(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, 'RR SF virtual size high', 'virtual_file_size_high')
    obj_cmp_int(obj1, obj2, 'RR SF virtual size low', 'virtual_file_size_low')

def rr_cmp_re(obj1, obj2, name):
    # Nothing to do here!
    pass

def rr_cmp_sl(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, 'RR SL flags', 'flags')
    if len(obj1.symlink_components) != len(obj2.symlink_components):
        print("RR SL obj1 has %d components, obj2 has %d" % (len(obj1.symlink_components), len(obj2.symlink_components)))
        return

    for index,comp in enumerate(obj1.symlink_components):
        comp1 = obj1.symlink_components[index]
        comp2 = obj2.symlink_components[index]

        if comp1 != comp2:
            print("RR SL component %d %s#1 (%s) != %s#2 (%s)" % (index, name, comp1, name, comp2))

def rr_cmp(obj1, obj2, name):
    cmp_if_both_not_none(obj1, obj2, 'SP Record', 'sp_record', rr_cmp_sp)
    cmp_if_both_not_none(obj1, obj2, 'RR Record', 'rr_record', rr_cmp_rr)
    cmp_if_both_not_none(obj1, obj2, 'CE Record', 'ce_record', rr_cmp_ce)
    cmp_if_both_not_none(obj1, obj2, 'PX Record', 'px_record', rr_cmp_px)
    cmp_if_both_not_none(obj1, obj2, 'ER Record', 'er_record', rr_cmp_er)
    cmp_if_both_not_none(obj1, obj2, 'ES Record', 'es_record', rr_cmp_es)
    cmp_if_both_not_none(obj1, obj2, 'PN Record', 'pn_record', rr_cmp_pn)
    if len(obj1.sl_records) != len(obj2.sl_records):
        print("RR SL records for 1 and 2 are not the same length!")
    else:
        for index,rec in enumerate(obj1.sl_records):
            rr_cmp_sl(obj1.sl_records[index], obj2.sl_records[index], 'SL Record')
    cmp_if_both_not_none(obj1, obj2, 'NM Record', 'nm_record', rr_cmp_nm)
    cmp_if_both_not_none(obj1, obj2, 'CL Record', 'cl_record', rr_cmp_cl)
    cmp_if_both_not_none(obj1, obj2, 'PL Record', 'pl_record', rr_cmp_pl)
    if compare_date:
        cmp_if_both_not_none(obj1, obj2, 'TF Record', 'tf_record', rr_cmp_tf)
    cmp_if_both_not_none(obj1, obj2, 'SF Record', 'sf_record', rr_cmp_sf)
    cmp_if_both_not_none(obj1, obj2, 'RE Record', 're_record', rr_cmp_re)

def dir_record_cmp(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, name, 'dr_len')
    obj_cmp_int(obj1, obj2, name, 'xattr_len')
    obj_cmp_int(obj1, obj2, name, 'orig_extent_loc')
    obj_cmp_int(obj1, obj2, name, 'new_extent_loc')
    obj_cmp_int(obj1, obj2, name, 'data_length')
    if compare_date:
        dir_record_date_cmp(obj1.date, obj2.date, name)
    obj_cmp_int(obj1, obj2, name, 'file_flags')
    obj_cmp_int(obj1, obj2, name, 'file_unit_size')
    obj_cmp_int(obj1, obj2, name, 'interleave_gap_size')
    obj_cmp_int(obj1, obj2, name, 'seqnum')
    obj_cmp_int(obj1, obj2, name, 'len_fi')
    # Also check for rock ridge extensions
    cmp_if_both_not_none(obj1, obj2, 'Rock Ridge', 'rock_ridge', rr_cmp)

def ident_cmp(obj1, obj2, name):
    obj_cmp_str(obj1, obj2, name, 'text')

def vol_date_cmp(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, name, 'year')
    obj_cmp_int(obj1, obj2, name, 'month')
    obj_cmp_int(obj1, obj2, name, 'dayofmonth')
    obj_cmp_int(obj1, obj2, name, 'hour')
    obj_cmp_int(obj1, obj2, name, 'minute')
    obj_cmp_int(obj1, obj2, name, 'second')
    obj_cmp_int(obj1, obj2, name, 'hundredthsofsecond')
    obj_cmp_int(obj1, obj2, name, 'gmtoffset')
    obj_cmp_int(obj1, obj2, name, 'present')
    obj_cmp_str(obj1, obj2, name, 'date_str')

def isohybrid_cmp(obj1, obj2, name):
    obj_cmp_str(obj1, obj2, 'ISO Hybrid', 'mbr')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'rba')
    obj_cmp_str(obj1, obj2, 'ISO Hybrid', 'mbr_id')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'part_entry')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'bhead')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'bsect')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'bcyle')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'ptype')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'ehead')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'part_offset')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'geometry_heads')
    obj_cmp_int(obj1, obj2, 'ISO Hybrid', 'geometry_sectors')

def br_cmp(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, name, 'descriptor_type')
    obj_cmp_str(obj1, obj2, name, 'identifier')
    obj_cmp_int(obj1, obj2, name, 'version')
    obj_cmp_str(obj1, obj2, name, 'boot_system_identifier')
    obj_cmp_str(obj1, obj2, name, 'boot_identifier')
    obj_cmp_str(obj1, obj2, name, 'boot_system_use')
    obj_cmp_int(obj1, obj2, name, 'orig_extent_loc')

def vdst_cmp(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, name, 'descriptor_type')
    obj_cmp_str(obj1, obj2, name, 'identifier')
    obj_cmp_int(obj1, obj2, name, 'version')
    obj_cmp_int(obj1, obj2, name, 'orig_extent_loc')

def svd_cmp(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, name, 'descriptor_type')
    obj_cmp_str(obj1, obj2, name, 'identifier')
    obj_cmp_int(obj1, obj2, name, 'version')
    obj_cmp_int(obj1, obj2, name, 'flags')
    obj_cmp_str(obj1, obj2, name, 'system_identifier')
    obj_cmp_str(obj1, obj2, name, 'volume_identifier')
    obj_cmp_int(obj1, obj2, name, 'space_size')
    obj_cmp_str(obj1, obj2, name, 'escape_sequences')
    obj_cmp_int(obj1, obj2, name, 'set_size')
    obj_cmp_int(obj1, obj2, name, 'seqnum')
    obj_cmp_int(obj1, obj2, name, 'log_block_size')
    obj_cmp_int(obj1, obj2, name, 'path_tbl_size')
    obj_cmp_int(obj1, obj2, name, 'path_table_location_le')
    obj_cmp_int(obj1, obj2, name, 'path_table_location_be')
    obj_cmp_int(obj1, obj2, name, 'optional_path_table_location_le')
    obj_cmp_int(obj1, obj2, name, 'optional_path_table_location_be')
    dir_record_cmp(obj1.root_dir_record, obj2.root_dir_record, 'SVD Root Dir Record')
    obj_cmp_str(obj1, obj2, name, 'volume_set_identifier')
    ident_cmp(obj1.publisher_identifier, obj2.publisher_identifier, 'SVD Publisher Identifier')
    ident_cmp(obj1.preparer_identifier, obj2.preparer_identifier, 'SVD Preparer Identifier')
    ident_cmp(obj1.application_identifier, obj2.application_identifier, 'SVD Application Identifier')
    obj_cmp_str(obj1, obj2, name, 'copyright_file_identifier')
    obj_cmp_str(obj1, obj2, name, 'abstract_file_identifier')
    obj_cmp_str(obj1, obj2, name, 'bibliographic_file_identifier')
    if compare_date:
        vol_date_cmp(obj1.volume_creation_date, obj2.volume_creation_date, 'SVD Volume Creation Date')
        vol_date_cmp(obj1.volume_modification_date, obj2.volume_modification_date, 'SVD Volume Modification Date')
        vol_date_cmp(obj1.volume_expiration_date, obj2.volume_expiration_date, 'SVD Volume Expiration Date')
        vol_date_cmp(obj1.volume_effective_date, obj2.volume_effective_date, 'SVD Volume Effective Date')
    obj_cmp_int(obj1, obj2, name, 'file_structure_version')
    obj_cmp_str(obj1, obj2, name, 'application_use')

def eltorito_cmp(obj1, obj2, name):
    obj_cmp_int(obj1, obj2, 'El Torito state', 'state')
    obj_cmp_int(obj1.validation_entry, obj2.validation_entry, 'El Torito validation entry', 'header_id')
    obj_cmp_int(obj1.validation_entry, obj2.validation_entry, 'El Torito validation entry', 'platform_id')
    obj_cmp_str(obj1.validation_entry, obj2.validation_entry, 'El Torito validation entry', 'id_string')
    obj_cmp_int(obj1.validation_entry, obj2.validation_entry, 'El Torito validation entry', 'checksum')
    obj_cmp_int(obj1.validation_entry, obj2.validation_entry, 'El Torito validation entry', 'keybyte1')
    obj_cmp_int(obj1.validation_entry, obj2.validation_entry, 'El Torito validation entry', 'keybyte2')

    obj_cmp_int(obj1.initial_entry, obj2.initial_entry, 'El Torito initial entry', 'boot_indicator')
    obj_cmp_int(obj1.initial_entry, obj2.initial_entry, 'El Torito initial entry', 'boot_media_type')
    obj_cmp_int(obj1.initial_entry, obj2.initial_entry, 'El Torito initial entry', 'load_segment')
    obj_cmp_int(obj1.initial_entry, obj2.initial_entry, 'El Torito initial entry', 'system_type')
    obj_cmp_int(obj1.initial_entry, obj2.initial_entry, 'El Torito initial entry', 'sector_count')
    obj_cmp_int(obj1.initial_entry, obj2.initial_entry, 'El Torito initial entry', 'load_rba')

    dir_record_cmp(obj1.dirrecord, obj2.dirrecord, "El Torito dir record")
    # FIXME: Check eltorito section headers
    # FIXME: Check eltorito section entries

def content_cmp(obj1, obj2):
    fp1old = fp1.tell()
    fp2old = fp2.tell()

    fp1.seek(obj1.orig_extent_loc * 2048)
    fp2.seek(obj2.orig_extent_loc * 2048)

    data1 = fp1.read(obj1.data_length)
    data2 = fp2.read(obj2.data_length)

    if data1 != data2:
        print("%s#1 contents != %s#2 contents" % (obj1.file_ident, obj2.file_ident))

    fp1.seek(fp1old)
    fp2.seek(fp2old)

# ==========================================================================

def usage():
    print("Usage: %s <iso1> <iso2>" % (sys.argv[0]))
    print(" OPTIONS:")
    print("  -d (--no-date)\tDon't compare dates")
    sys.exit(1)

try:
    opts, args = getopt.gnu_getopt(sys.argv[1:], 'd', ['no-date'])
except getopt.GetoptError as err:
    print(str(err))
    usage()

compare_date = True
for o, a in opts:
    if o in ('-d', '--no-date'):
        compare_date = False

if len(args) != 2:
    usage()

file1 = args[0]
file2 = args[1]

fp1 = open(file1, 'rb')
fp2 = open(file2, 'rb')

fp1.seek(0, 2)
fp1_len = fp1.tell()
fp1.seek(0)

fp2.seek(0, 2)
fp2_len = fp2.tell()
fp2.seek(0)

iso1 = pycdlib.PyCdlib()
iso2 = pycdlib.PyCdlib()

# Open up the two isos
print("Parsing file1 %s..." % (file1))
iso1.open_fp(fp1)
print("Parsing file2 %s..." % (file2))
iso2.open_fp(fp2)

# Now start comparing them
if fp1_len != fp2_len:
    print("ISO1 has length %d, ISO2 has length %d" % (fp1_len, fp2_len))

# Check the PVD
pvd_cmp(iso1.pvd, iso2.pvd)

# Check the isohybrid (if it exists)
cmp_if_both_not_none(iso1, iso2, 'ISO Hybrid', 'isohybrid_mbr', isohybrid_cmp)

# Check the boot records
if len(iso1.brs) != len(iso2.brs):
    print("ISO1 has %d boot records, ISO2 has %d" % (len(iso1.brs), len(iso2.brs)))
else:
    for index,br in enumerate(iso1.brs):
        br_cmp(iso1.brs[index], iso2.brs[index], 'Boot Record')

# Check the Volume Descriptor Set Terminators
if len(iso1.vdsts) != len(iso2.vdsts):
    print("ISO1 has %d VDSTs, ISO2 has %d" % (len(iso1.vdsts), len(iso2.vdsts)))
else:
    for index,vdst in enumerate(iso1.vdsts):
        vdst_cmp(iso1.vdsts[index], iso2.vdsts[index], 'VDST')

# FIXME: check Partition Volume Descriptors

# Check SVDs
if len(iso1.svds) != len(iso2.svds):
    print("ISO1 has %d SVDs, ISO2 has %d" % (len(iso1.svds), len(iso2.svds)))
else:
    for index,svd in enumerate(iso1.svds):
        svd_cmp(iso1.svds[index], iso2.svds[index], 'SVD')

# Check eltorito
cmp_if_both_not_none(iso1, iso2, 'El Torito', 'eltorito_boot_catalog', eltorito_cmp)

# Now check out the PVD directory record tree
dirs = collections.deque([iso1.pvd.root_directory_record()])
while dirs:
    dir_record1 = dirs.popleft()
    for child1 in dir_record1.children:
        full_iso_path = iso1.full_path_from_dirrecord(child1)
        #print "FULL ISO PATH: %s" % (full_iso_path)
        try:
            child2 = iso2.get_entry(full_iso_path)
        except pycdlib.PyCdlibException:
            print("Record %s exists on first, not second" % (full_iso_path))
            continue
        dir_record_cmp(child1, child2, child1.file_identifier())
        if child1.isdir:
            dirs.append(child1)

# Now checkout the SVD (Joliet) directory record tree
if iso1.joliet_vd is not None and iso2.joliet_vd is None:
    print("ISO1 has Joliet records, ISO2 does not")
elif iso1.joliet_vd is None and iso2.joliet_vd is not None:
    print("ISO1 has not Joliet records, ISO2 does")
elif iso1.joliet_vd is not None and iso2.joliet_vd is not None:
    joliet_dirs = collections.deque([(iso1.joliet_vd.root_directory_record(), iso2.joliet_vd.root_directory_record())])
    while joliet_dirs:
        jdir1,jdir2 = joliet_dirs.popleft()
        for index,child in enumerate(jdir1.children):
            child1 = jdir1.children[index]
            child2 = jdir2.children[index]
            dir_record_cmp(child1, child2, child1.file_identifier())
            if jdir1.isdir:
                joliet_dirs.append((child1, child2))

# Now compare the contents
dirs = collections.deque([(iso1.pvd.root_directory_record(), iso2.pvd.root_directory_record())])
while dirs:
    dir_record1,dir_record2 = dirs.popleft()
    for index,child in enumerate(dir_record1.children):
        child1 = dir_record1.children[index]
        child2 = dir_record2.children[index]
        if child1.file_identifier() == '.' and child2.file_identifier() == '.':
            continue
        if child1.file_identifier() == '..' and child2.file_identifier() == '..':
            continue
        if child1.isdir:
            dirs.append((child1, child2))
        else:
            content_cmp(child1, child2)

iso2.close()
iso1.close()

fp2.close()
fp1.close()
