1// -------------------------------------------------------
2// File: "tclResource.c"
3//                        Created: 2003-09-20 10:13:07
4//              Last modification: 2006-01-05 09:23:29
5// Author: Bernard Desgraupes
6// e-mail: <bdesgraupes@users.sourceforge.net>
7// (c) Copyright : Bernard Desgraupes, 2003-2006
8// All rights reserved.
9// This software is free software with BSD licence.
10// Versions history: see the Changes.Log file.
11//
12// $Date: 2007/08/23 11:04:53 $
13// -------------------------------------------------------
14
15#include "tclResource_version.h"
16
17#include <CoreServices/CoreServices.h>
18#ifndef TCLRESOURCE_DONT_USE_CARBON
19#include <Carbon/Carbon.h>
20#endif
21
22#ifdef TCLRESOURCE_USE_FRAMEWORK_INCLUDES
23#include <Tcl/tcl.h>
24#include <Tcl/tclInt.h>
25#else
26#include <tcl.h>
27#include <tclInt.h>
28#endif
29
30#include <fcntl.h>
31
32#define TCLRESOURCE_PATH_SEP '/'
33
34// Hash table to track open resource files.
35typedef struct OpenResourceFork {
36	short fileRef;
37	int   fileFork;
38	int   flags;
39} OpenResourceFork;
40
41// Flags used by the TclRes_RegisterResourceFork() function.
42// See comments with this function.
43enum {
44	fork_InsertTail = 1,
45	fork_DontClose = 2,
46	fork_CheckIfOpen = 4
47};
48
49// Enumerated values to designate the resource fork
50enum {
51	from_unspecified = -1,
52	from_anyfork = 0,
53	from_rezfork,
54	from_datafork
55};
56
57
58//  Prototypes for static functions
59static int	TclResCmd_Attributes(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
60static int	TclResCmd_Close(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
61static int	TclResCmd_Delete(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
62static int	TclResCmd_Files(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
63static int	TclResCmd_Fork(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
64static int	TclResCmd_Id(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
65static int	TclResCmd_List(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
66static int	TclResCmd_Name(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
67static int	TclResCmd_Open(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
68static int	TclResCmd_Read(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
69static int	TclResCmd_Types(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
70static int	TclResCmd_Update(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
71static int	TclResCmd_Write(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], Tcl_Obj *resultPtr);
72
73static void	TclRes_BuildResourceForkList(void);
74static void	TclRes_UpdateStringOfOSType(Tcl_Obj *objPtr);
75static void	TclRes_DupOSTypeInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
76static int	TclRes_SetOSTypeFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
77
78// Prototypes moved from /tcl/generic/tclPlatDecls.h
79static Handle	 	TclRes_FindResource(Tcl_Interp * interp,
80									long resourceType,
81									CONST char * resourceName,
82									int resourceNumber,
83									CONST char * resFileRef,
84									int * releaseIt);
85static OpenResourceFork * TclRes_GetResourceRefFromObj(Tcl_Obj *objPtr,
86									int okayOnReadOnly,
87									const char *operation,
88									Tcl_Obj *resultPtr);
89static void			TclRes_InitializeTables(void);
90static int 			TclRes_GetOSTypeFromObj(Tcl_Interp * interp, Tcl_Obj * objPtr, OSType * osTypePtr);
91static void 		TclRes_SetOSTypeObj(Tcl_Obj * objPtr, OSType osType);
92static Tcl_Obj * 	TclRes_NewOSTypeObj(OSType osType);
93static int 			TclRes_RegisterResourceFork(short fileRef, Tcl_Obj * tokenPtr, int whichFork, int insert);
94static short 		TclRes_UnRegisterResourceFork(char * tokenPtr, Tcl_Obj * resultPtr);
95
96
97static int			Tcl_ResourceCommand(ClientData clientData,  Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]);
98
99// The init function called when the package is loaded in the Tcl interpreter.
100#pragma export on
101int Resource_Init(Tcl_Interp *interp);
102#pragma export off
103
104
105// The structure below defines the Tcl object type defined in this file by
106// means of procedures that can be invoked by generic object code.
107static Tcl_ObjType osType = {
108	"ostype",							// name
109	(Tcl_FreeInternalRepProc *) NULL,   // freeIntRepProc
110	TclRes_DupOSTypeInternalRep,		// dupIntRepProc
111	TclRes_UpdateStringOfOSType,		// updateStringProc
112	TclRes_SetOSTypeFromAny				// setFromAnyProc
113};
114
115
116static Tcl_HashTable nameTable;			// Id to process number mapping.
117static Tcl_HashTable resourceTable;		// Process number to id mapping.
118
119Tcl_Obj *resourceForkList = NULL;		// Ordered list of resource forks
120int newId = 0;							// Id source.
121int osTypeInit = 0;						// 0 means Tcl object of osType hasn't
122										//     been initialized yet.
123int initialized = 0;					// 0 means static structures haven't
124										//     been initialized yet.
125
126
127
128
129// ----------------------------------------------------------------------
130//
131// Resource_Init --
132//
133//	This procedure is invoked when the package is loaded.
134//
135// Results:
136//	A standard Tcl result.
137//
138// Side effects:
139//	None.
140//
141// ----------------------------------------------------------------------
142
143int Resource_Init(Tcl_Interp *interp) {
144	char vstr[64];
145
146#ifdef USE_TCL_STUBS
147	if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
148		return TCL_ERROR;
149	}
150#endif
151
152	// Register resource command
153	Tcl_CreateObjCommand(interp, "resource", Tcl_ResourceCommand, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
154
155	// Version numbering
156	if (TCLRESOURCE_STAGE=='f') {
157		if (TCLRESOURCE_SUBMINOR) {
158			sprintf(vstr,"%d.%d.%d", TCLRESOURCE_MAJOR, TCLRESOURCE_MINOR, TCLRESOURCE_SUBMINOR);
159		} else {
160			sprintf(vstr,"%d.%d", TCLRESOURCE_MAJOR, TCLRESOURCE_MINOR);
161		}
162	} else {
163		sprintf(vstr,"%d.%d%c%d", TCLRESOURCE_MAJOR, TCLRESOURCE_MINOR,
164				TCLRESOURCE_STAGE, TCLRESOURCE_SUBMINOR);
165	}
166
167	// Declare the TclResource package.
168	if (Tcl_PkgProvide(interp, "resource", vstr) != TCL_OK) {
169		return TCL_ERROR;
170	}
171	return TCL_OK;
172}
173
174
175
176// ----------------------------------------------------------------------
177//
178// Tcl_ResourceCommand --
179//
180//	This procedure is invoked to process the "resource" Tcl command.
181//	See the user documentation for details on what it does.
182//
183// Results:
184//	A standard Tcl result.
185//
186// Side effects:
187//	See the user documentation.
188//
189// ----------------------------------------------------------------------
190
191int
192Tcl_ResourceCommand(
193    ClientData clientData,		// Not used.
194    Tcl_Interp *interp,			// Current interpreter.
195    int objc,					// Number of arguments.
196    Tcl_Obj *CONST objv[])		// Argument values.
197{
198	Tcl_Obj *resultPtr;
199	int index, result;
200
201	static CONST char *switches[] = {
202		"attributes", "close", "delete", "files",
203		"fork", "id", "list", "name", "open",
204		"read", "types", "update", "write", (char *) NULL
205	};
206
207	enum {
208		RESOURCE_ATTRIBUTES, RESOURCE_CLOSE, RESOURCE_DELETE, RESOURCE_FILES,
209		RESOURCE_FORK, RESOURCE_ID, RESOURCE_LIST, RESOURCE_NAME, RESOURCE_OPEN,
210		RESOURCE_READ, RESOURCE_TYPES, RESOURCE_UPDATE, RESOURCE_WRITE
211	};
212
213 	resultPtr = Tcl_NewObj();
214
215	if (objc < 2) {
216		Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?");
217		return TCL_ERROR;
218	}
219
220	if (Tcl_GetIndexFromObj(interp, objv[1], switches, "subcommand", 0, &index) != TCL_OK) {
221		return TCL_ERROR;
222	}
223	if (!initialized) {
224		TclRes_InitializeTables();
225	}
226
227	switch (index) {
228		case RESOURCE_ATTRIBUTES:
229		result = TclResCmd_Attributes(clientData, interp, objc, objv, resultPtr);
230		break;
231
232		case RESOURCE_CLOSE:
233		result = TclResCmd_Close(clientData, interp, objc, objv, resultPtr);
234		break;
235
236		case RESOURCE_DELETE:
237		result = TclResCmd_Delete(clientData, interp, objc, objv, resultPtr);
238		break;
239
240		case RESOURCE_FILES:
241		result = TclResCmd_Files(clientData, interp, objc, objv, resultPtr);
242		break;
243
244		case RESOURCE_FORK:
245		result = TclResCmd_Fork(clientData, interp, objc, objv, resultPtr);
246		break;
247
248		case RESOURCE_ID:
249		result = TclResCmd_Id(clientData, interp, objc, objv, resultPtr);
250		break;
251
252		case RESOURCE_LIST:
253		result = TclResCmd_List(clientData, interp, objc, objv, resultPtr);
254		break;
255
256		case RESOURCE_NAME:
257		result = TclResCmd_Name(clientData, interp, objc, objv, resultPtr);
258		break;
259
260		case RESOURCE_OPEN:
261		result = TclResCmd_Open(clientData, interp, objc, objv, resultPtr);
262		break;
263
264		case RESOURCE_READ:
265		result = TclResCmd_Read(clientData, interp, objc, objv, resultPtr);
266		break;
267
268		case RESOURCE_TYPES:
269		result = TclResCmd_Types(clientData, interp, objc, objv, resultPtr);
270		break;
271
272		case RESOURCE_UPDATE:
273		result = TclResCmd_Update(clientData, interp, objc, objv, resultPtr);
274		break;
275
276		case RESOURCE_WRITE:
277		result = TclResCmd_Write(clientData, interp, objc, objv, resultPtr);
278		break;
279
280		default:
281		panic("Tcl_GetIndexFromObj returned unrecognized option");
282		return TCL_ERROR;	// Should never be reached.
283	}
284
285	Tcl_ResetResult(interp);
286	Tcl_SetObjResult(interp, resultPtr);
287
288	return result;
289}
290
291
292// ----------------------------------------------------------------------
293//
294// TclResCmd_Attributes --
295//
296//	This procedure is invoked to process the [resource attributes] Tcl command.
297//	See the user documentation for details on what it does.
298//
299// Syntax:
300// 		resource attributes resourceRef
301// 		resource attributes resourceRef value
302// 		resource attributes resourceRef option resourceType
303// 		resource attributes resourceRef option resourceType value
304//
305// Results:
306//	A standard Tcl result.
307//
308// Side effects:
309//	See the user documentation.
310//
311// ----------------------------------------------------------------------
312
313int
314TclResCmd_Attributes(
315    ClientData clientData,		// Not used.
316    Tcl_Interp *interp,			// Current interpreter.
317    int objc,					// Number of arguments.
318    Tcl_Obj *CONST objv[],		// Argument values.
319    Tcl_Obj *resultPtr)			// Pointer to store the result.
320{
321	OpenResourceFork * resourceRef;
322    int		index, result, gotResID, gotValue, length, newValue;
323	short	rsrcId = 0;
324	long	theLong;
325    short	saveRef, theMapAttrs, theRezAttrs;
326    char *	resourceName = NULL;
327    char	buffer[128];
328    Handle	resourceH = NULL;
329    OSErr	err = noErr;
330    Str255	theName;
331    OSType	rezType;
332
333	static CONST char *attributesSwitches[] = {
334		"-id", "-name", (char *) NULL
335	};
336
337	enum {
338		RESOURCE_ATTRIBUTES_ID, RESOURCE_ATTRIBUTES_NAME
339	};
340
341	result = TCL_OK;
342
343	if (!(objc == 3 || objc == 4 || objc == 6 || objc == 7)) {
344		Tcl_WrongNumArgs(interp, 2, objv,
345						 "resourceRef ?(-id resourceID|-name resourceName) resourceType? ?value?");
346		return TCL_ERROR;
347	}
348
349	resourceRef = TclRes_GetResourceRefFromObj(objv[2], true,
350											   "get attributes from", resultPtr);
351	if (resourceRef == NULL) {
352		return TCL_ERROR;
353	}
354
355	gotValue = false;
356
357	if (objc == 4 || objc == 7) {
358		if (Tcl_GetIntFromObj(interp, objv[objc-1], &newValue) != TCL_OK) {
359			return TCL_ERROR;
360		}
361		gotValue = true;
362	}
363
364	if (objc == 3) {
365		// Getting the resource map attributes
366		theMapAttrs = GetResFileAttrs(resourceRef->fileRef);
367		err = ResError();
368		if (err != noErr) {
369			Tcl_AppendStringsToObj(resultPtr, "error getting resource map attributes", (char *) NULL);
370			return TCL_ERROR;
371		} else {
372			Tcl_SetIntObj(resultPtr, theMapAttrs);
373			return TCL_OK;
374		}
375	}
376
377	if (objc == 4) {
378		// Setting the resource map attributes
379		SetResFileAttrs(resourceRef->fileRef, newValue);
380		err = ResError();
381		if (err != noErr) {
382			Tcl_AppendStringsToObj(resultPtr, "error setting resource map attributes", (char *) NULL);
383			return TCL_ERROR;
384		}
385		return TCL_OK;
386	}
387
388	gotResID = false;
389	resourceName = NULL;
390
391	if (Tcl_GetIndexFromObj(interp, objv[3], attributesSwitches, "switch", 0, &index) != TCL_OK) {
392		return TCL_ERROR;
393	}
394
395	switch (index) {
396
397		case RESOURCE_ATTRIBUTES_ID:
398		if (Tcl_GetLongFromObj(interp, objv[4], &theLong) != TCL_OK) {
399			return TCL_ERROR;
400		}
401		rsrcId = (short) theLong;
402		gotResID = true;
403		break;
404
405		case RESOURCE_ATTRIBUTES_NAME:
406		resourceName = Tcl_GetStringFromObj(objv[4], &length);
407		resourceName = strcpy((char *) theName, resourceName);
408		c2pstr(resourceName);
409		break;
410	}
411
412	if (TclRes_GetOSTypeFromObj(interp, objv[5], &rezType) != TCL_OK) {
413		return TCL_ERROR;
414	}
415
416	if ((resourceName == NULL) && !gotResID) {
417		Tcl_AppendStringsToObj(resultPtr,"you must specify either ",
418				"-id or -name",
419				(char *) NULL);
420		return TCL_ERROR;
421	}
422
423	saveRef = CurResFile();
424	UseResFile(resourceRef->fileRef);
425
426	// Don't load the resource in memory
427	SetResLoad(false);
428	if (gotResID == true) {
429		resourceH = Get1Resource(rezType, rsrcId);
430		err = ResError();
431	} else if (resourceName != NULL) {
432		resourceH = Get1NamedResource(rezType, (StringPtr) resourceName);
433		err = ResError();
434	}
435
436	SetResLoad(true);
437
438	if (err != noErr) {
439		sprintf(buffer, "resource error %d while trying to find resource", err);
440		Tcl_AppendStringsToObj(resultPtr, buffer, (char *) NULL);
441		result = TCL_ERROR;
442		goto attributesDone;
443	}
444
445	// Getting/setting the value
446	if (resourceH != NULL) {
447		if (gotValue) {
448			// Setting the resource attributes
449			theMapAttrs = GetResFileAttrs(resourceRef->fileRef);
450			if (theMapAttrs & mapReadOnly) {
451				Tcl_AppendStringsToObj(resultPtr, "cannot set the attributes, resource map is read only", (char *) NULL);
452				result = TCL_ERROR;
453				goto attributesDone;
454			}
455			theRezAttrs = GetResAttrs(resourceH);
456			if (theRezAttrs != newValue) {
457				// If the user is setting the resChanged flag on, load the
458				// resource in memory if it is not already there (i-e if its
459				// master pointer contains NULL) otherwise, upon updating, null
460				// data would be written to the disk. NB: no need to bother about
461				// releasing the resource because anyway ReleaseResource() won�t
462				// release a resource whose resChanged attribute has been set.
463				if (newValue & resChanged) {
464					if (*resourceH == NULL) {
465						LoadResource(resourceH);
466					}
467				}
468				SetResAttrs(resourceH, newValue);
469				err = ResError();
470				if (err != noErr) {
471					sprintf(buffer, "error %d setting resource attributes", err);
472					Tcl_AppendStringsToObj(resultPtr, buffer, (char *) NULL);
473					result = TCL_ERROR;
474					goto attributesDone;
475				}
476			}
477			result = TCL_OK;
478		} else {
479			// Getting the resource attributes
480			theRezAttrs = GetResAttrs(resourceH);
481			err = ResError();
482			if (err != noErr) {
483				Tcl_AppendStringsToObj(resultPtr, "error getting resource attributes", (char *) NULL);
484				result = TCL_ERROR;
485				goto attributesDone;
486			} else {
487				Tcl_SetIntObj(resultPtr, theRezAttrs);
488				result = TCL_OK;
489			}
490		}
491	} else {
492		Tcl_AppendStringsToObj(resultPtr, "resource not found", (char *) NULL);
493		result = TCL_ERROR;
494		goto attributesDone;
495	}
496
497attributesDone:
498	UseResFile(saveRef);
499	return result;
500}
501
502
503// ----------------------------------------------------------------------
504//
505// TclResCmd_Close --
506//
507//	This procedure is invoked to process the [resource close] Tcl command.
508//	See the user documentation for details on what it does.
509//
510// Syntax:
511// 		resource close resourceRef
512//
513// Results:
514//	A standard Tcl result.
515//
516// Side effects:
517//	See the user documentation.
518//
519// ----------------------------------------------------------------------
520
521int
522TclResCmd_Close(
523	ClientData clientData,		// Not used.
524	Tcl_Interp *interp,			// Current interpreter.
525	int objc,					// Number of arguments.
526	Tcl_Obj *CONST objv[],		// Argument values.
527	Tcl_Obj *resultPtr)			// Pointer to store the result.
528{
529	int		length;
530	short	fileRef;
531	char *	stringPtr;
532	OSErr	err;
533	int		result = TCL_OK;
534
535	if (objc != 3) {
536		Tcl_WrongNumArgs(interp, 2, objv, "resourceRef");
537		return TCL_ERROR;
538	}
539	stringPtr = Tcl_GetStringFromObj(objv[2], &length);
540	fileRef = TclRes_UnRegisterResourceFork(stringPtr, resultPtr);
541
542	// If fileRef is not a reference number for a file whose resource fork is
543	// open, CloseResFile() does nothing, and ResError() returns the
544	// result code resFNotFound. If fileRef is 0, it represents the System
545	// file and is ignored. You cannot close the System file�s resource fork.
546	if (fileRef > 0) {
547		CloseResFile(fileRef);
548		err = ResError();
549		if (err != noErr) {
550			Tcl_AppendStringsToObj(resultPtr, "couldn't close the resource fork", (char *) NULL);
551			result = TCL_ERROR;
552		}
553	} else {
554		result = TCL_ERROR;
555	}
556	return result;
557}
558
559
560// ----------------------------------------------------------------------
561//
562// TclResCmd_Delete --
563//
564//	This procedure is invoked to process the [resource delete] Tcl command.
565//	See the user documentation for details on what it does.
566//
567// Syntax:
568// 		resource delete ?options? resourceType
569//
570// Results:
571//	A standard Tcl result.
572//
573// Side effects:
574//	See the user documentation.
575//
576// ----------------------------------------------------------------------
577
578int
579TclResCmd_Delete(
580    ClientData clientData,		// Not used.
581    Tcl_Interp *interp,			// Current interpreter.
582    int objc,					// Number of arguments.
583    Tcl_Obj *CONST objv[],		// Argument values.
584    Tcl_Obj *resultPtr)			// Pointer to store the result.
585{
586	OpenResourceFork *resourceRef = NULL;
587    int		index, result, gotResID;
588    int		i, limitSearch, length;
589    short	saveRef = 0, resInfo;
590	short	fileRef, rsrcId = 0;
591	long	theLong;
592    char *	resourceName = NULL;
593	char	buffer[128];
594    Handle	resourceH = NULL;
595    OSErr	err;
596    Str255	theName;
597    OSType	rezType;
598
599    static CONST char *deleteSwitches[] = {"-id", "-name", "-file", (char *) NULL};
600
601    enum {RESOURCE_DELETE_ID, RESOURCE_DELETE_NAME, RESOURCE_DELETE_FILE};
602
603    result = TCL_OK;
604
605	if (!((objc >= 3) && (objc <= 9) && ((objc % 2) == 1))) {
606		Tcl_WrongNumArgs(interp, 2, objv,
607						 "?-id resourceID? ?-name resourceName? ?-file resourceRef? resourceType");
608		return TCL_ERROR;
609	}
610
611	i = 2;
612	fileRef = kResFileNotOpened;
613	gotResID = false;
614	resourceName = NULL;
615	limitSearch = false;
616
617	while (i < (objc - 2)) {
618		if (Tcl_GetIndexFromObj(interp, objv[i], deleteSwitches, "option", 0, &index) != TCL_OK) {
619			return TCL_ERROR;
620		}
621
622		switch (index) {
623
624			case RESOURCE_DELETE_ID:
625			if (Tcl_GetLongFromObj(interp, objv[i+1], &theLong) != TCL_OK) {
626				return TCL_ERROR;
627			}
628			rsrcId = (short) theLong;
629			gotResID = true;
630			break;
631
632			case RESOURCE_DELETE_NAME:
633			resourceName = Tcl_GetStringFromObj(objv[i+1], &length);
634			if (length > 255) {
635				Tcl_AppendStringsToObj(resultPtr,
636					   "-name argument too long, must be < 255 characters", (char *) NULL);
637				return TCL_ERROR;
638			}
639			resourceName = strcpy((char *) theName, resourceName);
640			c2pstr(resourceName);
641			break;
642
643			case RESOURCE_DELETE_FILE:
644			resourceRef = TclRes_GetResourceRefFromObj(objv[i+1], 0, "delete from", resultPtr);
645			if (resourceRef == NULL) {
646				return TCL_ERROR;
647			}
648			limitSearch = true;
649			break;
650		}
651		i += 2;
652	}
653
654	if ((resourceName == NULL) && !gotResID) {
655		Tcl_AppendStringsToObj(resultPtr,"you must specify either ",
656		        "-id or -name or both", (char *) NULL);
657		return TCL_ERROR;
658	}
659
660	if (TclRes_GetOSTypeFromObj(interp, objv[i], &rezType) != TCL_OK) {
661		return TCL_ERROR;
662	}
663
664	if (limitSearch) {
665		saveRef = CurResFile();
666		UseResFile(resourceRef->fileRef);
667	}
668
669	SetResLoad(false);
670
671	if (gotResID == true) {
672		if (limitSearch) {
673			resourceH = Get1Resource(rezType, rsrcId);
674		} else {
675			resourceH = GetResource(rezType, rsrcId);
676		}
677		err = ResError();
678
679		if (err == resNotFound || resourceH == NULL) {
680			Tcl_AppendStringsToObj(resultPtr, "resource not found", (char *) NULL);
681			result = TCL_ERROR;
682			goto deleteDone;
683		} else if (err != noErr) {
684			sprintf(buffer, "error %d while trying to find resource", err);
685			Tcl_AppendStringsToObj(resultPtr, buffer, (char *) NULL);
686			result = TCL_ERROR;
687			goto deleteDone;
688		}
689	}
690
691	if (resourceName != NULL) {
692		Handle tmpResource;
693		if (limitSearch) {
694			tmpResource = Get1NamedResource(rezType, (StringPtr) resourceName);
695		} else {
696			tmpResource = GetNamedResource(rezType, (StringPtr) resourceName);
697		}
698		err = ResError();
699
700		if (err == resNotFound || tmpResource == NULL) {
701			Tcl_AppendStringsToObj(resultPtr, "resource not found", (char *) NULL);
702			result = TCL_ERROR;
703			goto deleteDone;
704		} else if (err != noErr) {
705			sprintf(buffer, "error %d while trying to find resource", err);
706			Tcl_AppendStringsToObj(resultPtr, buffer, (char *) NULL);
707			result = TCL_ERROR;
708			goto deleteDone;
709		}
710
711		if (gotResID) {
712			if (resourceH != tmpResource) {
713				Tcl_AppendStringsToObj(resultPtr, "-id and -name ",
714									   "values do not point to the same resource", (char *) NULL);
715				result = TCL_ERROR;
716				goto deleteDone;
717			}
718		} else {
719			resourceH = tmpResource;
720		}
721	}
722
723	resInfo = GetResAttrs(resourceH);
724
725	if ((resInfo & resProtected) == resProtected) {
726		Tcl_AppendStringsToObj(resultPtr,
727							   "resource cannot be deleted: it is protected.", (char *) NULL);
728		result = TCL_ERROR;
729		goto deleteDone;
730	} else if ((resInfo & resSysHeap) == resSysHeap) {
731		Tcl_AppendStringsToObj(resultPtr,
732							   "resource cannot be deleted: it is in the system heap.", (char *) NULL);
733		result = TCL_ERROR;
734		goto deleteDone;
735	}
736
737	// Find the resource file, if it was not specified,
738	// so we can flush the changes now. Perhaps this is
739	// a little paranoid, but better safe than sorry.
740	RemoveResource(resourceH);
741
742	if (!limitSearch) {
743		UpdateResFile(HomeResFile(resourceH));
744	} else {
745		UpdateResFile(resourceRef->fileRef);
746	}
747
748deleteDone:
749	SetResLoad(true);
750	if (limitSearch) {
751		UseResFile(saveRef);
752	}
753	return result;
754}
755
756
757// ----------------------------------------------------------------------
758//
759// TclResCmd_Files --
760//
761//	This procedure is invoked to process the [resource files] Tcl command.
762//	See the user documentation for details on what it does.
763//
764// Syntax:
765// 		resource files ?resourceRef?
766//
767// Results:
768//	A standard Tcl result.
769//
770// Side effects:
771//	See the user documentation.
772//
773// ----------------------------------------------------------------------
774
775int
776TclResCmd_Files(
777    ClientData clientData,		// Not used.
778    Tcl_Interp *interp,			// Current interpreter.
779    int objc,					// Number of arguments.
780    Tcl_Obj *CONST objv[],		// Argument values.
781    Tcl_Obj *resultPtr)			// Pointer to store the result.
782{
783	OpenResourceFork * resourceRef;
784	int		length;
785	char *	stringPtr;
786	OSErr	err;
787
788	if ((objc < 2) || (objc > 3)) {
789		Tcl_WrongNumArgs(interp, 2, objv, "?resourceID?");
790		return TCL_ERROR;
791	}
792
793	if (objc == 2) {
794		stringPtr = Tcl_GetStringFromObj(resourceForkList, &length);
795		Tcl_SetStringObj(resultPtr, stringPtr, length);
796	} else {
797		FCBPBRec	fileRec;
798		Str255		fileName;
799		UInt8		pathPtr[256];
800		FSSpec		fileFSSpec;
801		FSRef		fileFSRef;
802		Tcl_DString	ds;
803
804		resourceRef = TclRes_GetResourceRefFromObj(objv[2], 1, "files", resultPtr);
805		if (resourceRef == NULL) {
806			return TCL_ERROR;
807		}
808
809		fileRec.ioCompletion = NULL;
810		fileRec.ioFCBIndx = 0;
811		fileRec.ioNamePtr = fileName;
812		fileRec.ioVRefNum = 0;
813		fileRec.ioRefNum = resourceRef->fileRef;
814		err = PBGetFCBInfo(&fileRec, false);
815		if (err != noErr) {
816			Tcl_SetStringObj(resultPtr,
817							 "could not get FCB for resource file", -1);
818			return TCL_ERROR;
819		}
820
821		// Get an FSRef and build the path
822		fileFSSpec.vRefNum = fileRec.ioFCBVRefNum;
823		fileFSSpec.parID = fileRec.ioFCBParID;
824		strncpy( (char *) fileFSSpec.name, (char *) fileRec.ioNamePtr, fileRec.ioNamePtr[0]+1);
825		err = FSpMakeFSRef(&fileFSSpec, &fileFSRef);
826		err = FSRefMakePath(&fileFSRef, pathPtr, 256);
827		if ( err != noErr) {
828			Tcl_SetStringObj(resultPtr,
829							 "could not get file path from token", -1);
830			return TCL_ERROR;
831		}
832
833		Tcl_ExternalToUtfDString(NULL, pathPtr, strlen(pathPtr), &ds);
834		Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds));
835		Tcl_DStringFree(&ds);
836	}
837	return TCL_OK;
838}
839
840
841// ----------------------------------------------------------------------
842//
843// TclResCmd_Fork --
844//
845//	This procedure is invoked to process the [resource fork] Tcl command.
846//	See the user documentation for details on what it does.
847//
848// Syntax:
849// 		resource fork resourceRef
850//
851// Results:
852//	A standard Tcl result.
853//
854// Side effects:
855//	See the user documentation.
856//
857// ----------------------------------------------------------------------
858
859int
860TclResCmd_Fork(
861    ClientData clientData,		// Not used.
862    Tcl_Interp *interp,			// Current interpreter.
863    int objc,					// Number of arguments.
864    Tcl_Obj *CONST objv[],		// Argument values.
865    Tcl_Obj *resultPtr)			// Pointer to store the result.
866{
867	OpenResourceFork * resourceRef;
868
869	if (objc != 3) {
870		Tcl_WrongNumArgs(interp, 2, objv, "resourceRef");
871		return TCL_ERROR;
872	}
873	resourceRef = TclRes_GetResourceRefFromObj(objv[2], true,
874										"get fork from", resultPtr);
875
876	if (resourceRef != NULL) {
877		Tcl_ResetResult(interp);
878		switch (resourceRef->fileFork) {
879
880			case from_rezfork:
881			Tcl_AppendStringsToObj(resultPtr, "resourcefork", (char *) NULL);
882			return TCL_OK;
883			break;
884
885			case from_datafork:
886			Tcl_AppendStringsToObj(resultPtr, "datafork", (char *) NULL);
887			return TCL_OK;
888			break;
889
890			default:
891			Tcl_AppendStringsToObj(resultPtr, "unknown", (char *) NULL);
892			return TCL_OK;
893		}
894	} else {
895		return TCL_ERROR;
896	}
897}
898
899
900// ----------------------------------------------------------------------
901//
902// TclResCmd_Id --
903//
904//	This procedure is invoked to process the [resource id] Tcl command.
905//	See the user documentation for details on what it does.
906//
907// Syntax:
908// 		resource id resourceType resourceName resourceRef
909//
910// Results:
911//	A standard Tcl result.
912//
913// Side effects:
914//	See the user documentation.
915//
916// ----------------------------------------------------------------------
917
918int
919TclResCmd_Id(
920    ClientData clientData,		// Not used.
921    Tcl_Interp *interp,			// Current interpreter.
922    int objc,					// Number of arguments.
923    Tcl_Obj *CONST objv[],		// Argument values.
924    Tcl_Obj *resultPtr)			// Pointer to store the result.
925{
926	short	rsrcId = 0;
927	int		length, releaseIt = 0;
928	char *	resmapRef;
929	char *	resourceName = NULL;
930	Handle	resourceH = NULL;
931	OSErr	err;
932	Str255	theName;
933	OSType	rezType;
934
935	Tcl_ResetResult(interp);
936	if (objc != 5) {
937		Tcl_WrongNumArgs(interp, 2, objv,
938						 "resourceType resourceName resourceRef");
939		return TCL_ERROR;
940	}
941
942	if (TclRes_GetOSTypeFromObj(interp, objv[2], &rezType) != TCL_OK) {
943		return TCL_ERROR;
944	}
945
946	resourceName = Tcl_GetStringFromObj(objv[3], &length);
947	if (resourceName == NULL) {
948		Tcl_AppendStringsToObj(resultPtr, "wrong third argument", (char *) NULL);
949		return TCL_ERROR;
950	}
951
952	resmapRef = Tcl_GetStringFromObj(objv[4], &length);
953	resourceH = TclRes_FindResource(interp, rezType, resourceName,
954									rsrcId, resmapRef, &releaseIt);
955
956	if (resourceH != NULL) {
957		GetResInfo(resourceH, &rsrcId, (ResType *) &rezType, theName);
958		err = ResError();
959		if (err == noErr) {
960			Tcl_SetIntObj(resultPtr, rsrcId);
961			return TCL_OK;
962		} else {
963			Tcl_AppendStringsToObj(resultPtr, "could not get resource info", (char *) NULL);
964			return TCL_ERROR;
965		}
966		if (releaseIt) {
967			ReleaseResource(resourceH);
968		}
969	} else {
970		Tcl_AppendStringsToObj(resultPtr, "could not find resource", (char *) NULL);
971		return TCL_ERROR;
972	}
973}
974
975
976// ----------------------------------------------------------------------
977//
978// TclResCmd_List --
979//
980//	This procedure is invoked to process the [resource list] Tcl command.
981//	See the user documentation for details on what it does.
982//
983// Syntax:
984// 		resource list ?-ids? resourceType ?resourceRef?
985//
986// Results:
987//	A standard Tcl result.
988//
989// Side effects:
990//	See the user documentation.
991//
992// ----------------------------------------------------------------------
993
994int
995TclResCmd_List(
996    ClientData clientData,		// Not used.
997    Tcl_Interp *interp,			// Current interpreter.
998    int objc,					// Number of arguments.
999    Tcl_Obj *CONST objv[],		// Argument values.
1000    Tcl_Obj *resultPtr)			// Pointer to store the result.
1001{
1002	OpenResourceFork * resourceRef;
1003	Tcl_Obj *	objPtr;
1004	int			i, count, result, limitSearch, onlyID, length;
1005	short		id, saveRef = 0;
1006	char *		string;
1007	Handle		resourceH = NULL;
1008	Str255		theName;
1009	OSType		rezType;
1010
1011	result = TCL_OK;
1012	limitSearch = false;
1013	onlyID = false;
1014	i = 2;
1015
1016	if (!((objc >= 3) && (objc <= 5))) {
1017		Tcl_WrongNumArgs(interp, 2, objv, "?-ids? resourceType ?resourceRef?");
1018		return TCL_ERROR;
1019	}
1020	string = Tcl_GetStringFromObj(objv[i], &length);
1021	if (!strcmp(string, "-ids")) {
1022		onlyID = true;
1023		i++;
1024	}
1025	if (TclRes_GetOSTypeFromObj(interp, objv[i], &rezType) != TCL_OK) {
1026		return TCL_ERROR;
1027	}
1028	i++;
1029	if (objc == i + 1) {
1030		resourceRef = TclRes_GetResourceRefFromObj(objv[i], 1, "list", resultPtr);
1031		if (resourceRef == NULL) {
1032			return TCL_ERROR;
1033		}
1034
1035		saveRef = CurResFile();
1036		UseResFile(resourceRef->fileRef);
1037		limitSearch = true;
1038	}
1039
1040	Tcl_ResetResult(interp);
1041	if (limitSearch) {
1042		count = Count1Resources(rezType);
1043	} else {
1044		count = CountResources(rezType);
1045	}
1046
1047	SetResLoad(false);
1048	for (i = 1; i <= count; i++) {
1049		if (limitSearch) {
1050			resourceH = Get1IndResource(rezType, i);
1051		} else {
1052			resourceH = GetIndResource(rezType, i);
1053		}
1054		if (resourceH != NULL) {
1055			GetResInfo(resourceH, &id, (ResType *) &rezType, theName);
1056			if (theName[0] != 0 && !onlyID) {
1057				objPtr = Tcl_NewStringObj((char *) theName + 1, theName[0]);
1058			} else {
1059				objPtr = Tcl_NewIntObj(id);
1060			}
1061			// Bug in the original code: the resource was released in all cases
1062			// This could cause a crash when calling the command without a
1063			// recourceRef, like for instance:
1064			//     resource list CURS
1065			// because this would release system CURS resources.
1066			// Fix: if the Master Pointer of the returned handle is
1067			// null, then the resource was not in memory, and it is
1068			// safe to release it. Otherwise, it is not.
1069			if (*resourceH == NULL) {
1070				ReleaseResource(resourceH);
1071			}
1072			result = Tcl_ListObjAppendElement(interp, resultPtr, objPtr);
1073			if (result != TCL_OK) {
1074				Tcl_DecrRefCount(objPtr);
1075				break;
1076			}
1077		}
1078	}
1079	SetResLoad(true);
1080
1081	if (limitSearch) {
1082		UseResFile(saveRef);
1083	}
1084
1085	return result;
1086}
1087
1088
1089// ----------------------------------------------------------------------
1090//
1091// TclResCmd_Name --
1092//
1093//	This procedure is invoked to process the [resource name] Tcl command.
1094//	See the user documentation for details on what it does.
1095//
1096// Syntax:
1097// 		resource name resourceType resourceId resourceRef
1098//
1099// Results:
1100//	A standard Tcl result.
1101//
1102// Side effects:
1103//	See the user documentation.
1104//
1105// ----------------------------------------------------------------------
1106
1107int
1108TclResCmd_Name(
1109    ClientData clientData,		// Not used.
1110    Tcl_Interp *interp,			// Current interpreter.
1111    int objc,					// Number of arguments.
1112    Tcl_Obj *CONST objv[],		// Argument values.
1113    Tcl_Obj *resultPtr)			// Pointer to store the result.
1114{
1115	short	rsrcId;
1116	long	theLong;
1117	int		length, releaseIt = 0;
1118	char *	resmapRef;
1119	Handle	resourceH = NULL;
1120	OSErr	err;
1121	Str255	theName;
1122	OSType	rezType;
1123
1124	Tcl_ResetResult(interp);
1125	if (objc != 5) {
1126		Tcl_WrongNumArgs(interp, 2, objv, "resourceType resourceID resourceRef");
1127		return TCL_ERROR;
1128	}
1129
1130	if (TclRes_GetOSTypeFromObj(interp, objv[2], &rezType) != TCL_OK) {
1131		return TCL_ERROR;
1132	}
1133
1134	if (Tcl_GetLongFromObj(interp, objv[3], &theLong) != TCL_OK) {
1135		Tcl_AppendStringsToObj(resultPtr, "wrong third argument: expected integer", (char *) NULL);
1136		return TCL_ERROR;
1137	}
1138	rsrcId = (short) theLong;
1139	resmapRef = Tcl_GetStringFromObj(objv[4], &length);
1140	resourceH = TclRes_FindResource(interp, rezType, NULL,
1141									rsrcId, resmapRef, &releaseIt);
1142
1143	if (resourceH != NULL) {
1144		GetResInfo(resourceH, &rsrcId, (ResType *) &rezType, theName);
1145		err = ResError();
1146		if (err == noErr) {
1147			p2cstr(theName);
1148			Tcl_AppendStringsToObj(resultPtr, theName, (char *) NULL);
1149			return TCL_OK;
1150		} else {
1151			Tcl_AppendStringsToObj(resultPtr, "could not get resource info", (char *) NULL);
1152			return TCL_ERROR;
1153		}
1154		if (releaseIt) {
1155			ReleaseResource(resourceH);
1156		}
1157	} else {
1158		Tcl_AppendStringsToObj(resultPtr, "could not find resource", (char *) NULL);
1159		return TCL_ERROR;
1160	}
1161}
1162
1163
1164// ----------------------------------------------------------------------
1165//
1166// TclResCmd_Open --
1167//
1168//	This procedure is invoked to process the [resource open] Tcl command.
1169//	See the user documentation for details on what it does.
1170//
1171// Syntax:
1172// 		resource open ?(-datafork|-resourcefork)? fileName ?access?
1173//
1174// Results:
1175//	A standard Tcl result.
1176//
1177// Side effects:
1178//	See the user documentation.
1179//
1180// ----------------------------------------------------------------------
1181
1182int
1183TclResCmd_Open(
1184    ClientData clientData,		// Not used.
1185    Tcl_Interp *interp,			// Current interpreter.
1186    int objc,					// Number of arguments.
1187    Tcl_Obj *CONST objv[],		// Argument values.
1188    Tcl_Obj *resultPtr)			// Pointer to store the result.
1189{
1190    int		index, length, mode;
1191    int		fromFork = from_anyfork, foundFork = from_unspecified, filenameIdx = 2;
1192	Boolean permSpecified = false, isDir = false, gotParentRef = false;
1193	short	refnum;
1194    char *	stringPtr;
1195	char *	native;
1196	char	resultStr[256];
1197    SInt8	macPermision = 0;
1198    FSSpec	fileSpec;
1199    FSRef	fileFSRef, parentFSRef;
1200    OSErr	err;
1201	CONST char * str;
1202	Tcl_DString dss, ds;
1203
1204    static CONST char *openSwitches[] = {
1205		"-datafork", "-resourcefork", (char *) NULL
1206    };
1207
1208    enum {
1209		RESOURCE_OPEN_DATAFORK, RESOURCE_OPEN_RESOURCEFORK
1210    };
1211
1212    if (!((objc == 3) || (objc == 4) || (objc == 5))) {
1213		Tcl_WrongNumArgs(interp, 2, objv, "?(-datafork|-resourcefork)? fileName ?permission?");
1214		return TCL_ERROR;
1215    }
1216
1217	// Parse the arguments
1218    if (objc != 3) {
1219		if (Tcl_GetIndexFromObj(interp, objv[2], openSwitches,
1220					"switch", 0, &index) == TCL_OK) {
1221			switch (index) {
1222				case RESOURCE_OPEN_DATAFORK:
1223					fromFork = from_datafork;
1224					break;
1225
1226				case RESOURCE_OPEN_RESOURCEFORK:
1227					fromFork = from_rezfork;
1228					break;
1229			}
1230			filenameIdx = 3;
1231			if (objc == 5) {
1232				permSpecified = true;
1233			}
1234		} else {
1235			if (objc == 5) {
1236				return TCL_ERROR;
1237			} else {
1238				filenameIdx = 2;
1239				permSpecified = true;
1240			}
1241		}
1242    }
1243
1244    str = Tcl_GetStringFromObj(objv[filenameIdx], &length);
1245    if (Tcl_TranslateFileName(interp, str, &ds) == NULL) {
1246		Tcl_AppendStringsToObj(resultPtr, "couldn't translate file name", (char *) NULL);
1247		return TCL_ERROR;
1248    }
1249    native = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), &dss);
1250
1251	// Get an FSRef
1252	err = FSPathMakeRef(native, &fileFSRef, &isDir);
1253    Tcl_DStringFree(&ds);
1254    if (err != noErr && err != fnfErr) {
1255		Tcl_AppendStringsToObj(resultPtr, "couldn't get file ref from path", (char *) NULL);
1256		return TCL_ERROR;
1257    }
1258    if (isDir) {
1259		Tcl_AppendStringsToObj(resultPtr, "specified path is a directory", (char *) NULL);
1260		return TCL_ERROR;
1261    }
1262
1263	if (err == fnfErr) {
1264		// Build an FSSpec manually with the parent folder (which must exist) and the name
1265		char * 			separatorPtr;
1266		FSCatalogInfo 	catalogInfo;
1267
1268		separatorPtr = strrchr(native, TCLRESOURCE_PATH_SEP);
1269		if (separatorPtr) {
1270			native[separatorPtr-native] = 0;
1271			err = FSPathMakeRef(native, &parentFSRef, &isDir);
1272			err = FSGetCatalogInfo(&parentFSRef, kFSCatInfoNodeID | kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
1273			if (err != noErr) {
1274				Tcl_AppendStringsToObj(resultPtr, "invalid parent folder", (char *) NULL);
1275				return TCL_ERROR;
1276			} else {
1277				gotParentRef = true;
1278			}
1279			fileSpec.vRefNum = catalogInfo.volume;
1280			fileSpec.parID = catalogInfo.nodeID;
1281			CopyCStringToPascal(separatorPtr+1, fileSpec.name);
1282			err = fnfErr;
1283		}
1284	} else {
1285		// Get the FSSpec from the FSRef
1286		err = FSGetCatalogInfo(&fileFSRef, kFSCatInfoNone, NULL, NULL, &fileSpec, NULL);
1287		if (err != noErr) {
1288			Tcl_AppendStringsToObj(resultPtr, "couldn't get file spec", (char *) NULL);
1289			return TCL_ERROR;
1290		}
1291	}
1292    Tcl_DStringFree(&dss);
1293
1294	// Get permissions for the file. We really only understand read-only and
1295	// shared-read-write. If no permissions are given, we default to read only.
1296    if (permSpecified) {
1297		stringPtr = Tcl_GetStringFromObj(objv[objc-1], &length);
1298 		mode = TclGetOpenMode(interp, stringPtr, &index);
1299		if (mode == -1) {
1300			// TODO: TclGetOpenMode doesn't work with Obj commands.
1301			Tcl_AppendStringsToObj(resultPtr, "invalid access mode '", stringPtr, "'", (char *) NULL);
1302			return TCL_ERROR;
1303		}
1304		switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
1305			case O_RDONLY:
1306			macPermision = fsRdPerm;
1307			break;
1308
1309			case O_WRONLY:
1310			case O_RDWR:
1311			macPermision = fsRdWrShPerm;
1312			break;
1313
1314			default:
1315			panic("TclResCmd_Open: invalid permission value");
1316			break;
1317		}
1318    } else {
1319		macPermision = fsRdPerm;
1320    }
1321
1322	// If path was invalid, don't try to open the resource map. If file
1323	// permission is fsRdWrShPerm we will try to create a new file.
1324	if (err == fnfErr) {
1325		refnum = kResFileNotOpened;
1326		goto openforkDone;
1327	}
1328
1329	// The opening functions below are enclosed between SetResLoad(false) and
1330	// SetResLoad(true) statements in order not to load in any of the
1331	// resources in the file: this could cause problems if you open a file
1332	// that has CODE resources...
1333	//
1334	// The following heuristic is applied:
1335	// - if we have from_rezfork or from_datafork, then only the
1336	//   corresponding fork is searched
1337	// - if it is from_anyfork, then we first look for resources in the data
1338	//   fork and, if this fails, we look for resources in the resource fork
1339
1340	if (fromFork != from_rezfork) {
1341		// Try to open the file as a datafork resource file
1342		SetResLoad(false);
1343		err = FSOpenResourceFile( &fileFSRef, 0, nil, macPermision, &refnum );
1344		SetResLoad(true);
1345		if (err == noErr) {
1346			foundFork = from_datafork;
1347			goto openforkDone;
1348		}
1349    }
1350    if (fromFork != from_datafork) {
1351		// Now try to open as a resourcefork resource file
1352		SetResLoad(false);
1353		refnum = FSpOpenResFile( &fileSpec, macPermision);
1354		SetResLoad(true);
1355		err = ResError();
1356		if (err == noErr) {
1357			foundFork = from_rezfork;
1358		}
1359    }
1360
1361    openforkDone:
1362	// If the functions opening the resource map failed and if the permission is
1363	// fsRdWrShPerm, try to create a new resource file.
1364    if (refnum == kResFileNotOpened) {
1365		if (((err == fnfErr) || (err == eofErr)) && (macPermision == fsRdWrShPerm)) {
1366			// Create the resource fork now.
1367			switch (fromFork) {
1368
1369				case from_rezfork:
1370				HCreateResFile(fileSpec.vRefNum, fileSpec.parID, fileSpec.name);
1371				refnum = FSpOpenResFile(&fileSpec, macPermision);
1372				break;
1373
1374				default: {
1375					CONST Tcl_UniChar *	uniString;
1376					FSSpec 				parentSpec;
1377					int 				numChars;
1378
1379					if (!gotParentRef) {
1380						// Get FSRef of parent
1381						CInfoPBRec	pb;
1382						Str255		dirName;
1383
1384						pb.dirInfo.ioNamePtr = dirName;
1385						pb.dirInfo.ioVRefNum = fileSpec.vRefNum;
1386						pb.dirInfo.ioDrParID = fileSpec.parID;
1387						pb.dirInfo.ioFDirIndex = -1;	// Info about directory
1388						if ( pb.dirInfo.ioDrDirID != fsRtDirID ) {
1389							pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
1390							err = PBGetCatInfo( &pb, false);
1391							if ( err == noErr ) {
1392								BlockMoveData(dirName, parentSpec.name, dirName[0]+1);
1393								parentSpec.vRefNum = fileSpec.vRefNum;
1394								parentSpec.parID = pb.dirInfo.ioDrParID;
1395							}
1396						}
1397						err = FSpMakeFSRef( &parentSpec, &parentFSRef );
1398						if (err != noErr) {
1399							Tcl_AppendStringsToObj(resultPtr,
1400										   "couldn't get parent's ref", (char *) NULL);
1401							return TCL_ERROR;
1402						}
1403					}
1404					// Get Unicode name
1405					Tcl_DStringInit(&ds);
1406					Tcl_ExternalToUtfDString(NULL, (CONST char *) fileSpec.name + 1, fileSpec.name[0], &ds);
1407
1408					Tcl_DStringInit(&dss);
1409					uniString = Tcl_UtfToUniCharDString(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), &dss);
1410					numChars = Tcl_DStringLength(&dss) / sizeof(Tcl_UniChar);
1411					Tcl_DStringFree(&ds);
1412					Tcl_DStringFree(&dss);
1413
1414					err = FSCreateResourceFile(&parentFSRef, numChars, uniString, kFSCatInfoNone,
1415									 NULL, 0, NULL, &fileFSRef, &fileSpec);
1416					if (err == noErr) {
1417						err = FSOpenResourceFile( &fileFSRef, 0, NULL, macPermision, &refnum );
1418					}
1419					break;
1420				}
1421			}
1422			if (refnum == kResFileNotOpened) {
1423				goto openError;
1424			} else {
1425				foundFork = fromFork;
1426			}
1427		} else if (err == fnfErr) {
1428			Tcl_AppendStringsToObj(resultPtr,
1429			"file does not exist", (char *) NULL);
1430			return TCL_ERROR;
1431		} else if (err == eofErr || err == mapReadErr) {
1432			switch (fromFork) {
1433				case from_rezfork:
1434				Tcl_AppendStringsToObj(resultPtr,
1435					"file does not contain resources in the resource fork", (char *) NULL);
1436				break;
1437
1438				case from_datafork:
1439				Tcl_AppendStringsToObj(resultPtr,
1440					"file does not contain resources in the data fork", (char *) NULL);
1441				break;
1442
1443				default: {
1444				Tcl_AppendStringsToObj(resultPtr,
1445					"file does not contain resources in any fork", (char *) NULL);
1446				break;
1447				}
1448			}
1449			return TCL_ERROR;
1450		} else {
1451			openError:
1452			sprintf(resultStr, "error %d opening resource file", err);
1453			Tcl_AppendStringsToObj(resultPtr, resultStr, (char *) NULL);
1454			return TCL_ERROR;
1455		}
1456    }
1457
1458	// The FspOpenResFile function does not set the ResFileAttrs.
1459	// Even if you open the file read only, the mapReadOnly attribute is not
1460	// set. This means we can't detect writes to a read only resource fork
1461	// until the write fails, which is bogus. So set it here...
1462    if (macPermision == fsRdPerm) {
1463		SetResFileAttrs(refnum, mapReadOnly);
1464    }
1465
1466	Tcl_SetStringObj(resultPtr, "", 0);
1467    if (TclRes_RegisterResourceFork(refnum, resultPtr, foundFork, fork_CheckIfOpen) != TCL_OK) {
1468		CloseResFile(refnum);
1469		return TCL_ERROR;
1470    }
1471
1472    return TCL_OK;
1473}
1474
1475
1476// ----------------------------------------------------------------------
1477//
1478// TclResCmd_Read --
1479//
1480//	This procedure is invoked to process the [resource read] Tcl command.
1481//	See the user documentation for details on what it does.
1482//
1483// Syntax:
1484// 		resource read resourceType resourceId ?resourceRef?
1485//
1486// Results:
1487//	A standard Tcl result.
1488//
1489// Side effects:
1490//	See the user documentation.
1491//
1492// ----------------------------------------------------------------------
1493
1494int
1495TclResCmd_Read(
1496    ClientData clientData,		// Not used.
1497    Tcl_Interp *interp,			// Current interpreter.
1498    int objc,					// Number of arguments.
1499    Tcl_Obj *CONST objv[],		// Argument values.
1500    Tcl_Obj *resultPtr)			// Pointer to store the result.
1501{
1502	short	rsrcId = 0;
1503	long	theLong, size;
1504    int		length, releaseIt = 0;
1505    char *	resmapRef;
1506    char *	resourceName = NULL;
1507    Handle	resourceH = NULL;
1508    OSType	rezType;
1509
1510	if (!((objc == 4) || (objc == 5))) {
1511		Tcl_WrongNumArgs(interp, 2, objv, "resourceType resourceID ?resourceRef?");
1512		return TCL_ERROR;
1513	}
1514
1515	if (TclRes_GetOSTypeFromObj(interp, objv[2], &rezType) != TCL_OK) {
1516		return TCL_ERROR;
1517	}
1518
1519	if (Tcl_GetLongFromObj((Tcl_Interp *) NULL, objv[3], &theLong) != TCL_OK) {
1520		resourceName = Tcl_GetStringFromObj(objv[3], &length);
1521	} else {
1522		rsrcId = (short) theLong;
1523	}
1524
1525	if (objc == 5) {
1526		resmapRef = Tcl_GetStringFromObj(objv[4], &length);
1527	} else {
1528		resmapRef = NULL;
1529	}
1530
1531	resourceH = TclRes_FindResource(interp, rezType, resourceName,
1532									rsrcId, resmapRef, &releaseIt);
1533
1534	if (resourceH != NULL) {
1535		size = GetResourceSizeOnDisk(resourceH);
1536		Tcl_SetByteArrayObj(resultPtr, (unsigned char *) *resourceH, size);
1537
1538		// Don't release the resource unless WE loaded it...
1539		if (releaseIt) {
1540			ReleaseResource(resourceH);
1541		}
1542		return TCL_OK;
1543	} else {
1544		Tcl_AppendStringsToObj(resultPtr, "could not load resource", (char *) NULL);
1545		return TCL_ERROR;
1546	}
1547}
1548
1549
1550// ----------------------------------------------------------------------
1551//
1552// TclResCmd_Types --
1553//
1554//	This procedure is invoked to process the [resource types] Tcl command.
1555//	See the user documentation for details on what it does.
1556//
1557// Syntax:
1558// 		resource types ?resourceRef?
1559//
1560// Results:
1561//	A standard Tcl result.
1562//
1563// Side effects:
1564//	See the user documentation.
1565//
1566// ----------------------------------------------------------------------
1567
1568int
1569TclResCmd_Types(
1570    ClientData clientData,		// Not used.
1571    Tcl_Interp *interp,			// Current interpreter.
1572    int objc,					// Number of arguments.
1573    Tcl_Obj *CONST objv[],		// Argument values.
1574    Tcl_Obj *resultPtr)			// Pointer to store the result.
1575{
1576	OpenResourceFork * resourceRef;
1577	Tcl_Obj *	objPtr;
1578	int			i, count, result, limitSearch;
1579	short		saveRef = 0;
1580	OSType		rezType;
1581
1582	result = TCL_OK;
1583	limitSearch = false;
1584
1585	if (!((objc == 2) || (objc == 3))) {
1586		Tcl_WrongNumArgs(interp, 2, objv, "?resourceRef?");
1587		return TCL_ERROR;
1588	}
1589
1590	if (objc == 3) {
1591		resourceRef = TclRes_GetResourceRefFromObj(objv[2], 1, "get types of", resultPtr);
1592		if (resourceRef == NULL) {
1593			return TCL_ERROR;
1594		}
1595		saveRef = CurResFile();
1596		UseResFile(resourceRef->fileRef);
1597		limitSearch = true;
1598	}
1599
1600	if (limitSearch) {
1601		count = Count1Types();
1602	} else {
1603		count = CountTypes();
1604	}
1605
1606	for (i = 1; i <= count; i++) {
1607		if (limitSearch) {
1608			Get1IndType((ResType *) &rezType, i);
1609		} else {
1610			GetIndType((ResType *) &rezType, i);
1611		}
1612		objPtr = TclRes_NewOSTypeObj(rezType);
1613		result = Tcl_ListObjAppendElement(interp, resultPtr, objPtr);
1614		if (result != TCL_OK) {
1615			Tcl_DecrRefCount(objPtr);
1616			break;
1617		}
1618	}
1619
1620	if (limitSearch) {
1621		UseResFile(saveRef);
1622	}
1623
1624	return result;
1625}
1626
1627
1628// ----------------------------------------------------------------------
1629//
1630// TclResCmd_Update --
1631//
1632//	This procedure is invoked to process the [resource update] Tcl command.
1633//	See the user documentation for details on what it does.
1634//
1635// Syntax:
1636// 		resource update resourceRef
1637//
1638// Results:
1639//	A standard Tcl result.
1640//
1641// Side effects:
1642//	See the user documentation.
1643//
1644// ----------------------------------------------------------------------
1645
1646int
1647TclResCmd_Update(
1648    ClientData clientData,		// Not used.
1649    Tcl_Interp *interp,			// Current interpreter.
1650    int objc,					// Number of arguments.
1651    Tcl_Obj *CONST objv[],		// Argument values.
1652    Tcl_Obj *resultPtr)			// Pointer to store the result.
1653{
1654	OpenResourceFork * resourceRef;
1655	char	buffer[128];
1656	OSErr	err;
1657
1658	if (objc != 3) {
1659		Tcl_WrongNumArgs(interp, 2, objv, "resourceRef");
1660		return TCL_ERROR;
1661	}
1662
1663	resourceRef = TclRes_GetResourceRefFromObj(objv[2], true, "update", resultPtr);
1664	if (resourceRef == NULL) {
1665		return TCL_ERROR;
1666	}
1667
1668	if (resourceRef->fileRef >= 0) {
1669		UpdateResFile(resourceRef->fileRef);
1670		err = ResError();
1671		if (err != noErr) {
1672			sprintf(buffer, "error %d updating resource map", err);
1673			Tcl_AppendStringsToObj(resultPtr, buffer, (char *) NULL);
1674			return TCL_ERROR;
1675		}
1676		return TCL_OK;
1677	} else {
1678		Tcl_AppendStringsToObj(resultPtr, "invalid file ref", (char *) NULL);
1679		return TCL_ERROR;
1680	}
1681}
1682
1683
1684// ----------------------------------------------------------------------
1685//
1686// TclResCmd_Write --
1687//
1688//	This procedure is invoked to process the [resource write] Tcl command.
1689//	See the user documentation for details on what it does.
1690//
1691// Syntax:
1692// 		resource write ?options? resourceType data
1693//
1694// Results:
1695//	A standard Tcl result.
1696//
1697// Side effects:
1698//	See the user documentation.
1699//
1700// ----------------------------------------------------------------------
1701
1702int
1703TclResCmd_Write(
1704    ClientData clientData,		// Not used.
1705    Tcl_Interp *interp,			// Current interpreter.
1706    int objc,					// Number of arguments.
1707    Tcl_Obj *CONST objv[],		// Argument values.
1708    Tcl_Obj *resultPtr)			// Pointer to store the result.
1709{
1710    int		index, result, gotResID, releaseIt = 0, force;
1711    int		i, limitSearch, length;
1712	short	rsrcId = 0;
1713	long	theLong;
1714    short	saveRef = 0;
1715    char *	bytesPtr;
1716    char *	resourceName = NULL;
1717    char	errbuf[16];
1718    OpenResourceFork * resourceRef = NULL;
1719    Handle	resourceH = NULL;
1720    OSErr	err;
1721    Str255	theName;
1722    OSType	rezType;
1723
1724	static CONST char *writeSwitches[] = {
1725		"-id", "-name", "-file", "-force", "-datafork", (char *) NULL
1726	};
1727
1728	enum {
1729		RESOURCE_WRITE_ID, RESOURCE_WRITE_NAME, RESOURCE_WRITE_FILE,
1730		RESOURCE_WRITE_FORCE, RESOURCE_WRITE_DATAFORK
1731	};
1732
1733	result = TCL_OK;
1734	limitSearch = false;
1735
1736	if ((objc < 4) || (objc > 11)) {
1737		Tcl_WrongNumArgs(interp, 2, objv,
1738						 "?-id resourceID? ?-name resourceName? ?-file resourceRef? ?-force? resourceType data");
1739		return TCL_ERROR;
1740	}
1741
1742	i = 2;
1743	gotResID = false;
1744	theName[0] = 0;
1745	limitSearch = false;
1746	force = 0;
1747
1748	while (i < (objc - 2)) {
1749		if (Tcl_GetIndexFromObj(interp, objv[i], writeSwitches, "switch", 0, &index) != TCL_OK) {
1750			return TCL_ERROR;
1751		}
1752
1753		switch (index) {
1754
1755			case RESOURCE_WRITE_ID:
1756			if (Tcl_GetLongFromObj(interp, objv[i+1], &theLong) != TCL_OK) {
1757				return TCL_ERROR;
1758			}
1759			rsrcId = (short) theLong;
1760			gotResID = true;
1761			i += 2;
1762			break;
1763
1764			case RESOURCE_WRITE_NAME: {
1765			resourceName = Tcl_GetStringFromObj(objv[i+1], &length);
1766			strcpy((char *) theName, resourceName);
1767			i += 2;
1768			break;
1769			}
1770
1771
1772			case RESOURCE_WRITE_FILE:
1773			resourceRef = TclRes_GetResourceRefFromObj(objv[i+1], 0, "write to", resultPtr);
1774			if (resourceRef == NULL) {
1775				return TCL_ERROR;
1776			}
1777			limitSearch = true;
1778			i += 2;
1779			break;
1780
1781			case RESOURCE_WRITE_FORCE:
1782			force = 1;
1783			i += 1;
1784			break;
1785		}
1786	}
1787	if (TclRes_GetOSTypeFromObj(interp, objv[i], &rezType) != TCL_OK) {
1788		return TCL_ERROR;
1789	}
1790	bytesPtr = (char *) Tcl_GetByteArrayFromObj(objv[i+1], &length);
1791
1792	resourceName = (char *) theName;
1793	c2pstr(resourceName);
1794
1795	if (limitSearch) {
1796		saveRef = CurResFile();
1797		UseResFile(resourceRef->fileRef);
1798	}
1799	if (gotResID == false) {
1800		if (limitSearch) {
1801			rsrcId = Unique1ID(rezType);
1802		} else {
1803			rsrcId = UniqueID(rezType);
1804		}
1805	}
1806
1807	// If we are adding the resource by number, then we must make sure
1808	// there is not already a resource of that number. We are not going
1809	// load it here, since we want to detect whether we loaded it or
1810	// not. Remember that releasing some resources, in particular menu
1811	// related ones, can be fatal.
1812	if (gotResID == true) {
1813		SetResLoad(false);
1814		resourceH = Get1Resource(rezType,rsrcId);
1815		SetResLoad(true);
1816	}
1817
1818	if (resourceH == NULL) {
1819		// We get into this branch either if there was not already a
1820		// resource of this type and ID, or the ID was not specified.
1821		resourceH = NewHandle(length);
1822		if (resourceH == NULL) {
1823			resourceH = NewHandle(length);
1824			if (resourceH == NULL) {
1825				panic("could not allocate memory to write resource");
1826			}
1827		}
1828		HLock(resourceH);
1829		memcpy(*resourceH, bytesPtr, length);
1830		HUnlock(resourceH);
1831		AddResource(resourceH, rezType, rsrcId, (StringPtr) resourceName);
1832		releaseIt = 1;
1833	} else {
1834		// We got here because there was a resource of this type and ID in the file.
1835		if (*resourceH == NULL) {
1836			releaseIt = 1;
1837		} else {
1838			releaseIt = 0;
1839		}
1840
1841		if (!force) {
1842			// We only overwrite existant resources when the -force flag has been set.
1843			sprintf(errbuf,"%d", rsrcId);
1844
1845			Tcl_AppendStringsToObj(resultPtr, "the resource ", errbuf,
1846				" already exists, use the \"-force\" option to overwrite it.", (char *) NULL);
1847			result = TCL_ERROR;
1848			goto writeDone;
1849		} else if (GetResAttrs(resourceH) & resProtected) {
1850			// If it is protected
1851			sprintf(errbuf,"%d", rsrcId);
1852			Tcl_AppendStringsToObj(resultPtr,
1853								   "could not write resource id ",
1854								   errbuf, " of type ",
1855								   Tcl_GetStringFromObj(objv[i],&length),
1856								   ", it was protected.",(char *) NULL);
1857		   result = TCL_ERROR;
1858		   goto writeDone;
1859	   } else {
1860			// Be careful, the resource might already be in memory if something else loaded it.
1861			if (*resourceH == 0) {
1862				LoadResource(resourceH);
1863				err = ResError();
1864				if (err != noErr) {
1865					sprintf(errbuf,"%d", rsrcId);
1866					Tcl_AppendStringsToObj(resultPtr,
1867										   "error loading resource ",
1868										   errbuf, " of type ",
1869										   Tcl_GetStringFromObj(objv[i],&length),
1870										   " to overwrite it", (char *) NULL);
1871				   goto writeDone;
1872			   }
1873		   }
1874
1875			SetHandleSize(resourceH, length);
1876			if ( MemError() != noErr ) {
1877				panic("could not allocate memory to write resource");
1878			}
1879
1880			HLock(resourceH);
1881			memcpy(*resourceH, bytesPtr, length);
1882			HUnlock(resourceH);
1883
1884			ChangedResource(resourceH);
1885
1886			// We also may have changed the name...
1887			SetResInfo(resourceH, rsrcId, (StringPtr) resourceName);
1888		}
1889	}
1890
1891	err = ResError();
1892	if (err != noErr) {
1893		Tcl_AppendStringsToObj(resultPtr, "error adding resource to resource map", (char *) NULL);
1894		result = TCL_ERROR;
1895		goto writeDone;
1896	}
1897
1898	WriteResource(resourceH);
1899	err = ResError();
1900	if (err != noErr) {
1901		Tcl_AppendStringsToObj(resultPtr, "error writing resource to disk", (char *) NULL);
1902		result = TCL_ERROR;
1903	}
1904
1905writeDone:
1906	if (releaseIt) {
1907		ReleaseResource(resourceH);
1908		err = ResError();
1909		if (err != noErr) {
1910			Tcl_GetStringFromObj(resultPtr, &length);
1911			if (length == 0) {
1912				Tcl_AppendStringsToObj(resultPtr, "error releasing resource", (char *) NULL);
1913			}
1914			result = TCL_ERROR;
1915		}
1916	}
1917
1918	if (limitSearch) {
1919		UseResFile(saveRef);
1920	}
1921
1922	return result;
1923}
1924
1925
1926/****************
1927*               *
1928*   Utilities   *
1929*               *
1930****************/
1931
1932
1933// ----------------------------------------------------------------------
1934//
1935// TclRes_InitializeTables --
1936//
1937//	Initialize the structures used for resource management.
1938//
1939// Results:
1940//	None.
1941//
1942// Side effects:
1943//	Read the code.
1944//
1945// ----------------------------------------------------------------------
1946
1947void
1948TclRes_InitializeTables()
1949{
1950	initialized = 1;
1951	Tcl_InitHashTable(&nameTable, TCL_STRING_KEYS);
1952	Tcl_InitHashTable(&resourceTable, TCL_ONE_WORD_KEYS);
1953	resourceForkList = Tcl_NewObj();
1954	Tcl_IncrRefCount(resourceForkList);
1955
1956	TclRes_BuildResourceForkList();
1957}
1958
1959
1960// -----------------------------------------------------------------------------
1961//
1962// TclRes_FindResource --
1963//
1964//	Higher level interface for loading resources.
1965//
1966// Side Effects:
1967//	Attempts to load a resource.
1968//
1969// Results:
1970//  A handle on success.
1971//
1972// -----------------------------------------------------------------------------
1973
1974Handle
1975TclRes_FindResource(
1976	Tcl_Interp *interp,			// Interpreter in which to process file.
1977	long resourceType,			// Type of resource to load.
1978	CONST char *resourceName,	// Name of resource to find,
1979								//   NULL if number should be used.
1980	int resourceNumber,			// Resource id of source.
1981	CONST char *resFileRef,		// Registered resource file reference,
1982								//   NULL if searching all open resource files.
1983	int *releaseIt)				// Should we release this resource when done.
1984{
1985	OpenResourceFork *	resourceRef;
1986	Tcl_HashEntry *		nameHashPtr;
1987	int					limitSearch = false;
1988	short				saveRef = 0;
1989	Handle				resourceH;
1990
1991	if (resFileRef != NULL) {
1992		nameHashPtr = Tcl_FindHashEntry(&nameTable, resFileRef);
1993		if (nameHashPtr == NULL) {
1994			Tcl_AppendResult(interp, "invalid resource file reference \"", resFileRef, "\"", (char *) NULL);
1995			return NULL;
1996		}
1997		resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr);
1998		saveRef = CurResFile();
1999		UseResFile(resourceRef->fileRef);
2000		limitSearch = true;
2001	}
2002
2003	// Some system resources (for example system resources) should not
2004	// be released.  So we set autoload to false, and try to get the resource.
2005	// If the Master Pointer of the returned handle is null, then resource was
2006	// not in memory, and it is safe to release it.  Otherwise, it is not.
2007	SetResLoad(false);
2008
2009	if (resourceName == NULL) {
2010		if (limitSearch) {
2011			resourceH = Get1Resource(resourceType, resourceNumber);
2012		} else {
2013			resourceH = GetResource(resourceType, resourceNumber);
2014		}
2015	} else {
2016		Str255 rezName;
2017		Tcl_DString ds;
2018		Tcl_UtfToExternalDString(NULL, resourceName, -1, &ds);
2019		strcpy((char *) rezName + 1, Tcl_DStringValue(&ds));
2020		rezName[0] = (unsigned) Tcl_DStringLength(&ds);
2021		if (limitSearch) {
2022			resourceH = Get1NamedResource(resourceType, rezName);
2023		} else {
2024			resourceH = GetNamedResource(resourceType, rezName);
2025		}
2026		Tcl_DStringFree(&ds);
2027	}
2028
2029	if (resourceH != NULL && *resourceH == NULL) {
2030		*releaseIt = 1;
2031		LoadResource(resourceH);
2032	} else {
2033		*releaseIt = 0;
2034	}
2035
2036	SetResLoad(true);
2037
2038	if (limitSearch) {
2039		UseResFile(saveRef);
2040	}
2041
2042	return resourceH;
2043}
2044
2045
2046// ----------------------------------------------------------------------
2047//
2048// TclRes_GetResourceRefFromObj --
2049//
2050//	Given a String object containing a resource file token, return
2051//	the OpenResourceFork structure that it represents, or NULL if
2052//	the token cannot be found.  If okayOnReadOnly is false, it will
2053//      also check whether the token corresponds to a read-only file,
2054//      and return NULL if it is.
2055//
2056// Results:
2057//	A pointer to an OpenResourceFork structure, or NULL.
2058//
2059// Side effects:
2060//	An error message may be left in resultPtr.
2061//
2062// ----------------------------------------------------------------------
2063
2064OpenResourceFork *
2065TclRes_GetResourceRefFromObj(
2066	register Tcl_Obj *objPtr,	// String obj containing file token
2067	int okayOnReadOnly,         // Whether this operation is okay for a
2068								//   read only file.
2069	const char *operation,      // String containing the operation we were
2070								//   trying to perform, used for errors
2071	Tcl_Obj *resultPtr)         // Tcl_Obj to contain error message
2072{
2073	OpenResourceFork *	resourceRef;
2074	char *				stringPtr;
2075	Tcl_HashEntry *		nameHashPtr;
2076	int					length;
2077	OSErr				err;
2078
2079	stringPtr = Tcl_GetStringFromObj(objPtr, &length);
2080	nameHashPtr = Tcl_FindHashEntry(&nameTable, stringPtr);
2081	if (nameHashPtr == NULL) {
2082		Tcl_AppendStringsToObj(resultPtr,
2083			"invalid resource file reference \"", stringPtr, "\"", (char *) NULL);
2084		return NULL;
2085	}
2086
2087	resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr);
2088
2089	if (!okayOnReadOnly) {
2090		err = GetResFileAttrs(resourceRef->fileRef);
2091		if (err & mapReadOnly) {
2092			Tcl_AppendStringsToObj(resultPtr, "cannot ", operation, " resource file \"",
2093								   stringPtr, "\", it was opened read only", (char *) NULL);
2094			return NULL;
2095		}
2096	}
2097	return resourceRef;
2098}
2099
2100
2101// ----------------------------------------------------------------------
2102//
2103// TclRes_RegisterResourceFork --
2104//
2105//	Register an open resource fork in the table of open resources
2106//	managed by the procedures in this file.  If the resource file
2107//  is already registered with the table, then no new token is made.
2108//
2109//  The behavior is controlled by the value of tokenPtr, and of the
2110//	flags variable.
2111//
2112//	For tokenPtr, the possibilities are:
2113//	  * NULL: the new token is auto-generated, but not returned.
2114//    * The string value of tokenPtr is the empty string: then
2115//		the new token is auto-generated, and returned in tokenPtr.
2116//	  * tokenPtr has a value: the string value will be used for the token,
2117//		unless it is already in use, in which case a new token will
2118//		be generated, and returned in tokenPtr.
2119//
2120//  For the flags variable, it can be one of:
2121//	  * fork_InsertTail: the element is inserted at the
2122//              end of the list of open resources.  Used only in Resource_Init.
2123//	  * fork_dontclose: the [resource close] command will not close
2124//	        this resource.
2125//	  * fork_CheckIfOpen: this will check to see if this file's
2126//	        resource fork is already opened by this Tcl shell, and return
2127//	        an error without registering the resource fork.
2128//
2129// Results:
2130//	Standard Tcl Result
2131//
2132// Side effects:
2133//	An entry may be added to the resource name table.
2134//
2135// ----------------------------------------------------------------------
2136
2137int
2138TclRes_RegisterResourceFork(
2139	short fileRef,        	// File ref for an open resource fork.
2140	Tcl_Obj * tokenPtr,		// A Tcl Object to which to write the new token
2141	int whichFork, 			// The fork in which the resource map has been found
2142	int flags)	     		// 1 means insert at the head of the resource
2143							//    fork list, 0 means at the tail
2144{
2145	OpenResourceFork *	resourceRef;
2146	Tcl_HashEntry *		resourceHashPtr = NULL;
2147	Tcl_HashEntry *		nameHashPtr;
2148	char *				resourceId = NULL;
2149	int 				new;
2150
2151	if (!initialized) {
2152		TclRes_InitializeTables();
2153	}
2154
2155	// If we were asked to, check that this file has not been opened
2156	// already with a different permission. If it has, then return an error.
2157	new = 1;
2158	if (flags & fork_CheckIfOpen) {
2159		Tcl_HashSearch	search;
2160		short			oldFileRef, filePermissionFlag;
2161		FCBPBRec		newFileRec, oldFileRec;
2162		OSErr			err;
2163
2164		oldFileRec.ioCompletion = NULL;
2165		oldFileRec.ioFCBIndx = 0;
2166		oldFileRec.ioNamePtr = NULL;
2167
2168		newFileRec.ioCompletion = NULL;
2169		newFileRec.ioFCBIndx = 0;
2170		newFileRec.ioNamePtr = NULL;
2171		newFileRec.ioVRefNum = 0;
2172		newFileRec.ioRefNum = fileRef;
2173		err = PBGetFCBInfo(&newFileRec, false);
2174		filePermissionFlag = ( newFileRec.ioFCBFlags >> 12 ) & 0x1;
2175
2176		resourceHashPtr = Tcl_FirstHashEntry(&resourceTable, &search);
2177		while (resourceHashPtr != NULL) {
2178			oldFileRef = (short) Tcl_GetHashKey(&resourceTable, resourceHashPtr);
2179			if (oldFileRef == fileRef) {
2180				new = 0;
2181				break;
2182			}
2183			oldFileRec.ioVRefNum = 0;
2184			oldFileRec.ioRefNum = oldFileRef;
2185			err = PBGetFCBInfo(&oldFileRec, false);
2186
2187			// err might not be noErr either because the file has closed
2188			// out from under us somehow, which is bad but we're not going
2189			// to fix it here, OR because it is the ROM MAP, which has a
2190			// fileRef, but can't be gotten to by PBGetFCBInfo.
2191			if ((err == noErr)
2192				&& (newFileRec.ioFCBVRefNum == oldFileRec.ioFCBVRefNum)
2193				&& (newFileRec.ioFCBFlNm == oldFileRec.ioFCBFlNm)) {
2194				// In MacOS 8.1 it seems like we get different file refs even
2195				// though we pass the same file & permissions.  This is not
2196				// what Inside Mac says should happen, but it does, so if it
2197				// does, then close the new res file and return the original one...
2198				if (filePermissionFlag == ((oldFileRec.ioFCBFlags >> 12) & 0x1)) {
2199					CloseResFile(fileRef);
2200					new = 0;
2201					break;
2202				} else {
2203					if (tokenPtr != NULL) {
2204						Tcl_SetStringObj(tokenPtr, "resource already opened with different permission", -1);
2205					}
2206					return TCL_ERROR;
2207				}
2208			}
2209			resourceHashPtr = Tcl_NextHashEntry(&search);
2210		}
2211	}
2212
2213	// If the file has already been opened with these same permissions, then
2214	// it will be in our list and we will have set new to 0 above. So we will
2215	// just return the token (if tokenPtr is non-null).
2216	if (new) {
2217		resourceHashPtr = Tcl_CreateHashEntry(&resourceTable, (char *) fileRef, &new);
2218	} else {
2219		if (tokenPtr != NULL) {
2220			resourceId = (char *) Tcl_GetHashValue(resourceHashPtr);
2221			Tcl_SetStringObj(tokenPtr, resourceId, -1);
2222		}
2223		return TCL_OK;
2224	}
2225
2226	// If we were passed in a result pointer which is not an empty string,
2227	// attempt to use that as the key. If the key already exists, silently
2228	// fall back on "resource%d"...
2229	if (tokenPtr != NULL) {
2230		char *	tokenVal;
2231		int		length;
2232		tokenVal = Tcl_GetStringFromObj(tokenPtr, &length);
2233		if (length > 0) {
2234			nameHashPtr = Tcl_FindHashEntry(&nameTable, tokenVal);
2235			if (nameHashPtr == NULL) {
2236				resourceId = ckalloc(length + 1);
2237				memcpy(resourceId, tokenVal, length);
2238				resourceId[length] = '\0';
2239			}
2240		}
2241	}
2242
2243	if (resourceId == NULL) {
2244		resourceId = (char *) ckalloc(15);
2245		sprintf(resourceId, "resource%d", newId);
2246	}
2247
2248	Tcl_SetHashValue(resourceHashPtr, resourceId);
2249	newId++;
2250
2251	nameHashPtr = Tcl_CreateHashEntry(&nameTable, resourceId, &new);
2252	if (!new) {
2253		panic("resource id has repeated itself");
2254	}
2255
2256	resourceRef = (OpenResourceFork *) ckalloc(sizeof(OpenResourceFork));
2257	resourceRef->fileRef = fileRef;
2258	resourceRef->fileFork = whichFork;
2259	resourceRef->flags = flags;
2260
2261	Tcl_SetHashValue(nameHashPtr, (ClientData) resourceRef);
2262	if (tokenPtr != NULL) {
2263		Tcl_SetStringObj(tokenPtr, resourceId, -1);
2264	}
2265
2266	if (flags & fork_InsertTail) {
2267		Tcl_ListObjAppendElement(NULL, resourceForkList, tokenPtr);
2268	} else {
2269		Tcl_ListObjReplace(NULL, resourceForkList, 0, 0, 1, &tokenPtr);
2270	}
2271	return TCL_OK;
2272}
2273
2274
2275// ----------------------------------------------------------------------
2276//
2277// TclRes_UnRegisterResourceFork --
2278//
2279//	Removes the entry for an open resource fork from the table of
2280//	open resources managed by the procedures in this file.
2281//      If resultPtr is not NULL, it will be used for error reporting.
2282//
2283// Results:
2284//	The fileRef for this token, or -1 if an error occured.
2285//
2286// Side effects:
2287//	An entry is removed from the resource name table.
2288//
2289// ----------------------------------------------------------------------
2290
2291short
2292TclRes_UnRegisterResourceFork(
2293	char * tokenPtr,
2294	Tcl_Obj * resultPtr)
2295
2296{
2297	OpenResourceFork *	resourceRef;
2298	Tcl_HashEntry *		resourceHashPtr;
2299	Tcl_HashEntry *		nameHashPtr;
2300	Tcl_Obj **			elemPtrs;
2301	short				fileRef;
2302	char *				bytes;
2303	int 				i, match = 0, index, listLen, length, elemLen;
2304
2305	nameHashPtr = Tcl_FindHashEntry(&nameTable, tokenPtr);
2306	if (nameHashPtr == NULL) {
2307		if (resultPtr != NULL) {
2308			Tcl_AppendStringsToObj(resultPtr, "invalid resource file reference \"",
2309								   tokenPtr, "\"", (char *) NULL);
2310		}
2311		return -1;
2312	}
2313
2314	resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr);
2315	fileRef = resourceRef->fileRef;
2316
2317	if ( resourceRef->flags & fork_DontClose ) {
2318		if (resultPtr != NULL) {
2319			Tcl_AppendStringsToObj(resultPtr, "not allowed to close \"",
2320								   tokenPtr, "\" resource file", (char *) NULL);
2321		}
2322		return -1;
2323	}
2324
2325	Tcl_DeleteHashEntry(nameHashPtr);
2326	ckfree((char *) resourceRef);
2327
2328	// Now remove the resource from the resourceForkList object
2329	Tcl_ListObjGetElements(NULL, resourceForkList, &listLen, &elemPtrs);
2330	index = -1;
2331	length = strlen(tokenPtr);
2332
2333	for (i = 0; i < listLen; i++) {
2334		match = 0;
2335		bytes = Tcl_GetStringFromObj(elemPtrs[i], &elemLen);
2336		if (length == elemLen) {
2337			match = (memcmp(bytes, tokenPtr, (size_t) length) == 0);
2338		}
2339		if (match) {
2340			index = i;
2341			break;
2342		}
2343	}
2344	if (!match) {
2345		panic("the resource Fork List is out of synch!");
2346	}
2347
2348	Tcl_ListObjReplace(NULL, resourceForkList, index, 1, 0, NULL);
2349
2350	resourceHashPtr = Tcl_FindHashEntry(&resourceTable, (char *) fileRef);
2351	if (resourceHashPtr == NULL) {
2352		panic("Resource & Name tables are out of synch in resource command.");
2353	}
2354	ckfree(Tcl_GetHashValue(resourceHashPtr));
2355	Tcl_DeleteHashEntry(resourceHashPtr);
2356
2357	return fileRef;
2358}
2359
2360
2361// ----------------------------------------------------------------------
2362//
2363// TclRes_BuildResourceForkList --
2364//
2365//	Traverses the list of open resource forks, and builds the
2366//	list of resources forks.  Also creates a resource token for any that
2367//      are opened but not registered with our resource system.
2368//      This is based on code from Apple DTS.
2369//
2370//	This code had to be redefined on OSX because some low-memory
2371//	accessor functions it used in its OS8/9 incarnation are
2372//	now obsolete (LMGetTopMapHndl and LMGetSysMapHndl). Using
2373//	GetTopResourceFile() and GetNextResourceFile() instead.
2374//
2375// Results:
2376//	None.
2377//
2378// Side effects:
2379//      The list of resource forks is updated.
2380//	The resource name table may be augmented.
2381//
2382// ----------------------------------------------------------------------
2383
2384void
2385TclRes_BuildResourceForkList()
2386{
2387	FCBPBRec	fileRec;
2388	char		fileName[256];
2389	char *		s;
2390	Tcl_Obj *	nameObj;
2391	OSErr		err;
2392	FSSpec				fileSpec;
2393	SInt16				curRefNum, nextRefNum;
2394#ifndef TCLRESOURCE_DONT_USE_CARBON
2395	char		appName[256];
2396	ProcessSerialNumber psn;
2397	ProcessInfoRec		info;
2398
2399	// Get the application name, so we can substitute
2400	// the token "application" for the application's resource.
2401	GetCurrentProcess(&psn);
2402	info.processInfoLength = sizeof(ProcessInfoRec);
2403	info.processName = (StringPtr) &appName;
2404	info.processAppSpec = &fileSpec;
2405	GetProcessInformation(&psn, &info);
2406	p2cstr((StringPtr) appName);
2407#endif
2408
2409	fileRec.ioCompletion = NULL;
2410	fileRec.ioVRefNum = 0;
2411	fileRec.ioFCBIndx = 0;
2412	fileRec.ioNamePtr = (StringPtr) &fileName;
2413
2414	err = GetTopResourceFile(&nextRefNum);
2415
2416	if (err==noErr) {
2417		while (nextRefNum != 0) {
2418			curRefNum = nextRefNum;
2419
2420			// Now do the ones opened after the application
2421			nameObj = Tcl_NewObj();
2422
2423			fileRec.ioRefNum = curRefNum;
2424			err = PBGetFCBInfo(&fileRec, false);
2425
2426			if (err == noErr) {
2427				p2cstr((StringPtr) fileName);
2428				// Strip rsrc extension: for bundled applications, the main resource
2429				// fork is named after the name of the app followed by this extension.
2430				s = strrchr(fileName,'.');
2431				if (s != NULL && strcmp(s+1,"rsrc") == 0) {
2432					*s = 0;
2433				}
2434#ifndef TCLRESOURCE_DONT_USE_CARBON
2435				if (strcmp(fileName,appName) == 0) {
2436					Tcl_SetStringObj(nameObj, "application", -1);
2437				} else
2438#endif
2439				{
2440					Tcl_SetStringObj(nameObj, fileName, -1);
2441				}
2442				c2pstr(fileName);
2443			}
2444
2445			TclRes_RegisterResourceFork(fileRec.ioRefNum, nameObj,
2446									   from_unspecified, fork_DontClose | fork_InsertTail);
2447
2448			GetNextResourceFile(curRefNum, &nextRefNum);
2449		}
2450	}
2451}
2452
2453
2454// ----------------------------------------------------------------------
2455//
2456// TclRes_NewOSTypeObj --
2457//
2458//	This procedure is used to create a new resource name type object.
2459//
2460// Results:
2461//	The newly created object is returned. This object will have a NULL
2462//	string representation. The returned object has ref count 0.
2463//
2464// Side effects:
2465//	None.
2466//
2467// ----------------------------------------------------------------------
2468
2469Tcl_Obj *
2470TclRes_NewOSTypeObj(
2471	OSType newOSType)		// Int used to initialize the new object
2472{
2473	register Tcl_Obj *objPtr;
2474
2475	if (!osTypeInit) {
2476		osTypeInit = 1;
2477		Tcl_RegisterObjType(&osType);
2478	}
2479
2480	objPtr = Tcl_NewObj();
2481	objPtr->bytes = NULL;
2482	objPtr->internalRep.longValue = newOSType;
2483	objPtr->typePtr = &osType;
2484	return objPtr;
2485}
2486
2487
2488// ----------------------------------------------------------------------
2489//
2490// TclRes_SetOSTypeObj --
2491//
2492//	Modify an object to be a resource type and to have the
2493//	specified long value.
2494//
2495// Results:
2496//	None.
2497//
2498// Side effects:
2499//	The object's old string rep, if any, is freed. Also, any old
2500//	internal rep is freed.
2501//
2502// ----------------------------------------------------------------------
2503
2504void
2505TclRes_SetOSTypeObj(
2506	Tcl_Obj *objPtr,		// Object whose internal rep to init.
2507	OSType newOSType)		// Integer used to set object's value.
2508{
2509	register Tcl_ObjType *oldTypePtr = objPtr->typePtr;
2510
2511	if (!osTypeInit) {
2512		osTypeInit = 1;
2513		Tcl_RegisterObjType(&osType);
2514	}
2515
2516	if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
2517		oldTypePtr->freeIntRepProc(objPtr);
2518	}
2519
2520	objPtr->internalRep.longValue = newOSType;
2521	objPtr->typePtr = &osType;
2522
2523	Tcl_InvalidateStringRep(objPtr);
2524}
2525
2526
2527// ----------------------------------------------------------------------
2528//
2529// TclRes_GetOSTypeFromObj --
2530//
2531//	Attempt to return an int from the Tcl object "objPtr". If the object
2532//	is not already an int, an attempt will be made to convert it to one.
2533//
2534// Results:
2535//	The return value is a standard Tcl object result. If an error occurs
2536//	during conversion, an error message is left in interp->objResult
2537//	unless "interp" is NULL.
2538//
2539// Side effects:
2540//	If the object is not already an int, the conversion will free
2541//	any old internal representation.
2542//
2543// ----------------------------------------------------------------------
2544
2545int
2546TclRes_GetOSTypeFromObj(
2547	Tcl_Interp *interp, 	// Used for error reporting if not NULL
2548	Tcl_Obj *objPtr,		// The object from which to get a int
2549	OSType *osTypePtr)		// Place to store resulting int
2550{
2551	register int result;
2552
2553	if (!osTypeInit) {
2554		osTypeInit = 1;
2555		Tcl_RegisterObjType(&osType);
2556	}
2557
2558	if (objPtr->typePtr == &osType) {
2559		*osTypePtr = objPtr->internalRep.longValue;
2560		return TCL_OK;
2561	}
2562
2563	result = TclRes_SetOSTypeFromAny(interp, objPtr);
2564	if (result == TCL_OK) {
2565		*osTypePtr = objPtr->internalRep.longValue;
2566	}
2567	return result;
2568}
2569
2570
2571// ----------------------------------------------------------------------
2572//
2573// TclRes_DupOSTypeInternalRep --
2574//
2575//	Initialize the internal representation of an int Tcl_Obj to a
2576//	copy of the internal representation of an existing int object.
2577//
2578// Results:
2579//	None.
2580//
2581// Side effects:
2582//	"copyPtr"s internal rep is set to the integer corresponding to
2583//	"srcPtr"s internal rep.
2584//
2585// ----------------------------------------------------------------------
2586
2587static void
2588TclRes_DupOSTypeInternalRep(
2589	Tcl_Obj *srcPtr,	// Object with internal rep to copy
2590	Tcl_Obj *copyPtr)	// Object with internal rep to set
2591{
2592	copyPtr->internalRep.longValue = srcPtr->internalRep.longValue;
2593	copyPtr->typePtr = &osType;
2594}
2595
2596
2597// ----------------------------------------------------------------------
2598//
2599// TclRes_SetOSTypeFromAny --
2600//
2601//	Attempt to generate an integer internal form for the Tcl object
2602//	"objPtr".
2603//
2604// Results:
2605//	The return value is a standard object Tcl result. If an error occurs
2606//	during conversion, an error message is left in interp->objResult
2607//	unless "interp" is NULL.
2608//
2609// Side effects:
2610//	If no error occurs, an int is stored as "objPtr"s internal
2611//	representation.
2612//
2613// ----------------------------------------------------------------------
2614
2615static int
2616TclRes_SetOSTypeFromAny(
2617	Tcl_Interp *interp,		// Used for error reporting if not NULL
2618	Tcl_Obj *objPtr)		// The object to convert
2619{
2620	Tcl_ObjType *oldTypePtr = objPtr->typePtr;
2621	char *string;
2622	int length;
2623	OSType newOSType = 0UL;
2624	Tcl_DString ds;
2625
2626	// Get the string representation. Make it up-to-date if necessary.
2627	string = Tcl_GetStringFromObj(objPtr, &length);
2628	Tcl_UtfToExternalDString(NULL, string, length, &ds);
2629
2630	if (Tcl_DStringLength(&ds) > sizeof(OSType)) {
2631		if (interp != NULL) {
2632			Tcl_ResetResult(interp);
2633			Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "expected Macintosh OS type but got \"", string, "\"", (char *) NULL);
2634		}
2635		Tcl_DStringFree(&ds);
2636		return TCL_ERROR;
2637	}
2638	memcpy(&newOSType, Tcl_DStringValue(&ds), (size_t) Tcl_DStringLength(&ds));
2639	Tcl_DStringFree(&ds);
2640
2641	// The conversion to resource type succeeded. Free the old internalRep
2642	// before setting the new one.
2643	if ((oldTypePtr != NULL) &&	(oldTypePtr->freeIntRepProc != NULL)) {
2644		oldTypePtr->freeIntRepProc(objPtr);
2645	}
2646
2647	objPtr->internalRep.longValue = newOSType;
2648	objPtr->typePtr = &osType;
2649	return TCL_OK;
2650}
2651
2652
2653// ----------------------------------------------------------------------
2654//
2655// TclRes_UpdateStringOfOSType --
2656//
2657//	Update the string representation for an resource type object.
2658//	Note: This procedure does not free an existing old string rep
2659//	so storage will be lost if this has not already been done.
2660//
2661// Results:
2662//	None.
2663//
2664// Side effects:
2665//	The object's string is set to a valid string that results from
2666//	the int-to-string conversion.
2667//
2668// ----------------------------------------------------------------------
2669
2670static void
2671TclRes_UpdateStringOfOSType(
2672	register Tcl_Obj *objPtr)	// Int object whose string rep to update.
2673{
2674	char string[sizeof(OSType)+1];
2675	Tcl_DString ds;
2676
2677	memcpy(string, &(objPtr->internalRep.longValue), sizeof(OSType));
2678	string[sizeof(OSType)] = '\0';
2679	Tcl_ExternalToUtfDString(NULL, string, -1, &ds);
2680	objPtr->bytes = ckalloc(Tcl_DStringLength(&ds) + 1);
2681	memcpy(objPtr->bytes, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds) + 1);
2682	objPtr->length = Tcl_DStringLength(&ds);
2683	Tcl_DStringFree(&ds);
2684}
2685
2686
2687