srcanamdw/codescanner/pyinstaller/carchive.py
changeset 1 22878952f6e2
equal deleted inserted replaced
0:509e4801c378 1:22878952f6e2
       
     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 == 1:
       
   188       rslt = zlib.decompress(rslt)
       
   189     if typcd == 'M':
       
   190       return (1, rslt)
       
   191     return (0, rslt)
       
   192 
       
   193   def contents(self):
       
   194     """Return the names of the entries"""
       
   195     rslt = []
       
   196     for (dpos, dlen, ulen, flag, typcd, nm) in self.toc:
       
   197       rslt.append(nm)
       
   198     return rslt
       
   199 
       
   200   def add(self, entry):
       
   201     """Add an ENTRY to the CArchive.
       
   202 
       
   203        ENTRY must have:
       
   204          entry[0] is name (under which it will be saved).
       
   205          entry[1] is fullpathname of the file.
       
   206          entry[2] is a flag for it's storage format (0==uncompressed,
       
   207          1==compressed)
       
   208          entry[3] is the entry's type code.
       
   209          Version 5:
       
   210            If the type code is 'o':
       
   211              entry[0] is the runtime option
       
   212              eg: v  (meaning verbose imports)
       
   213                  u  (menaing unbuffered)
       
   214                  W arg (warning option arg)
       
   215                  s  (meaning do site.py processing."""
       
   216     (nm, pathnm, flag, typcd) = entry[:4]
       
   217     # version 5 - allow type 'o' = runtime option
       
   218     try:
       
   219       if typcd == 'o':
       
   220           s = ''
       
   221           flag = 0
       
   222       elif typcd == 's':
       
   223           # If it's a source code file, add \0 terminator as it will be
       
   224           # executed as-is by the bootloader.
       
   225           s = open(pathnm, 'r').read()
       
   226           s = s + '\n\0'
       
   227       else:
       
   228           s = open(pathnm, 'rb').read()
       
   229     except IOError:
       
   230       print "Cannot find ('%s', '%s', %s, '%s')" % (nm, pathnm, flag, typcd)
       
   231       raise
       
   232     ulen = len(s)
       
   233     if flag == 1:
       
   234       s = zlib.compress(s, self.LEVEL)
       
   235     dlen = len(s)
       
   236     where = self.lib.tell()
       
   237     if typcd == 'm':
       
   238       if find(pathnm, '.__init__.py') > -1:
       
   239         typcd = 'M'
       
   240     self.toc.add(where, dlen, ulen, flag, typcd, nm)
       
   241     self.lib.write(s)
       
   242 
       
   243   def save_toc(self, tocpos):
       
   244     """Save the table of contents to disk."""
       
   245     self.tocpos = tocpos
       
   246     tocstr = self.toc.tobinary()
       
   247     self.toclen = len(tocstr)
       
   248     self.lib.write(tocstr)
       
   249 
       
   250   def save_trailer(self, tocpos):
       
   251     """Save the trailer to disk.
       
   252 
       
   253        CArchives can be opened from the end - the trailer points
       
   254        back to the start. """
       
   255     totallen = tocpos + self.toclen + self.TRLLEN
       
   256     if hasattr(sys, "version_info"):
       
   257         pyvers = sys.version_info[0]*10 + sys.version_info[1]
       
   258     else:
       
   259         toks = split(sys.version, '.', 2)
       
   260         pyvers = int(toks[0])*10 + int(toks[1])
       
   261     trl = struct.pack(self.TRLSTRUCT, self.MAGIC, totallen,
       
   262                       tocpos, self.toclen, pyvers)
       
   263     self.lib.write(trl)
       
   264 
       
   265   def openEmbedded(self, name):
       
   266     """Open a CArchive of name NAME embedded within this CArchive."""
       
   267     ndx = self.toc.find(name)
       
   268     if ndx == -1:
       
   269       raise KeyError, "Member '%s' not found in %s" % (name, self.path)
       
   270     (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx)
       
   271     if flag:
       
   272       raise ValueError, "Cannot open compressed archive %s in place"
       
   273     return CArchive(self.path, self.pkgstart+dpos, dlen)