1"""codefragments.py -- wrapper to modify code fragments."""
2
3# (c) 1998, Just van Rossum, Letterror
4
5__version__ = "0.8b3"
6__author__ = "jvr"
7
8import warnings
9warnings.warnpy3k("the cfmfile module is deprecated and is removed in 3,0",
10              stacklevel=2)
11
12import Carbon.File
13import struct
14from Carbon import Res
15import os
16import sys
17
18DEBUG = 0
19
20error = "cfm.error"
21
22BUFSIZE = 0x80000
23
24def mergecfmfiles(srclist, dst, architecture = 'fat'):
25    """Merge all files in srclist into a new file dst.
26
27    If architecture is given, only code fragments of that type will be used:
28    "pwpc" for PPC, "m68k" for cfm68k. This does not work for "classic"
29    68k code, since it does not use code fragments to begin with.
30    If architecture is None, all fragments will be used, enabling FAT binaries.
31    """
32
33    srclist = list(srclist)
34    for i in range(len(srclist)):
35        srclist[i] = Carbon.File.pathname(srclist[i])
36    dst = Carbon.File.pathname(dst)
37
38    dstfile = open(dst, "wb")
39    rf = Res.FSpOpenResFile(dst, 3)
40    try:
41        dstcfrg = CfrgResource()
42        for src in srclist:
43            srccfrg = CfrgResource(src)
44            for frag in srccfrg.fragments:
45                if frag.architecture == 'pwpc' and architecture == 'm68k':
46                    continue
47                if frag.architecture == 'm68k' and architecture == 'pwpc':
48                    continue
49                dstcfrg.append(frag)
50
51                frag.copydata(dstfile)
52
53        cfrgres = Res.Resource(dstcfrg.build())
54        Res.UseResFile(rf)
55        cfrgres.AddResource('cfrg', 0, "")
56    finally:
57        dstfile.close()
58        rf = Res.CloseResFile(rf)
59
60
61class CfrgResource:
62
63    def __init__(self, path = None):
64        self.version = 1
65        self.fragments = []
66        self.path = path
67        if path is not None and os.path.exists(path):
68            currentresref = Res.CurResFile()
69            resref = Res.FSpOpenResFile(path, 1)
70            Res.UseResFile(resref)
71            try:
72                try:
73                    data = Res.Get1Resource('cfrg', 0).data
74                except Res.Error:
75                    raise Res.Error, "no 'cfrg' resource found", sys.exc_traceback
76            finally:
77                Res.CloseResFile(resref)
78                Res.UseResFile(currentresref)
79            self.parse(data)
80            if self.version != 1:
81                raise error, "unknown 'cfrg' resource format"
82
83    def parse(self, data):
84        (res1, res2, self.version,
85            res3, res4, res5, res6,
86            self.memberCount) = struct.unpack("8l", data[:32])
87        data = data[32:]
88        while data:
89            frag = FragmentDescriptor(self.path, data)
90            data = data[frag.memberSize:]
91            self.fragments.append(frag)
92
93    def build(self):
94        self.memberCount = len(self.fragments)
95        data = struct.pack("8l", 0, 0, self.version, 0, 0, 0, 0, self.memberCount)
96        for frag in self.fragments:
97            data = data + frag.build()
98        return data
99
100    def append(self, frag):
101        self.fragments.append(frag)
102
103
104class FragmentDescriptor:
105
106    def __init__(self, path, data = None):
107        self.path = path
108        if data is not None:
109            self.parse(data)
110
111    def parse(self, data):
112        self.architecture = data[:4]
113        (   self.updatelevel,
114            self.currentVersion,
115            self.oldDefVersion,
116            self.stacksize,
117            self.applibdir,
118            self.fragtype,
119            self.where,
120            self.offset,
121            self.length,
122            self.res1, self.res2,
123            self.memberSize,) = struct.unpack("4lhBB4lh", data[4:42])
124        pname = data[42:self.memberSize]
125        self.name = pname[1:1+ord(pname[0])]
126
127    def build(self):
128        data = self.architecture
129        data = data + struct.pack("4lhBB4l",
130                self.updatelevel,
131                self.currentVersion,
132                self.oldDefVersion,
133                self.stacksize,
134                self.applibdir,
135                self.fragtype,
136                self.where,
137                self.offset,
138                self.length,
139                self.res1, self.res2)
140        self.memberSize = len(data) + 2 + 1 + len(self.name)
141        # pad to 4 byte boundaries
142        if self.memberSize % 4:
143            self.memberSize = self.memberSize + 4 - (self.memberSize % 4)
144        data = data + struct.pack("hb", self.memberSize, len(self.name))
145        data = data + self.name
146        data = data + '\000' * (self.memberSize - len(data))
147        return data
148
149    def getfragment(self):
150        if self.where != 1:
151            raise error, "can't read fragment, unsupported location"
152        f = open(self.path, "rb")
153        f.seek(self.offset)
154        if self.length:
155            frag = f.read(self.length)
156        else:
157            frag = f.read()
158        f.close()
159        return frag
160
161    def copydata(self, outfile):
162        if self.where != 1:
163            raise error, "can't read fragment, unsupported location"
164        infile = open(self.path, "rb")
165        if self.length == 0:
166            infile.seek(0, 2)
167            self.length = infile.tell()
168
169        # Position input file and record new offset from output file
170        infile.seek(self.offset)
171
172        # pad to 16 byte boundaries
173        offset = outfile.tell()
174        if offset % 16:
175            offset = offset + 16 - (offset % 16)
176        outfile.seek(offset)
177        self.offset = offset
178
179        l = self.length
180        while l:
181            if l > BUFSIZE:
182                outfile.write(infile.read(BUFSIZE))
183                l = l - BUFSIZE
184            else:
185                outfile.write(infile.read(l))
186                l = 0
187        infile.close()
188