17db96d56Sopenharmony_ci# Copyright (C) 2005 Martin v. Löwis
27db96d56Sopenharmony_ci# Licensed to PSF under a Contributor Agreement.
37db96d56Sopenharmony_cifrom _msi import *
47db96d56Sopenharmony_ciimport fnmatch
57db96d56Sopenharmony_ciimport os
67db96d56Sopenharmony_ciimport re
77db96d56Sopenharmony_ciimport string
87db96d56Sopenharmony_ciimport sys
97db96d56Sopenharmony_ciimport warnings
107db96d56Sopenharmony_ci
117db96d56Sopenharmony_ciwarnings._deprecated(__name__, remove=(3, 13))
127db96d56Sopenharmony_ci
137db96d56Sopenharmony_ciAMD64 = "AMD64" in sys.version
147db96d56Sopenharmony_ci# Keep msilib.Win64 around to preserve backwards compatibility.
157db96d56Sopenharmony_ciWin64 = AMD64
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_ci# Partially taken from Wine
187db96d56Sopenharmony_cidatasizemask=      0x00ff
197db96d56Sopenharmony_citype_valid=        0x0100
207db96d56Sopenharmony_citype_localizable=  0x0200
217db96d56Sopenharmony_ci
227db96d56Sopenharmony_citypemask=          0x0c00
237db96d56Sopenharmony_citype_long=         0x0000
247db96d56Sopenharmony_citype_short=        0x0400
257db96d56Sopenharmony_citype_string=       0x0c00
267db96d56Sopenharmony_citype_binary=       0x0800
277db96d56Sopenharmony_ci
287db96d56Sopenharmony_citype_nullable=     0x1000
297db96d56Sopenharmony_citype_key=          0x2000
307db96d56Sopenharmony_ci# XXX temporary, localizable?
317db96d56Sopenharmony_ciknownbits = datasizemask | type_valid | type_localizable | \
327db96d56Sopenharmony_ci            typemask | type_nullable | type_key
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ciclass Table:
357db96d56Sopenharmony_ci    def __init__(self, name):
367db96d56Sopenharmony_ci        self.name = name
377db96d56Sopenharmony_ci        self.fields = []
387db96d56Sopenharmony_ci
397db96d56Sopenharmony_ci    def add_field(self, index, name, type):
407db96d56Sopenharmony_ci        self.fields.append((index,name,type))
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ci    def sql(self):
437db96d56Sopenharmony_ci        fields = []
447db96d56Sopenharmony_ci        keys = []
457db96d56Sopenharmony_ci        self.fields.sort()
467db96d56Sopenharmony_ci        fields = [None]*len(self.fields)
477db96d56Sopenharmony_ci        for index, name, type in self.fields:
487db96d56Sopenharmony_ci            index -= 1
497db96d56Sopenharmony_ci            unk = type & ~knownbits
507db96d56Sopenharmony_ci            if unk:
517db96d56Sopenharmony_ci                print("%s.%s unknown bits %x" % (self.name, name, unk))
527db96d56Sopenharmony_ci            size = type & datasizemask
537db96d56Sopenharmony_ci            dtype = type & typemask
547db96d56Sopenharmony_ci            if dtype == type_string:
557db96d56Sopenharmony_ci                if size:
567db96d56Sopenharmony_ci                    tname="CHAR(%d)" % size
577db96d56Sopenharmony_ci                else:
587db96d56Sopenharmony_ci                    tname="CHAR"
597db96d56Sopenharmony_ci            elif dtype == type_short:
607db96d56Sopenharmony_ci                assert size==2
617db96d56Sopenharmony_ci                tname = "SHORT"
627db96d56Sopenharmony_ci            elif dtype == type_long:
637db96d56Sopenharmony_ci                assert size==4
647db96d56Sopenharmony_ci                tname="LONG"
657db96d56Sopenharmony_ci            elif dtype == type_binary:
667db96d56Sopenharmony_ci                assert size==0
677db96d56Sopenharmony_ci                tname="OBJECT"
687db96d56Sopenharmony_ci            else:
697db96d56Sopenharmony_ci                tname="unknown"
707db96d56Sopenharmony_ci                print("%s.%sunknown integer type %d" % (self.name, name, size))
717db96d56Sopenharmony_ci            if type & type_nullable:
727db96d56Sopenharmony_ci                flags = ""
737db96d56Sopenharmony_ci            else:
747db96d56Sopenharmony_ci                flags = " NOT NULL"
757db96d56Sopenharmony_ci            if type & type_localizable:
767db96d56Sopenharmony_ci                flags += " LOCALIZABLE"
777db96d56Sopenharmony_ci            fields[index] = "`%s` %s%s" % (name, tname, flags)
787db96d56Sopenharmony_ci            if type & type_key:
797db96d56Sopenharmony_ci                keys.append("`%s`" % name)
807db96d56Sopenharmony_ci        fields = ", ".join(fields)
817db96d56Sopenharmony_ci        keys = ", ".join(keys)
827db96d56Sopenharmony_ci        return "CREATE TABLE %s (%s PRIMARY KEY %s)" % (self.name, fields, keys)
837db96d56Sopenharmony_ci
847db96d56Sopenharmony_ci    def create(self, db):
857db96d56Sopenharmony_ci        v = db.OpenView(self.sql())
867db96d56Sopenharmony_ci        v.Execute(None)
877db96d56Sopenharmony_ci        v.Close()
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ciclass _Unspecified:pass
907db96d56Sopenharmony_cidef change_sequence(seq, action, seqno=_Unspecified, cond = _Unspecified):
917db96d56Sopenharmony_ci    "Change the sequence number of an action in a sequence list"
927db96d56Sopenharmony_ci    for i in range(len(seq)):
937db96d56Sopenharmony_ci        if seq[i][0] == action:
947db96d56Sopenharmony_ci            if cond is _Unspecified:
957db96d56Sopenharmony_ci                cond = seq[i][1]
967db96d56Sopenharmony_ci            if seqno is _Unspecified:
977db96d56Sopenharmony_ci                seqno = seq[i][2]
987db96d56Sopenharmony_ci            seq[i] = (action, cond, seqno)
997db96d56Sopenharmony_ci            return
1007db96d56Sopenharmony_ci    raise ValueError("Action not found in sequence")
1017db96d56Sopenharmony_ci
1027db96d56Sopenharmony_cidef add_data(db, table, values):
1037db96d56Sopenharmony_ci    v = db.OpenView("SELECT * FROM `%s`" % table)
1047db96d56Sopenharmony_ci    count = v.GetColumnInfo(MSICOLINFO_NAMES).GetFieldCount()
1057db96d56Sopenharmony_ci    r = CreateRecord(count)
1067db96d56Sopenharmony_ci    for value in values:
1077db96d56Sopenharmony_ci        assert len(value) == count, value
1087db96d56Sopenharmony_ci        for i in range(count):
1097db96d56Sopenharmony_ci            field = value[i]
1107db96d56Sopenharmony_ci            if isinstance(field, int):
1117db96d56Sopenharmony_ci                r.SetInteger(i+1,field)
1127db96d56Sopenharmony_ci            elif isinstance(field, str):
1137db96d56Sopenharmony_ci                r.SetString(i+1,field)
1147db96d56Sopenharmony_ci            elif field is None:
1157db96d56Sopenharmony_ci                pass
1167db96d56Sopenharmony_ci            elif isinstance(field, Binary):
1177db96d56Sopenharmony_ci                r.SetStream(i+1, field.name)
1187db96d56Sopenharmony_ci            else:
1197db96d56Sopenharmony_ci                raise TypeError("Unsupported type %s" % field.__class__.__name__)
1207db96d56Sopenharmony_ci        try:
1217db96d56Sopenharmony_ci            v.Modify(MSIMODIFY_INSERT, r)
1227db96d56Sopenharmony_ci        except Exception:
1237db96d56Sopenharmony_ci            raise MSIError("Could not insert "+repr(values)+" into "+table)
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_ci        r.ClearData()
1267db96d56Sopenharmony_ci    v.Close()
1277db96d56Sopenharmony_ci
1287db96d56Sopenharmony_ci
1297db96d56Sopenharmony_cidef add_stream(db, name, path):
1307db96d56Sopenharmony_ci    v = db.OpenView("INSERT INTO _Streams (Name, Data) VALUES ('%s', ?)" % name)
1317db96d56Sopenharmony_ci    r = CreateRecord(1)
1327db96d56Sopenharmony_ci    r.SetStream(1, path)
1337db96d56Sopenharmony_ci    v.Execute(r)
1347db96d56Sopenharmony_ci    v.Close()
1357db96d56Sopenharmony_ci
1367db96d56Sopenharmony_cidef init_database(name, schema,
1377db96d56Sopenharmony_ci                  ProductName, ProductCode, ProductVersion,
1387db96d56Sopenharmony_ci                  Manufacturer):
1397db96d56Sopenharmony_ci    try:
1407db96d56Sopenharmony_ci        os.unlink(name)
1417db96d56Sopenharmony_ci    except OSError:
1427db96d56Sopenharmony_ci        pass
1437db96d56Sopenharmony_ci    ProductCode = ProductCode.upper()
1447db96d56Sopenharmony_ci    # Create the database
1457db96d56Sopenharmony_ci    db = OpenDatabase(name, MSIDBOPEN_CREATE)
1467db96d56Sopenharmony_ci    # Create the tables
1477db96d56Sopenharmony_ci    for t in schema.tables:
1487db96d56Sopenharmony_ci        t.create(db)
1497db96d56Sopenharmony_ci    # Fill the validation table
1507db96d56Sopenharmony_ci    add_data(db, "_Validation", schema._Validation_records)
1517db96d56Sopenharmony_ci    # Initialize the summary information, allowing atmost 20 properties
1527db96d56Sopenharmony_ci    si = db.GetSummaryInformation(20)
1537db96d56Sopenharmony_ci    si.SetProperty(PID_TITLE, "Installation Database")
1547db96d56Sopenharmony_ci    si.SetProperty(PID_SUBJECT, ProductName)
1557db96d56Sopenharmony_ci    si.SetProperty(PID_AUTHOR, Manufacturer)
1567db96d56Sopenharmony_ci    if AMD64:
1577db96d56Sopenharmony_ci        si.SetProperty(PID_TEMPLATE, "x64;1033")
1587db96d56Sopenharmony_ci    else:
1597db96d56Sopenharmony_ci        si.SetProperty(PID_TEMPLATE, "Intel;1033")
1607db96d56Sopenharmony_ci    si.SetProperty(PID_REVNUMBER, gen_uuid())
1617db96d56Sopenharmony_ci    si.SetProperty(PID_WORDCOUNT, 2) # long file names, compressed, original media
1627db96d56Sopenharmony_ci    si.SetProperty(PID_PAGECOUNT, 200)
1637db96d56Sopenharmony_ci    si.SetProperty(PID_APPNAME, "Python MSI Library")
1647db96d56Sopenharmony_ci    # XXX more properties
1657db96d56Sopenharmony_ci    si.Persist()
1667db96d56Sopenharmony_ci    add_data(db, "Property", [
1677db96d56Sopenharmony_ci        ("ProductName", ProductName),
1687db96d56Sopenharmony_ci        ("ProductCode", ProductCode),
1697db96d56Sopenharmony_ci        ("ProductVersion", ProductVersion),
1707db96d56Sopenharmony_ci        ("Manufacturer", Manufacturer),
1717db96d56Sopenharmony_ci        ("ProductLanguage", "1033")])
1727db96d56Sopenharmony_ci    db.Commit()
1737db96d56Sopenharmony_ci    return db
1747db96d56Sopenharmony_ci
1757db96d56Sopenharmony_cidef add_tables(db, module):
1767db96d56Sopenharmony_ci    for table in module.tables:
1777db96d56Sopenharmony_ci        add_data(db, table, getattr(module, table))
1787db96d56Sopenharmony_ci
1797db96d56Sopenharmony_cidef make_id(str):
1807db96d56Sopenharmony_ci    identifier_chars = string.ascii_letters + string.digits + "._"
1817db96d56Sopenharmony_ci    str = "".join([c if c in identifier_chars else "_" for c in str])
1827db96d56Sopenharmony_ci    if str[0] in (string.digits + "."):
1837db96d56Sopenharmony_ci        str = "_" + str
1847db96d56Sopenharmony_ci    assert re.match("^[A-Za-z_][A-Za-z0-9_.]*$", str), "FILE"+str
1857db96d56Sopenharmony_ci    return str
1867db96d56Sopenharmony_ci
1877db96d56Sopenharmony_cidef gen_uuid():
1887db96d56Sopenharmony_ci    return "{"+UuidCreate().upper()+"}"
1897db96d56Sopenharmony_ci
1907db96d56Sopenharmony_ciclass CAB:
1917db96d56Sopenharmony_ci    def __init__(self, name):
1927db96d56Sopenharmony_ci        self.name = name
1937db96d56Sopenharmony_ci        self.files = []
1947db96d56Sopenharmony_ci        self.filenames = set()
1957db96d56Sopenharmony_ci        self.index = 0
1967db96d56Sopenharmony_ci
1977db96d56Sopenharmony_ci    def gen_id(self, file):
1987db96d56Sopenharmony_ci        logical = _logical = make_id(file)
1997db96d56Sopenharmony_ci        pos = 1
2007db96d56Sopenharmony_ci        while logical in self.filenames:
2017db96d56Sopenharmony_ci            logical = "%s.%d" % (_logical, pos)
2027db96d56Sopenharmony_ci            pos += 1
2037db96d56Sopenharmony_ci        self.filenames.add(logical)
2047db96d56Sopenharmony_ci        return logical
2057db96d56Sopenharmony_ci
2067db96d56Sopenharmony_ci    def append(self, full, file, logical):
2077db96d56Sopenharmony_ci        if os.path.isdir(full):
2087db96d56Sopenharmony_ci            return
2097db96d56Sopenharmony_ci        if not logical:
2107db96d56Sopenharmony_ci            logical = self.gen_id(file)
2117db96d56Sopenharmony_ci        self.index += 1
2127db96d56Sopenharmony_ci        self.files.append((full, logical))
2137db96d56Sopenharmony_ci        return self.index, logical
2147db96d56Sopenharmony_ci
2157db96d56Sopenharmony_ci    def commit(self, db):
2167db96d56Sopenharmony_ci        from tempfile import mktemp
2177db96d56Sopenharmony_ci        filename = mktemp()
2187db96d56Sopenharmony_ci        FCICreate(filename, self.files)
2197db96d56Sopenharmony_ci        add_data(db, "Media",
2207db96d56Sopenharmony_ci                [(1, self.index, None, "#"+self.name, None, None)])
2217db96d56Sopenharmony_ci        add_stream(db, self.name, filename)
2227db96d56Sopenharmony_ci        os.unlink(filename)
2237db96d56Sopenharmony_ci        db.Commit()
2247db96d56Sopenharmony_ci
2257db96d56Sopenharmony_ci_directories = set()
2267db96d56Sopenharmony_ciclass Directory:
2277db96d56Sopenharmony_ci    def __init__(self, db, cab, basedir, physical, _logical, default, componentflags=None):
2287db96d56Sopenharmony_ci        """Create a new directory in the Directory table. There is a current component
2297db96d56Sopenharmony_ci        at each point in time for the directory, which is either explicitly created
2307db96d56Sopenharmony_ci        through start_component, or implicitly when files are added for the first
2317db96d56Sopenharmony_ci        time. Files are added into the current component, and into the cab file.
2327db96d56Sopenharmony_ci        To create a directory, a base directory object needs to be specified (can be
2337db96d56Sopenharmony_ci        None), the path to the physical directory, and a logical directory name.
2347db96d56Sopenharmony_ci        Default specifies the DefaultDir slot in the directory table. componentflags
2357db96d56Sopenharmony_ci        specifies the default flags that new components get."""
2367db96d56Sopenharmony_ci        index = 1
2377db96d56Sopenharmony_ci        _logical = make_id(_logical)
2387db96d56Sopenharmony_ci        logical = _logical
2397db96d56Sopenharmony_ci        while logical in _directories:
2407db96d56Sopenharmony_ci            logical = "%s%d" % (_logical, index)
2417db96d56Sopenharmony_ci            index += 1
2427db96d56Sopenharmony_ci        _directories.add(logical)
2437db96d56Sopenharmony_ci        self.db = db
2447db96d56Sopenharmony_ci        self.cab = cab
2457db96d56Sopenharmony_ci        self.basedir = basedir
2467db96d56Sopenharmony_ci        self.physical = physical
2477db96d56Sopenharmony_ci        self.logical = logical
2487db96d56Sopenharmony_ci        self.component = None
2497db96d56Sopenharmony_ci        self.short_names = set()
2507db96d56Sopenharmony_ci        self.ids = set()
2517db96d56Sopenharmony_ci        self.keyfiles = {}
2527db96d56Sopenharmony_ci        self.componentflags = componentflags
2537db96d56Sopenharmony_ci        if basedir:
2547db96d56Sopenharmony_ci            self.absolute = os.path.join(basedir.absolute, physical)
2557db96d56Sopenharmony_ci            blogical = basedir.logical
2567db96d56Sopenharmony_ci        else:
2577db96d56Sopenharmony_ci            self.absolute = physical
2587db96d56Sopenharmony_ci            blogical = None
2597db96d56Sopenharmony_ci        add_data(db, "Directory", [(logical, blogical, default)])
2607db96d56Sopenharmony_ci
2617db96d56Sopenharmony_ci    def start_component(self, component = None, feature = None, flags = None, keyfile = None, uuid=None):
2627db96d56Sopenharmony_ci        """Add an entry to the Component table, and make this component the current for this
2637db96d56Sopenharmony_ci        directory. If no component name is given, the directory name is used. If no feature
2647db96d56Sopenharmony_ci        is given, the current feature is used. If no flags are given, the directory's default
2657db96d56Sopenharmony_ci        flags are used. If no keyfile is given, the KeyPath is left null in the Component
2667db96d56Sopenharmony_ci        table."""
2677db96d56Sopenharmony_ci        if flags is None:
2687db96d56Sopenharmony_ci            flags = self.componentflags
2697db96d56Sopenharmony_ci        if uuid is None:
2707db96d56Sopenharmony_ci            uuid = gen_uuid()
2717db96d56Sopenharmony_ci        else:
2727db96d56Sopenharmony_ci            uuid = uuid.upper()
2737db96d56Sopenharmony_ci        if component is None:
2747db96d56Sopenharmony_ci            component = self.logical
2757db96d56Sopenharmony_ci        self.component = component
2767db96d56Sopenharmony_ci        if AMD64:
2777db96d56Sopenharmony_ci            flags |= 256
2787db96d56Sopenharmony_ci        if keyfile:
2797db96d56Sopenharmony_ci            keyid = self.cab.gen_id(keyfile)
2807db96d56Sopenharmony_ci            self.keyfiles[keyfile] = keyid
2817db96d56Sopenharmony_ci        else:
2827db96d56Sopenharmony_ci            keyid = None
2837db96d56Sopenharmony_ci        add_data(self.db, "Component",
2847db96d56Sopenharmony_ci                        [(component, uuid, self.logical, flags, None, keyid)])
2857db96d56Sopenharmony_ci        if feature is None:
2867db96d56Sopenharmony_ci            feature = current_feature
2877db96d56Sopenharmony_ci        add_data(self.db, "FeatureComponents",
2887db96d56Sopenharmony_ci                        [(feature.id, component)])
2897db96d56Sopenharmony_ci
2907db96d56Sopenharmony_ci    def make_short(self, file):
2917db96d56Sopenharmony_ci        oldfile = file
2927db96d56Sopenharmony_ci        file = file.replace('+', '_')
2937db96d56Sopenharmony_ci        file = ''.join(c for c in file if not c in r' "/\[]:;=,')
2947db96d56Sopenharmony_ci        parts = file.split(".")
2957db96d56Sopenharmony_ci        if len(parts) > 1:
2967db96d56Sopenharmony_ci            prefix = "".join(parts[:-1]).upper()
2977db96d56Sopenharmony_ci            suffix = parts[-1].upper()
2987db96d56Sopenharmony_ci            if not prefix:
2997db96d56Sopenharmony_ci                prefix = suffix
3007db96d56Sopenharmony_ci                suffix = None
3017db96d56Sopenharmony_ci        else:
3027db96d56Sopenharmony_ci            prefix = file.upper()
3037db96d56Sopenharmony_ci            suffix = None
3047db96d56Sopenharmony_ci        if len(parts) < 3 and len(prefix) <= 8 and file == oldfile and (
3057db96d56Sopenharmony_ci                                                not suffix or len(suffix) <= 3):
3067db96d56Sopenharmony_ci            if suffix:
3077db96d56Sopenharmony_ci                file = prefix+"."+suffix
3087db96d56Sopenharmony_ci            else:
3097db96d56Sopenharmony_ci                file = prefix
3107db96d56Sopenharmony_ci        else:
3117db96d56Sopenharmony_ci            file = None
3127db96d56Sopenharmony_ci        if file is None or file in self.short_names:
3137db96d56Sopenharmony_ci            prefix = prefix[:6]
3147db96d56Sopenharmony_ci            if suffix:
3157db96d56Sopenharmony_ci                suffix = suffix[:3]
3167db96d56Sopenharmony_ci            pos = 1
3177db96d56Sopenharmony_ci            while 1:
3187db96d56Sopenharmony_ci                if suffix:
3197db96d56Sopenharmony_ci                    file = "%s~%d.%s" % (prefix, pos, suffix)
3207db96d56Sopenharmony_ci                else:
3217db96d56Sopenharmony_ci                    file = "%s~%d" % (prefix, pos)
3227db96d56Sopenharmony_ci                if file not in self.short_names: break
3237db96d56Sopenharmony_ci                pos += 1
3247db96d56Sopenharmony_ci                assert pos < 10000
3257db96d56Sopenharmony_ci                if pos in (10, 100, 1000):
3267db96d56Sopenharmony_ci                    prefix = prefix[:-1]
3277db96d56Sopenharmony_ci        self.short_names.add(file)
3287db96d56Sopenharmony_ci        assert not re.search(r'[\?|><:/*"+,;=\[\]]', file) # restrictions on short names
3297db96d56Sopenharmony_ci        return file
3307db96d56Sopenharmony_ci
3317db96d56Sopenharmony_ci    def add_file(self, file, src=None, version=None, language=None):
3327db96d56Sopenharmony_ci        """Add a file to the current component of the directory, starting a new one
3337db96d56Sopenharmony_ci        if there is no current component. By default, the file name in the source
3347db96d56Sopenharmony_ci        and the file table will be identical. If the src file is specified, it is
3357db96d56Sopenharmony_ci        interpreted relative to the current directory. Optionally, a version and a
3367db96d56Sopenharmony_ci        language can be specified for the entry in the File table."""
3377db96d56Sopenharmony_ci        if not self.component:
3387db96d56Sopenharmony_ci            self.start_component(self.logical, current_feature, 0)
3397db96d56Sopenharmony_ci        if not src:
3407db96d56Sopenharmony_ci            # Allow relative paths for file if src is not specified
3417db96d56Sopenharmony_ci            src = file
3427db96d56Sopenharmony_ci            file = os.path.basename(file)
3437db96d56Sopenharmony_ci        absolute = os.path.join(self.absolute, src)
3447db96d56Sopenharmony_ci        assert not re.search(r'[\?|><:/*]"', file) # restrictions on long names
3457db96d56Sopenharmony_ci        if file in self.keyfiles:
3467db96d56Sopenharmony_ci            logical = self.keyfiles[file]
3477db96d56Sopenharmony_ci        else:
3487db96d56Sopenharmony_ci            logical = None
3497db96d56Sopenharmony_ci        sequence, logical = self.cab.append(absolute, file, logical)
3507db96d56Sopenharmony_ci        assert logical not in self.ids
3517db96d56Sopenharmony_ci        self.ids.add(logical)
3527db96d56Sopenharmony_ci        short = self.make_short(file)
3537db96d56Sopenharmony_ci        full = "%s|%s" % (short, file)
3547db96d56Sopenharmony_ci        filesize = os.stat(absolute).st_size
3557db96d56Sopenharmony_ci        # constants.msidbFileAttributesVital
3567db96d56Sopenharmony_ci        # Compressed omitted, since it is the database default
3577db96d56Sopenharmony_ci        # could add r/o, system, hidden
3587db96d56Sopenharmony_ci        attributes = 512
3597db96d56Sopenharmony_ci        add_data(self.db, "File",
3607db96d56Sopenharmony_ci                        [(logical, self.component, full, filesize, version,
3617db96d56Sopenharmony_ci                         language, attributes, sequence)])
3627db96d56Sopenharmony_ci        #if not version:
3637db96d56Sopenharmony_ci        #    # Add hash if the file is not versioned
3647db96d56Sopenharmony_ci        #    filehash = FileHash(absolute, 0)
3657db96d56Sopenharmony_ci        #    add_data(self.db, "MsiFileHash",
3667db96d56Sopenharmony_ci        #             [(logical, 0, filehash.IntegerData(1),
3677db96d56Sopenharmony_ci        #               filehash.IntegerData(2), filehash.IntegerData(3),
3687db96d56Sopenharmony_ci        #               filehash.IntegerData(4))])
3697db96d56Sopenharmony_ci        # Automatically remove .pyc files on uninstall (2)
3707db96d56Sopenharmony_ci        # XXX: adding so many RemoveFile entries makes installer unbelievably
3717db96d56Sopenharmony_ci        # slow. So instead, we have to use wildcard remove entries
3727db96d56Sopenharmony_ci        if file.endswith(".py"):
3737db96d56Sopenharmony_ci            add_data(self.db, "RemoveFile",
3747db96d56Sopenharmony_ci                      [(logical+"c", self.component, "%sC|%sc" % (short, file),
3757db96d56Sopenharmony_ci                        self.logical, 2),
3767db96d56Sopenharmony_ci                       (logical+"o", self.component, "%sO|%so" % (short, file),
3777db96d56Sopenharmony_ci                        self.logical, 2)])
3787db96d56Sopenharmony_ci        return logical
3797db96d56Sopenharmony_ci
3807db96d56Sopenharmony_ci    def glob(self, pattern, exclude = None):
3817db96d56Sopenharmony_ci        """Add a list of files to the current component as specified in the
3827db96d56Sopenharmony_ci        glob pattern. Individual files can be excluded in the exclude list."""
3837db96d56Sopenharmony_ci        try:
3847db96d56Sopenharmony_ci            files = os.listdir(self.absolute)
3857db96d56Sopenharmony_ci        except OSError:
3867db96d56Sopenharmony_ci            return []
3877db96d56Sopenharmony_ci        if pattern[:1] != '.':
3887db96d56Sopenharmony_ci            files = (f for f in files if f[0] != '.')
3897db96d56Sopenharmony_ci        files = fnmatch.filter(files, pattern)
3907db96d56Sopenharmony_ci        for f in files:
3917db96d56Sopenharmony_ci            if exclude and f in exclude: continue
3927db96d56Sopenharmony_ci            self.add_file(f)
3937db96d56Sopenharmony_ci        return files
3947db96d56Sopenharmony_ci
3957db96d56Sopenharmony_ci    def remove_pyc(self):
3967db96d56Sopenharmony_ci        "Remove .pyc files on uninstall"
3977db96d56Sopenharmony_ci        add_data(self.db, "RemoveFile",
3987db96d56Sopenharmony_ci                 [(self.component+"c", self.component, "*.pyc", self.logical, 2)])
3997db96d56Sopenharmony_ci
4007db96d56Sopenharmony_ciclass Binary:
4017db96d56Sopenharmony_ci    def __init__(self, fname):
4027db96d56Sopenharmony_ci        self.name = fname
4037db96d56Sopenharmony_ci    def __repr__(self):
4047db96d56Sopenharmony_ci        return 'msilib.Binary(os.path.join(dirname,"%s"))' % self.name
4057db96d56Sopenharmony_ci
4067db96d56Sopenharmony_ciclass Feature:
4077db96d56Sopenharmony_ci    def __init__(self, db, id, title, desc, display, level = 1,
4087db96d56Sopenharmony_ci                 parent=None, directory = None, attributes=0):
4097db96d56Sopenharmony_ci        self.id = id
4107db96d56Sopenharmony_ci        if parent:
4117db96d56Sopenharmony_ci            parent = parent.id
4127db96d56Sopenharmony_ci        add_data(db, "Feature",
4137db96d56Sopenharmony_ci                        [(id, parent, title, desc, display,
4147db96d56Sopenharmony_ci                          level, directory, attributes)])
4157db96d56Sopenharmony_ci    def set_current(self):
4167db96d56Sopenharmony_ci        global current_feature
4177db96d56Sopenharmony_ci        current_feature = self
4187db96d56Sopenharmony_ci
4197db96d56Sopenharmony_ciclass Control:
4207db96d56Sopenharmony_ci    def __init__(self, dlg, name):
4217db96d56Sopenharmony_ci        self.dlg = dlg
4227db96d56Sopenharmony_ci        self.name = name
4237db96d56Sopenharmony_ci
4247db96d56Sopenharmony_ci    def event(self, event, argument, condition = "1", ordering = None):
4257db96d56Sopenharmony_ci        add_data(self.dlg.db, "ControlEvent",
4267db96d56Sopenharmony_ci                 [(self.dlg.name, self.name, event, argument,
4277db96d56Sopenharmony_ci                   condition, ordering)])
4287db96d56Sopenharmony_ci
4297db96d56Sopenharmony_ci    def mapping(self, event, attribute):
4307db96d56Sopenharmony_ci        add_data(self.dlg.db, "EventMapping",
4317db96d56Sopenharmony_ci                 [(self.dlg.name, self.name, event, attribute)])
4327db96d56Sopenharmony_ci
4337db96d56Sopenharmony_ci    def condition(self, action, condition):
4347db96d56Sopenharmony_ci        add_data(self.dlg.db, "ControlCondition",
4357db96d56Sopenharmony_ci                 [(self.dlg.name, self.name, action, condition)])
4367db96d56Sopenharmony_ci
4377db96d56Sopenharmony_ciclass RadioButtonGroup(Control):
4387db96d56Sopenharmony_ci    def __init__(self, dlg, name, property):
4397db96d56Sopenharmony_ci        self.dlg = dlg
4407db96d56Sopenharmony_ci        self.name = name
4417db96d56Sopenharmony_ci        self.property = property
4427db96d56Sopenharmony_ci        self.index = 1
4437db96d56Sopenharmony_ci
4447db96d56Sopenharmony_ci    def add(self, name, x, y, w, h, text, value = None):
4457db96d56Sopenharmony_ci        if value is None:
4467db96d56Sopenharmony_ci            value = name
4477db96d56Sopenharmony_ci        add_data(self.dlg.db, "RadioButton",
4487db96d56Sopenharmony_ci                 [(self.property, self.index, value,
4497db96d56Sopenharmony_ci                   x, y, w, h, text, None)])
4507db96d56Sopenharmony_ci        self.index += 1
4517db96d56Sopenharmony_ci
4527db96d56Sopenharmony_ciclass Dialog:
4537db96d56Sopenharmony_ci    def __init__(self, db, name, x, y, w, h, attr, title, first, default, cancel):
4547db96d56Sopenharmony_ci        self.db = db
4557db96d56Sopenharmony_ci        self.name = name
4567db96d56Sopenharmony_ci        self.x, self.y, self.w, self.h = x,y,w,h
4577db96d56Sopenharmony_ci        add_data(db, "Dialog", [(name, x,y,w,h,attr,title,first,default,cancel)])
4587db96d56Sopenharmony_ci
4597db96d56Sopenharmony_ci    def control(self, name, type, x, y, w, h, attr, prop, text, next, help):
4607db96d56Sopenharmony_ci        add_data(self.db, "Control",
4617db96d56Sopenharmony_ci                 [(self.name, name, type, x, y, w, h, attr, prop, text, next, help)])
4627db96d56Sopenharmony_ci        return Control(self, name)
4637db96d56Sopenharmony_ci
4647db96d56Sopenharmony_ci    def text(self, name, x, y, w, h, attr, text):
4657db96d56Sopenharmony_ci        return self.control(name, "Text", x, y, w, h, attr, None,
4667db96d56Sopenharmony_ci                     text, None, None)
4677db96d56Sopenharmony_ci
4687db96d56Sopenharmony_ci    def bitmap(self, name, x, y, w, h, text):
4697db96d56Sopenharmony_ci        return self.control(name, "Bitmap", x, y, w, h, 1, None, text, None, None)
4707db96d56Sopenharmony_ci
4717db96d56Sopenharmony_ci    def line(self, name, x, y, w, h):
4727db96d56Sopenharmony_ci        return self.control(name, "Line", x, y, w, h, 1, None, None, None, None)
4737db96d56Sopenharmony_ci
4747db96d56Sopenharmony_ci    def pushbutton(self, name, x, y, w, h, attr, text, next):
4757db96d56Sopenharmony_ci        return self.control(name, "PushButton", x, y, w, h, attr, None, text, next, None)
4767db96d56Sopenharmony_ci
4777db96d56Sopenharmony_ci    def radiogroup(self, name, x, y, w, h, attr, prop, text, next):
4787db96d56Sopenharmony_ci        add_data(self.db, "Control",
4797db96d56Sopenharmony_ci                 [(self.name, name, "RadioButtonGroup",
4807db96d56Sopenharmony_ci                   x, y, w, h, attr, prop, text, next, None)])
4817db96d56Sopenharmony_ci        return RadioButtonGroup(self, name, prop)
4827db96d56Sopenharmony_ci
4837db96d56Sopenharmony_ci    def checkbox(self, name, x, y, w, h, attr, prop, text, next):
4847db96d56Sopenharmony_ci        return self.control(name, "CheckBox", x, y, w, h, attr, prop, text, next, None)
485