1/*
2 * tclLoadMac.c --
3 *
4 *	This procedure provides a version of the dlopen() function for use
5 *	on the Macintosh.  This procedure will only work with systems
6 *	that use the Code Fragment Manager.
7 *
8 *	Adapted from tclMacLoad.c in the Tcl 8.0p2 distribution.
9 */
10
11#include <CodeFragments.h>
12#include <Errors.h>
13#include <Resources.h>
14#include <Strings.h>
15#include <FSpCompat.h>
16
17/*
18 * Seems that the 3.0.1 Universal headers leave this define out.  So we
19 * define it here...
20 */
21
22#ifndef fragNoErr
23#   define fragNoErr noErr
24#endif
25
26#include "tcl.h"
27#include "compat:dlfcn.h"
28
29#if GENERATINGPOWERPC
30#   define OUR_ARCH_TYPE kPowerPCCFragArch
31#else
32#   define OUR_ARCH_TYPE kMotorola68KCFragArch
33#endif
34
35/*
36 * The following data structure defines the structure of a code fragment
37 * resource.  We can cast the resource to be of this type to access
38 * any fields we need to see.
39 */
40struct CfrgHeader {
41    long 	res1;
42    long 	res2;
43    long 	version;
44    long 	res3;
45    long 	res4;
46    long 	filler1;
47    long 	filler2;
48    long 	itemCount;
49    char	arrayStart;	/* Array of externalItems begins here. */
50};
51typedef struct CfrgHeader CfrgHeader, *CfrgHeaderPtr, **CfrgHeaderPtrHand;
52
53/*
54 * The below structure defines a cfrag item within the cfrag resource.
55 */
56struct CfrgItem {
57    OSType 	archType;
58    long 	updateLevel;
59    long	currVersion;
60    long	oldDefVersion;
61    long	appStackSize;
62    short	appSubFolder;
63    char	usage;
64    char	location;
65    long	codeOffset;
66    long	codeLength;
67    long	res1;
68    long	res2;
69    short	itemSize;
70    Str255	name;		/* This is actually variable sized. */
71};
72typedef struct CfrgItem CfrgItem;
73
74/*
75 *----------------------------------------------------------------------
76 *
77 * dlopen --
78 *
79 *	This function is an implementation of dlopen() for the Mac.
80 *
81 * Results:
82 *	Returns the handle of the newly loaded library, or NULL on
83 *	failure.
84 *
85 * Side effects:
86 *	Loads the specified library into the process.
87 *
88 *----------------------------------------------------------------------
89 */
90
91static Str255 errName;
92
93void *
94dlopen(path, mode)
95    const char *path;
96    int mode;
97{
98    CFragConnectionID connID;
99    Ptr dummy;
100    OSErr err;
101    FSSpec fileSpec;
102    short fragFileRef, saveFileRef;
103    Handle fragResource;
104    UInt32 offset = 0;
105    UInt32 length = kCFragGoesToEOF;
106    char packageName[255];
107    const char* pkgGuess;
108    char* p;
109
110    /*
111     * First thing we must do is infer the package name from the file
112     * name.  This is kind of dumb since the caller actually knows
113     * this value, it just doesn't give it to us.
114     */
115
116    if ((pkgGuess = strrchr(path,':')) != NULL) {
117      pkgGuess++;
118    } else {
119      pkgGuess = path;
120    }
121    if (!strncmp(pkgGuess,"lib",3)) {
122      pkgGuess+=3;
123    }
124    strcpy(packageName,pkgGuess);
125    p = packageName;
126    if ((*p) && islower(UCHAR(*p++))) {
127      packageName[0] = (char) toupper(UCHAR(packageName[0]));
128    }
129    while (isalpha(UCHAR(*p)) || (*p == '_')) {
130      if (isupper(UCHAR(*p))) {
131        *p = (char) tolower(UCHAR(*p));
132      }
133      p++;
134    }
135    *p = 0;
136
137    err = FSpLocationFromPath(strlen(path), (char *) path, &fileSpec);
138    if (err != noErr) {
139	strcpy((char *) errName, "file not found");
140	return (void *) NULL;
141    }
142
143    /*
144     * See if this fragment has a 'cfrg' resource.  It will tell us were
145     * to look for the fragment in the file.  If it doesn't exist we will
146     * assume we have a ppc frag using the whole data fork.  If it does
147     * exist we find the frag that matches the one we are looking for and
148     * get the offset and size from the resource.
149     */
150    saveFileRef = CurResFile();
151    SetResLoad(false);
152    fragFileRef = FSpOpenResFile(&fileSpec, fsRdPerm);
153    SetResLoad(true);
154    if (fragFileRef != -1) {
155	UseResFile(fragFileRef);
156	fragResource = Get1Resource(kCFragResourceType, kCFragResourceID);
157	HLock(fragResource);
158	if (ResError() == noErr) {
159	    CfrgItem* srcItem;
160	    long itemCount, index;
161	    Ptr itemStart;
162
163	    itemCount = (*(CfrgHeaderPtrHand)fragResource)->itemCount;
164	    itemStart = &(*(CfrgHeaderPtrHand)fragResource)->arrayStart;
165	    for (index = 0; index < itemCount;
166		 index++, itemStart += srcItem->itemSize) {
167		srcItem = (CfrgItem*)itemStart;
168		if (srcItem->archType != OUR_ARCH_TYPE) continue;
169		if (!strncasecmp(packageName, (char *) srcItem->name + 1,
170			srcItem->name[0])) {
171		    offset = srcItem->codeOffset;
172		    length = srcItem->codeLength;
173		}
174	    }
175	}
176	/*
177	 * Close the resource file.  If the extension wants to reopen the
178	 * resource fork it should use the tclMacLibrary.c file during it's
179	 * construction.
180	 */
181	HUnlock(fragResource);
182	ReleaseResource(fragResource);
183	CloseResFile(fragFileRef);
184	UseResFile(saveFileRef);
185    }
186
187    /*
188     * Now we can attempt to load the fragement using the offset & length
189     * obtained from the resource.  We don't worry about the main entry point
190     * as we are going to search for specific entry points passed to us.
191     */
192
193    c2pstr(packageName);
194    err = GetDiskFragment(&fileSpec, offset, length, (StringPtr) packageName,
195	    kLoadCFrag, &connID, &dummy, errName);
196    if (err != fragNoErr) {
197	p2cstr(errName);
198	return (void *) NULL;
199    }
200    return (void *) connID;
201}
202
203/*
204 *----------------------------------------------------------------------
205 *
206 * dlsym --
207 *
208 *	This function is an alternative for the system function
209 *	GetProcAddress. It returns the address of a
210 *	symbol, give the handle returned by dlopen().
211 *
212 * Results:
213 *	Returns the address of the symbol in the dll.
214 *
215 * Side effects:
216 *	None.
217 *
218 *----------------------------------------------------------------------
219 */
220
221void *
222dlsym(handle, symbol)
223    void *handle;
224    const char *symbol;
225{
226    void *procPtr;
227    char sym1[255];
228    OSErr err;
229    SymClass symClass;
230
231    strcpy(sym1, symbol);
232    c2pstr(sym1);
233
234    err = FindSymbol((ConnectionID) handle, (StringPtr) sym1,
235	    (Ptr *) &procPtr, &symClass);
236    if (err != fragNoErr || symClass == kDataCFragSymbol) {
237	procPtr = (void *) NULL;
238    }
239    return procPtr;
240}
241
242/*
243 *----------------------------------------------------------------------
244 *
245 * dlerror --
246 *
247 *	This function returns a string describing the error which
248 *	occurred in dlopen().
249 *
250 * Results:
251 *	Returns an error message.
252 *
253 * Side effects:
254 *	None.
255 *
256 *----------------------------------------------------------------------
257 */
258
259char *
260dlerror()
261{
262    return (char *) errName;
263}
264
265
266/*
267 *----------------------------------------------------------------------
268 *
269 * dlclose --
270 *
271 *	Not implemented yet.
272 *
273 * Results:
274 *	Always returns 0 (= O.K.)
275 *
276 * Side effects:
277 *	None
278 *
279 *----------------------------------------------------------------------
280 */
281
282int
283dlclose(handle)
284    void *handle;
285{
286    return 0;
287}
288
289