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