~jacksonc/python-packager/devel

« back to all changes in this revision

Viewing changes to Build-Server/pyinstaller/windows-2.6/carchive.py

  • Committer: Jackson Cooper
  • Date: 2010-04-13 03:14:06 UTC
  • Revision ID: jackson@jacksonc.com-20100413031406-415e68ruamsfc5se
Added pyinstaller for Windows

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Subclass of Archive that can be understood by a C program (see launch.c).
 
2
# Copyright (C) 2005, Giovanni Bajo
 
3
# Based on previous work under copyright (c) 1999, 2002 McMillan Enterprises, Inc.
 
4
#
 
5
# This program is free software; you can redistribute it and/or
 
6
# modify it under the terms of the GNU General Public License
 
7
# as published by the Free Software Foundation; either version 2
 
8
# of the License, or (at your option) any later version.
 
9
#
 
10
# This program is distributed in the hope that it will be useful,
 
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
# GNU General Public License for more details.
 
14
#
 
15
# You should have received a copy of the GNU General Public License
 
16
# along with this program; if not, write to the Free Software
 
17
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
18
import archive
 
19
import struct
 
20
try:
 
21
    import zlib
 
22
except ImportError:
 
23
    zlib = archive.DummyZlib()
 
24
import sys
 
25
if sys.version[0] == '1':
 
26
    import strop
 
27
    find = strop.find
 
28
    split = strop.split
 
29
else:
 
30
    def find(s, sub):
 
31
        return s.find(sub)
 
32
    def split(s, delim, count):
 
33
        return s.split(delim, count)
 
34
 
 
35
class CTOC:
 
36
    """A class encapsulating the table of contents of a CArchive.
 
37
 
 
38
       When written to disk, it is easily read from C."""
 
39
    ENTRYSTRUCT = '!iiiibc' #(structlen, dpos, dlen, ulen, flag, typcd) followed by name
 
40
    def __init__(self):
 
41
        self.data = []
 
42
 
 
43
    def frombinary(self, s):
 
44
        """Decode the binary string into an in memory list.
 
45
 
 
46
            S is a binary string."""
 
47
        entrylen = struct.calcsize(self.ENTRYSTRUCT)
 
48
        p = 0
 
49
        while p<len(s):
 
50
            (slen, dpos, dlen, ulen, flag, typcd) = struct.unpack(self.ENTRYSTRUCT,
 
51
                                                        s[p:p+entrylen])
 
52
            nmlen = slen - entrylen
 
53
            p = p + entrylen
 
54
            (nm,) = struct.unpack(`nmlen`+'s', s[p:p+nmlen])
 
55
            p = p + nmlen
 
56
            # version 4
 
57
            # self.data.append((dpos, dlen, ulen, flag, typcd, nm[:-1]))
 
58
            # version 5
 
59
            # nm may have up to 15 bytes of padding
 
60
            pos = find(nm, '\0')
 
61
            if pos < 0:
 
62
                self.data.append((dpos, dlen, ulen, flag, typcd, nm))
 
63
            else:
 
64
                self.data.append((dpos, dlen, ulen, flag, typcd, nm[:pos]))
 
65
            #end version 5
 
66
 
 
67
 
 
68
    def tobinary(self):
 
69
        """Return self as a binary string."""
 
70
        import string
 
71
        entrylen = struct.calcsize(self.ENTRYSTRUCT)
 
72
        rslt = []
 
73
        for (dpos, dlen, ulen, flag, typcd, nm) in self.data:
 
74
            nmlen = len(nm) + 1       # add 1 for a '\0'
 
75
            # version 4
 
76
            # rslt.append(struct.pack(self.ENTRYSTRUCT+`nmlen`+'s',
 
77
            #   nmlen+entrylen, dpos, dlen, ulen, flag, typcd, nm+'\0'))
 
78
            # version 5
 
79
            #   align to 16 byte boundary so xplatform C can read
 
80
            toclen = nmlen + entrylen
 
81
            if toclen % 16 == 0:
 
82
                pad = '\0'
 
83
            else:
 
84
                padlen = 16 - (toclen % 16)
 
85
                pad = '\0'*padlen
 
86
                nmlen = nmlen + padlen
 
87
            rslt.append(struct.pack(self.ENTRYSTRUCT+`nmlen`+'s',
 
88
                            nmlen+entrylen, dpos, dlen, ulen, flag, typcd, nm+pad))
 
89
            # end version 5
 
90
 
 
91
        return string.join(rslt, '')
 
92
 
 
93
    def add(self, dpos, dlen, ulen, flag, typcd, nm):
 
94
        """Add an entry to the table of contents.
 
95
 
 
96
           DPOS is data position.
 
97
           DLEN is data length.
 
98
           ULEN is the uncompressed data len.
 
99
           FLAG says if the data is compressed.
 
100
           TYPCD is the "type" of the entry (used by the C code)
 
101
           NM is the entry's name."""
 
102
        self.data.append((dpos, dlen, ulen, flag, typcd, nm))
 
103
 
 
104
    def get(self, ndx):
 
105
        """return the toc entry (tuple) at index NDX"""
 
106
        return self.data[ndx]
 
107
 
 
108
    def __getitem__(self, ndx):
 
109
        return self.data[ndx]
 
110
 
 
111
    def find(self, name):
 
112
        """Return the index of the toc entry with name NAME.
 
113
 
 
114
           Return -1 for failure."""
 
115
        for i in range(len(self.data)):
 
116
            if self.data[i][-1] == name:
 
117
                return i
 
118
        return -1
 
119
 
 
120
class CArchive(archive.Archive):
 
121
    """An Archive subclass that an hold arbitrary data.
 
122
 
 
123
       Easily handled from C or from Python."""
 
124
    MAGIC = 'MEI\014\013\012\013\016'
 
125
    HDRLEN = 0
 
126
    TOCTMPLT = CTOC
 
127
    TRLSTRUCT = '!8siiii'
 
128
    TRLLEN = 24
 
129
    LEVEL = 9
 
130
    def __init__(self, path=None, start=0, len=0):
 
131
        """Constructor.
 
132
 
 
133
           PATH is path name of file (create an empty CArchive if path is None).
 
134
           START is the seekposition within PATH.
 
135
           LEN is the length of the CArchive (if 0, then read till EOF). """
 
136
        self.len = len
 
137
        archive.Archive.__init__(self, path, start)
 
138
 
 
139
    def checkmagic(self):
 
140
        """Verify that self is a valid CArchive.
 
141
 
 
142
            Magic signature is at end of the archive."""
 
143
        #magic is at EOF; if we're embedded, we need to figure where that is
 
144
        if self.len:
 
145
            self.lib.seek(self.start+self.len, 0)
 
146
        else:
 
147
            self.lib.seek(0, 2)
 
148
        filelen = self.lib.tell()
 
149
        if self.len:
 
150
            self.lib.seek(self.start+self.len-self.TRLLEN, 0)
 
151
        else:
 
152
            self.lib.seek(-self.TRLLEN, 2)
 
153
        (magic, totallen, tocpos, toclen, pyvers) = struct.unpack(self.TRLSTRUCT,
 
154
                            self.lib.read(self.TRLLEN))
 
155
        if magic != self.MAGIC:
 
156
            raise RuntimeError, "%s is not a valid %s archive file" \
 
157
              % (self.path, self.__class__.__name__)
 
158
        self.pkgstart = filelen - totallen
 
159
        if self.len:
 
160
            if totallen != self.len or self.pkgstart != self.start:
 
161
                raise RuntimeError, "Problem with embedded archive in %s" % self.path
 
162
        self.tocpos, self.toclen = tocpos, toclen
 
163
 
 
164
    def loadtoc(self):
 
165
        """Load the table of contents into memory."""
 
166
        self.toc = self.TOCTMPLT()
 
167
        self.lib.seek(self.pkgstart+self.tocpos)
 
168
        tocstr = self.lib.read(self.toclen)
 
169
        self.toc.frombinary(tocstr)
 
170
 
 
171
    def extract(self, name):
 
172
        """Get the contents of an entry.
 
173
 
 
174
           NAME is an entry name.
 
175
           Return the tuple (ispkg, contents).
 
176
           For non-Python resoures, ispkg is meaningless (and 0).
 
177
           Used by the import mechanism."""
 
178
        if type(name) == type(''):
 
179
            ndx = self.toc.find(name)
 
180
            if ndx == -1:
 
181
                return None
 
182
        else:
 
183
            ndx = name
 
184
        (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx)
 
185
        self.lib.seek(self.pkgstart+dpos)
 
186
        rslt = self.lib.read(dlen)
 
187
        if flag == 2:
 
188
            global AES
 
189
            import AES
 
190
            key = rslt[:32]
 
191
            # Note: keep this in sync with bootloader's code
 
192
            rslt = AES.new(key, AES.MODE_CFB, "\0"*AES.block_size).decrypt(rslt[32:])
 
193
        if flag == 1 or flag == 2:
 
194
            rslt = zlib.decompress(rslt)
 
195
        if typcd == 'M':
 
196
            return (1, rslt)
 
197
        return (0, rslt)
 
198
 
 
199
    def contents(self):
 
200
        """Return the names of the entries"""
 
201
        rslt = []
 
202
        for (dpos, dlen, ulen, flag, typcd, nm) in self.toc:
 
203
            rslt.append(nm)
 
204
        return rslt
 
205
 
 
206
    def add(self, entry):
 
207
        """Add an ENTRY to the CArchive.
 
208
 
 
209
           ENTRY must have:
 
210
             entry[0] is name (under which it will be saved).
 
211
             entry[1] is fullpathname of the file.
 
212
             entry[2] is a flag for it's storage format (0==uncompressed,
 
213
             1==compressed)
 
214
             entry[3] is the entry's type code.
 
215
             Version 5:
 
216
               If the type code is 'o':
 
217
                 entry[0] is the runtime option
 
218
                 eg: v  (meaning verbose imports)
 
219
                     u  (menaing unbuffered)
 
220
                     W arg (warning option arg)
 
221
                     s  (meaning do site.py processing."""
 
222
        (nm, pathnm, flag, typcd) = entry[:4]
 
223
        # version 5 - allow type 'o' = runtime option
 
224
        try:
 
225
            if typcd == 'o':
 
226
                s = ''
 
227
                flag = 0
 
228
            elif typcd == 's':
 
229
                # If it's a source code file, add \0 terminator as it will be
 
230
                # executed as-is by the bootloader.
 
231
                s = open(pathnm, 'r').read()
 
232
                s = s.replace("\r\n", "\n")
 
233
                s = s + '\n\0'
 
234
            else:
 
235
                s = open(pathnm, 'rb').read()
 
236
        except IOError:
 
237
            print "Cannot find ('%s', '%s', %s, '%s')" % (nm, pathnm, flag, typcd)
 
238
            raise
 
239
        ulen = len(s)
 
240
        assert flag in range(3)
 
241
        if flag == 1 or flag == 2:
 
242
            s = zlib.compress(s, self.LEVEL)
 
243
        if flag == 2:
 
244
            global AES
 
245
            import AES, Crypt
 
246
            key = Crypt.gen_random_key(32)
 
247
            # Note: keep this in sync with bootloader's code
 
248
            s = key + AES.new(key, AES.MODE_CFB, "\0"*AES.block_size).encrypt(s)
 
249
        dlen = len(s)
 
250
        where = self.lib.tell()
 
251
        if typcd == 'm':
 
252
            if find(pathnm, '.__init__.py') > -1:
 
253
                typcd = 'M'
 
254
        self.toc.add(where, dlen, ulen, flag, typcd, nm)
 
255
        self.lib.write(s)
 
256
 
 
257
    def save_toc(self, tocpos):
 
258
        """Save the table of contents to disk."""
 
259
        self.tocpos = tocpos
 
260
        tocstr = self.toc.tobinary()
 
261
        self.toclen = len(tocstr)
 
262
        self.lib.write(tocstr)
 
263
 
 
264
    def save_trailer(self, tocpos):
 
265
        """Save the trailer to disk.
 
266
 
 
267
           CArchives can be opened from the end - the trailer points
 
268
           back to the start. """
 
269
        totallen = tocpos + self.toclen + self.TRLLEN
 
270
        if hasattr(sys, "version_info"):
 
271
            pyvers = sys.version_info[0]*10 + sys.version_info[1]
 
272
        else:
 
273
            toks = split(sys.version, '.', 2)
 
274
            pyvers = int(toks[0])*10 + int(toks[1])
 
275
        trl = struct.pack(self.TRLSTRUCT, self.MAGIC, totallen,
 
276
                          tocpos, self.toclen, pyvers)
 
277
        self.lib.write(trl)
 
278
 
 
279
    def openEmbedded(self, name):
 
280
        """Open a CArchive of name NAME embedded within this CArchive."""
 
281
        ndx = self.toc.find(name)
 
282
        if ndx == -1:
 
283
            raise KeyError, "Member '%s' not found in %s" % (name, self.path)
 
284
        (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx)
 
285
        if flag:
 
286
            raise ValueError, "Cannot open compressed archive %s in place"
 
287
        return CArchive(self.path, self.pkgstart+dpos, dlen)