tclMacResource.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:62k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /*
  2.  * tclMacResource.c --
  3.  *
  4.  * This file contains several commands that manipulate or use
  5.  * Macintosh resources.  Included are extensions to the "source"
  6.  * command, the mac specific "beep" and "resource" commands, and
  7.  * administration for open resource file references.
  8.  *
  9.  * Copyright (c) 1996-1997 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * RCS: @(#) $Id: tclMacResource.c,v 1.14.2.1 2003/10/01 14:34:16 das Exp $
  15.  */
  16. #include <Errors.h>
  17. #include <FSpCompat.h>
  18. #include <Processes.h>
  19. #include <Resources.h>
  20. #include <Sound.h>
  21. #include <Strings.h>
  22. #include <Traps.h>
  23. #include <LowMem.h>
  24. #include "FullPath.h"
  25. #include "tcl.h"
  26. #include "tclInt.h"
  27. #include "tclMac.h"
  28. #include "tclMacInt.h"
  29. #include "tclMacPort.h"
  30. /*
  31.  * This flag tells the RegisterResource function to insert the
  32.  * resource into the tail of the resource fork list.  Needed only
  33.  * Resource_Init.
  34.  */
  35.  
  36. #define TCL_RESOURCE_INSERT_TAIL 1
  37. /*
  38.  * 2 is taken by TCL_RESOURCE_DONT_CLOSE
  39.  * which is the only public flag to TclMacRegisterResourceFork.
  40.  */
  41.  
  42. #define TCL_RESOURCE_CHECK_IF_OPEN 4
  43. /*
  44.  * Pass this in the mode parameter of SetSoundVolume to determine
  45.  * which volume to set.
  46.  */
  47. enum WhichVolume {
  48.     SYS_BEEP_VOLUME,    /* This sets the volume for SysBeep calls */ 
  49.     DEFAULT_SND_VOLUME, /* This one for SndPlay calls */
  50.     RESET_VOLUME        /* And this undoes the last call to SetSoundVolume */
  51. };
  52.  
  53. /*
  54.  * Hash table to track open resource files.
  55.  */
  56. typedef struct OpenResourceFork {
  57.     short fileRef;
  58.     int   flags;
  59. } OpenResourceFork;
  60. static Tcl_HashTable nameTable; /* Id to process number mapping. */
  61. static Tcl_HashTable resourceTable; /* Process number to id mapping. */
  62. static Tcl_Obj *resourceForkList;       /* Ordered list of resource forks */
  63. static int appResourceIndex;            /* This is the index of the application*
  64.  * in the list of resource forks */
  65. static int newId = 0; /* Id source. */
  66. static int initialized = 0; /* 0 means static structures haven't 
  67.  * been initialized yet. */
  68. static int osTypeInit = 0; /* 0 means Tcl object of osType hasn't 
  69.  * been initialized yet. */
  70. /*
  71.  * Prototypes for procedures defined later in this file:
  72.  */
  73. static void DupOSTypeInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr,
  74.     Tcl_Obj *copyPtr));
  75. static void ResourceInit _ANSI_ARGS_((void));
  76. static void             BuildResourceForkList _ANSI_ARGS_((void));
  77. static int SetOSTypeFromAny _ANSI_ARGS_((Tcl_Interp *interp,
  78.     Tcl_Obj *objPtr));
  79. static void UpdateStringOfOSType _ANSI_ARGS_((Tcl_Obj *objPtr));
  80. static OpenResourceFork* GetRsrcRefFromObj _ANSI_ARGS_((Tcl_Obj *objPtr,
  81.                 int okayOnReadOnly, const char *operation,
  82.                         Tcl_Obj *resultPtr));
  83. static void  SetSoundVolume(int volume, enum WhichVolume mode);
  84. /*
  85.  * The structures below defines the Tcl object type defined in this file by
  86.  * means of procedures that can be invoked by generic object code.
  87.  */
  88. static Tcl_ObjType osType = {
  89.     "ostype", /* name */
  90.     (Tcl_FreeInternalRepProc *) NULL,   /* freeIntRepProc */
  91.     DupOSTypeInternalRep,         /* dupIntRepProc */
  92.     UpdateStringOfOSType, /* updateStringProc */
  93.     SetOSTypeFromAny /* setFromAnyProc */
  94. };
  95. /*
  96.  *----------------------------------------------------------------------
  97.  *
  98.  * Tcl_ResourceObjCmd --
  99.  *
  100.  * This procedure is invoked to process the "resource" Tcl command.
  101.  * See the user documentation for details on what it does.
  102.  *
  103.  * Results:
  104.  * A standard Tcl result.
  105.  *
  106.  * Side effects:
  107.  * See the user documentation.
  108.  *
  109.  *----------------------------------------------------------------------
  110.  */
  111. int
  112. Tcl_ResourceObjCmd(
  113.     ClientData clientData, /* Not used. */
  114.     Tcl_Interp *interp, /* Current interpreter. */
  115.     int objc, /* Number of arguments. */
  116.     Tcl_Obj *CONST objv[]) /* Argument values. */
  117. {
  118.     Tcl_Obj *resultPtr, *objPtr;
  119.     int index, result;
  120.     long fileRef, rsrcId;
  121.     FSSpec fileSpec;
  122.     char *stringPtr;
  123.     char errbuf[16];
  124.     OpenResourceFork *resourceRef;
  125.     Handle resource = NULL;
  126.     OSErr err;
  127.     int count, i, limitSearch = false, length;
  128.     short id, saveRef, resInfo;
  129.     Str255 theName;
  130.     OSType rezType;
  131.     int gotInt, releaseIt = 0, force;
  132.     char *resourceId = NULL;
  133.     long size;
  134.     char macPermision;
  135.     int mode;
  136.     static CONST char *switches[] = {"close", "delete" ,"files", "list", 
  137.             "open", "read", "types", "write", (char *) NULL
  138.     };
  139.         
  140.     enum {
  141.             RESOURCE_CLOSE, RESOURCE_DELETE, RESOURCE_FILES, RESOURCE_LIST, 
  142.             RESOURCE_OPEN, RESOURCE_READ, RESOURCE_TYPES, RESOURCE_WRITE
  143.     };
  144.               
  145.     static CONST char *writeSwitches[] = {
  146.             "-id", "-name", "-file", "-force", (char *) NULL
  147.     };
  148.             
  149.     enum {
  150.             RESOURCE_WRITE_ID, RESOURCE_WRITE_NAME, 
  151.             RESOURCE_WRITE_FILE, RESOURCE_FORCE
  152.     };
  153.             
  154.     static CONST char *deleteSwitches[] = {"-id", "-name", "-file", (char *) NULL};
  155.              
  156.     enum {RESOURCE_DELETE_ID, RESOURCE_DELETE_NAME, RESOURCE_DELETE_FILE};
  157.     resultPtr = Tcl_GetObjResult(interp);
  158.     
  159.     if (objc < 2) {
  160. Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
  161. return TCL_ERROR;
  162.     }
  163.     if (Tcl_GetIndexFromObj(interp, objv[1], switches, "option", 0, &index)
  164.     != TCL_OK) {
  165. return TCL_ERROR;
  166.     }
  167.     if (!initialized) {
  168. ResourceInit();
  169.     }
  170.     result = TCL_OK;
  171.     switch (index) {
  172. case RESOURCE_CLOSE:
  173.     if (objc != 3) {
  174. Tcl_WrongNumArgs(interp, 2, objv, "resourceRef");
  175. return TCL_ERROR;
  176.     }
  177.     stringPtr = Tcl_GetStringFromObj(objv[2], &length);
  178.     fileRef = TclMacUnRegisterResourceFork(stringPtr, resultPtr);
  179.     
  180.     if (fileRef >= 0) {
  181.         CloseResFile((short) fileRef);
  182.         return TCL_OK;
  183.     } else {
  184.         return TCL_ERROR;
  185.     }
  186. case RESOURCE_DELETE:
  187.     if (!((objc >= 3) && (objc <= 9) && ((objc % 2) == 1))) {
  188. Tcl_WrongNumArgs(interp, 2, objv, 
  189.     "?-id resourceId? ?-name resourceName? ?-file 
  190. resourceRef? resourceType");
  191. return TCL_ERROR;
  192.     }
  193.     
  194.     i = 2;
  195.     fileRef = -1;
  196.     gotInt = false;
  197.     resourceId = NULL;
  198.     limitSearch = false;
  199.     while (i < (objc - 2)) {
  200. if (Tcl_GetIndexFromObj(interp, objv[i], deleteSwitches,
  201. "option", 0, &index) != TCL_OK) {
  202.     return TCL_ERROR;
  203. }
  204. switch (index) {
  205.     case RESOURCE_DELETE_ID:
  206. if (Tcl_GetLongFromObj(interp, objv[i+1], &rsrcId)
  207. != TCL_OK) {
  208.     return TCL_ERROR;
  209. }
  210. gotInt = true;
  211. break;
  212.     case RESOURCE_DELETE_NAME:
  213. resourceId = Tcl_GetStringFromObj(objv[i+1], &length);
  214. if (length > 255) {
  215.     Tcl_AppendStringsToObj(resultPtr,"-name argument ",
  216.             "too long, must be < 255 characters",
  217.             (char *) NULL);
  218.     return TCL_ERROR;
  219. }
  220. strcpy((char *) theName, resourceId);
  221. resourceId = (char *) theName;
  222. c2pstr(resourceId);
  223. break;
  224.     case RESOURCE_DELETE_FILE:
  225.         resourceRef = GetRsrcRefFromObj(objv[i+1], 0, 
  226.                 "delete from", resultPtr);
  227.         if (resourceRef == NULL) {
  228.             return TCL_ERROR;
  229.         }
  230. limitSearch = true;
  231. break;
  232. }
  233. i += 2;
  234.     }
  235.     
  236.     if ((resourceId == NULL) && !gotInt) {
  237. Tcl_AppendStringsToObj(resultPtr,"you must specify either ",
  238.         ""-id" or "-name" or both ",
  239.         "to "resource delete"",
  240.         (char *) NULL);
  241.         return TCL_ERROR;
  242.             }
  243.     if (Tcl_GetOSTypeFromObj(interp, objv[i], &rezType) != TCL_OK) {
  244. return TCL_ERROR;
  245.     }
  246.     if (limitSearch) {
  247. saveRef = CurResFile();
  248. UseResFile((short) resourceRef->fileRef);
  249.     }
  250.     
  251.     SetResLoad(false);
  252.     
  253.     if (gotInt == true) {
  254.         if (limitSearch) {
  255.     resource = Get1Resource(rezType, rsrcId);
  256. } else {
  257.     resource = GetResource(rezType, rsrcId);
  258. }
  259.                 err = ResError();
  260.             
  261.                 if (err == resNotFound || resource == NULL) {
  262.             Tcl_AppendStringsToObj(resultPtr, "resource not found",
  263.                 (char *) NULL);
  264.             result = TCL_ERROR;
  265.             goto deleteDone;               
  266.                 } else if (err != noErr) {
  267.                     char buffer[16];
  268.                 
  269.                     sprintf(buffer, "%12d", err);
  270.             Tcl_AppendStringsToObj(resultPtr, "resource error #",
  271.                     buffer, "occured while trying to find resource",
  272.                     (char *) NULL);
  273.             result = TCL_ERROR;
  274.             goto deleteDone;               
  275.         }
  276.     } 
  277.     
  278.     if (resourceId != NULL) {
  279.         Handle tmpResource;
  280.         if (limitSearch) {
  281.             tmpResource = Get1NamedResource(rezType,
  282.     (StringPtr) resourceId);
  283.         } else {
  284.             tmpResource = GetNamedResource(rezType,
  285.     (StringPtr) resourceId);
  286.         }
  287.                 err = ResError();
  288.             
  289.                 if (err == resNotFound || tmpResource == NULL) {
  290.             Tcl_AppendStringsToObj(resultPtr, "resource not found",
  291.                 (char *) NULL);
  292.             result = TCL_ERROR;
  293.             goto deleteDone;               
  294.                 } else if (err != noErr) {
  295.                     char buffer[16];
  296.                 
  297.                     sprintf(buffer, "%12d", err);
  298.             Tcl_AppendStringsToObj(resultPtr, "resource error #",
  299.                     buffer, "occured while trying to find resource",
  300.                     (char *) NULL);
  301.             result = TCL_ERROR;
  302.             goto deleteDone;               
  303.         }
  304.         
  305.         if (gotInt) { 
  306.             if (resource != tmpResource) {
  307.                 Tcl_AppendStringsToObj(resultPtr,
  308. ""-id" and "-name" ",
  309.                         "values do not point to the same resource",
  310.                         (char *) NULL);
  311.                 result = TCL_ERROR;
  312.                 goto deleteDone;
  313.             }
  314.         } else {
  315.             resource = tmpResource;
  316.         }
  317.     }
  318.         
  319.             resInfo = GetResAttrs(resource);
  320.     
  321.     if ((resInfo & resProtected) == resProtected) {
  322.         Tcl_AppendStringsToObj(resultPtr, "resource ",
  323.                 "cannot be deleted: it is protected.",
  324.                 (char *) NULL);
  325.         result = TCL_ERROR;
  326.         goto deleteDone;               
  327.     } else if ((resInfo & resSysHeap) == resSysHeap) {   
  328.         Tcl_AppendStringsToObj(resultPtr, "resource",
  329.                 "cannot be deleted: it is in the system heap.",
  330.                 (char *) NULL);
  331.         result = TCL_ERROR;
  332.         goto deleteDone;               
  333.     }
  334.     
  335.     /*
  336.      * Find the resource file, if it was not specified,
  337.      * so we can flush the changes now.  Perhaps this is
  338.      * a little paranoid, but better safe than sorry.
  339.      */
  340.      
  341.     RemoveResource(resource);
  342.     
  343.     if (!limitSearch) {
  344.         UpdateResFile(HomeResFile(resource));
  345.     } else {
  346.         UpdateResFile(resourceRef->fileRef);
  347.     }
  348.     
  349.     
  350.     deleteDone:
  351.     
  352.             SetResLoad(true);
  353.     if (limitSearch) {
  354.                  UseResFile(saveRef);                        
  355.     }
  356.     return result;
  357.     
  358. case RESOURCE_FILES:
  359.     if ((objc < 2) || (objc > 3)) {
  360. Tcl_SetStringObj(resultPtr,
  361.         "wrong # args: should be "resource files 
  362. ?resourceId?"", -1);
  363. return TCL_ERROR;
  364.     }
  365.     
  366.     if (objc == 2) {
  367.         stringPtr = Tcl_GetStringFromObj(resourceForkList, &length);
  368.         Tcl_SetStringObj(resultPtr, stringPtr, length);
  369.     } else {
  370.                 FCBPBRec fileRec;
  371.                 Handle pathHandle;
  372.                 short pathLength;
  373.                 Str255 fileName;
  374.                 Tcl_DString dstr;
  375.         
  376.         if (strcmp(Tcl_GetString(objv[2]), "ROM Map") == 0) {
  377.             Tcl_SetStringObj(resultPtr,"no file path for ROM Map", -1);
  378.             return TCL_ERROR;
  379.         }
  380.         
  381.         resourceRef = GetRsrcRefFromObj(objv[2], 1, "files", resultPtr);
  382.         if (resourceRef == NULL) {
  383.             return TCL_ERROR;
  384.         }
  385.                 fileRec.ioCompletion = NULL;
  386.                 fileRec.ioFCBIndx = 0;
  387.                 fileRec.ioNamePtr = fileName;
  388.                 fileRec.ioVRefNum = 0;
  389.                 fileRec.ioRefNum = resourceRef->fileRef;
  390.                 err = PBGetFCBInfo(&fileRec, false);
  391.                 if (err != noErr) {
  392.                     Tcl_SetStringObj(resultPtr,
  393.                             "could not get FCB for resource file", -1);
  394.                     return TCL_ERROR;
  395.                 }
  396.                 
  397.                 err = GetFullPath(fileRec.ioFCBVRefNum, fileRec.ioFCBParID,
  398.                         fileRec.ioNamePtr, &pathLength, &pathHandle);
  399.                 if ( err != noErr) {
  400.                     Tcl_SetStringObj(resultPtr,
  401.                             "could not get file path from token", -1);
  402.                     return TCL_ERROR;
  403.                 }
  404.                 
  405.                 HLock(pathHandle);
  406.                 Tcl_ExternalToUtfDString(NULL, *pathHandle, pathLength, &dstr);
  407.                 
  408.                 Tcl_SetStringObj(resultPtr, Tcl_DStringValue(&dstr), Tcl_DStringLength(&dstr));
  409.                 HUnlock(pathHandle);
  410.                 DisposeHandle(pathHandle);
  411.                 Tcl_DStringFree(&dstr);
  412.             }                         
  413.     return TCL_OK;
  414. case RESOURCE_LIST:
  415.     if (!((objc == 3) || (objc == 4))) {
  416. Tcl_WrongNumArgs(interp, 2, objv, "resourceType ?resourceRef?");
  417. return TCL_ERROR;
  418.     }
  419.     if (Tcl_GetOSTypeFromObj(interp, objv[2], &rezType) != TCL_OK) {
  420. return TCL_ERROR;
  421.     }
  422.     if (objc == 4) {
  423.         resourceRef = GetRsrcRefFromObj(objv[3], 1, 
  424.                 "list", resultPtr);
  425. if (resourceRef == NULL) {
  426.     return TCL_ERROR;
  427. }
  428. saveRef = CurResFile();
  429. UseResFile((short) resourceRef->fileRef);
  430. limitSearch = true;
  431.     }
  432.     Tcl_ResetResult(interp);
  433.     if (limitSearch) {
  434. count = Count1Resources(rezType);
  435.     } else {
  436. count = CountResources(rezType);
  437.     }
  438.     SetResLoad(false);
  439.     for (i = 1; i <= count; i++) {
  440. if (limitSearch) {
  441.     resource = Get1IndResource(rezType, i);
  442. } else {
  443.     resource = GetIndResource(rezType, i);
  444. }
  445. if (resource != NULL) {
  446.     GetResInfo(resource, &id, (ResType *) &rezType, theName);
  447.     if (theName[0] != 0) {
  448.         
  449. objPtr = Tcl_NewStringObj((char *) theName + 1,
  450. theName[0]);
  451.     } else {
  452. objPtr = Tcl_NewIntObj(id);
  453.     }
  454.     ReleaseResource(resource);
  455.     result = Tcl_ListObjAppendElement(interp, resultPtr,
  456.     objPtr);
  457.     if (result != TCL_OK) {
  458. Tcl_DecrRefCount(objPtr);
  459. break;
  460.     }
  461. }
  462.     }
  463.     SetResLoad(true);
  464.     if (limitSearch) {
  465. UseResFile(saveRef);
  466.     }
  467.     return TCL_OK;
  468. case RESOURCE_OPEN: {
  469.     Tcl_DString ds, buffer;
  470.     CONST char *str, *native;
  471.     int length;
  472.     
  473.     if (!((objc == 3) || (objc == 4))) {
  474. Tcl_WrongNumArgs(interp, 2, objv, "fileName ?permissions?");
  475. return TCL_ERROR;
  476.     }
  477.     str = Tcl_GetStringFromObj(objv[2], &length);
  478.     if (Tcl_TranslateFileName(interp, str, &buffer) == NULL) {
  479.         return TCL_ERROR;
  480.     }
  481.     native = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer),
  482.          Tcl_DStringLength(&buffer), &ds);
  483.     err = FSpLocationFromPath(Tcl_DStringLength(&ds), native, &fileSpec);
  484.     Tcl_DStringFree(&ds);
  485.     Tcl_DStringFree(&buffer);
  486.     if (!((err == noErr) || (err == fnfErr))) {
  487. Tcl_AppendStringsToObj(resultPtr, "invalid path", (char *) NULL);
  488. return TCL_ERROR;
  489.     }
  490.     /*
  491.      * Get permissions for the file.  We really only understand
  492.      * read-only and shared-read-write.  If no permissions are 
  493.      * given we default to read only.
  494.      */
  495.     
  496.     if (objc == 4) {
  497. stringPtr = Tcl_GetStringFromObj(objv[3], &length);
  498. mode = TclGetOpenMode(interp, stringPtr, &index);
  499. if (mode == -1) {
  500.     /* TODO: TclGetOpenMode doesn't work with Obj commands. */
  501.     return TCL_ERROR;
  502. }
  503. switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
  504.     case O_RDONLY:
  505. macPermision = fsRdPerm;
  506.     break;
  507.     case O_WRONLY:
  508.     case O_RDWR:
  509. macPermision = fsRdWrShPerm;
  510. break;
  511.     default:
  512. panic("Tcl_ResourceObjCmd: invalid mode value");
  513.     break;
  514. }
  515.     } else {
  516. macPermision = fsRdPerm;
  517.     }
  518.     
  519.     /*
  520.      * Don't load in any of the resources in the file, this could 
  521.      * cause problems if you open a file that has CODE resources...
  522.      */
  523.      
  524.     SetResLoad(false); 
  525.     fileRef = (long) FSpOpenResFileCompat(&fileSpec, macPermision);
  526.     SetResLoad(true);
  527.     
  528.     if (fileRef == -1) {
  529.      err = ResError();
  530. if (((err == fnfErr) || (err == eofErr)) &&
  531. (macPermision == fsRdWrShPerm)) {
  532.     /*
  533.      * No resource fork existed for this file.  Since we are
  534.      * opening it for writing we will create the resource fork
  535.      * now.
  536.      */
  537.      
  538.     HCreateResFile(fileSpec.vRefNum, fileSpec.parID,
  539.     fileSpec.name);
  540.     fileRef = (long) FSpOpenResFileCompat(&fileSpec,
  541.     macPermision);
  542.     if (fileRef == -1) {
  543. goto openError;
  544.     }
  545. } else if (err == fnfErr) {
  546.     Tcl_AppendStringsToObj(resultPtr,
  547. "file does not exist", (char *) NULL);
  548.     return TCL_ERROR;
  549. } else if (err == eofErr) {
  550.     Tcl_AppendStringsToObj(resultPtr,
  551. "file does not contain resource fork", (char *) NULL);
  552.     return TCL_ERROR;
  553. } else {
  554.     openError:
  555.     Tcl_AppendStringsToObj(resultPtr,
  556. "error opening resource file", (char *) NULL);
  557.     return TCL_ERROR;
  558. }
  559.     }
  560.          
  561.             /*
  562.              * The FspOpenResFile function does not set the ResFileAttrs.
  563.              * Even if you open the file read only, the mapReadOnly
  564.              * attribute is not set.  This means we can't detect writes to a 
  565.              * read only resource fork until the write fails, which is bogus.  
  566.              * So set it here...
  567.              */
  568.             
  569.             if (macPermision == fsRdPerm) {
  570.                 SetResFileAttrs(fileRef, mapReadOnly);
  571.             }
  572.             
  573.             Tcl_SetStringObj(resultPtr, "", 0);
  574.             if (TclMacRegisterResourceFork(fileRef, resultPtr, 
  575.                     TCL_RESOURCE_CHECK_IF_OPEN) != TCL_OK) {
  576.                 CloseResFile(fileRef);
  577. return TCL_ERROR;
  578.             }
  579.     return TCL_OK;
  580. }
  581. case RESOURCE_READ:
  582.     if (!((objc == 4) || (objc == 5))) {
  583. Tcl_WrongNumArgs(interp, 2, objv,
  584. "resourceType resourceId ?resourceRef?");
  585. return TCL_ERROR;
  586.     }
  587.     if (Tcl_GetOSTypeFromObj(interp, objv[2], &rezType) != TCL_OK) {
  588. return TCL_ERROR;
  589.     }
  590.     
  591.     if (Tcl_GetLongFromObj((Tcl_Interp *) NULL, objv[3], &rsrcId)
  592.     != TCL_OK) {
  593. resourceId = Tcl_GetStringFromObj(objv[3], &length);
  594.             }
  595.     if (objc == 5) {
  596. stringPtr = Tcl_GetStringFromObj(objv[4], &length);
  597.     } else {
  598. stringPtr = NULL;
  599.     }
  600.     resource = Tcl_MacFindResource(interp, rezType, resourceId,
  601. rsrcId, stringPtr, &releaseIt);
  602.     
  603.     if (resource != NULL) {
  604. size = GetResourceSizeOnDisk(resource);
  605. Tcl_SetByteArrayObj(resultPtr, (unsigned char *) *resource, size);
  606. /*
  607.  * Don't release the resource unless WE loaded it...
  608.  */
  609.  
  610. if (releaseIt) {
  611.     ReleaseResource(resource);
  612. }
  613. return TCL_OK;
  614.     } else {
  615. Tcl_AppendStringsToObj(resultPtr, "could not load resource",
  616.     (char *) NULL);
  617. return TCL_ERROR;
  618.     }
  619. case RESOURCE_TYPES:
  620.     if (!((objc == 2) || (objc == 3))) {
  621. Tcl_WrongNumArgs(interp, 2, objv, "?resourceRef?");
  622. return TCL_ERROR;
  623.     }
  624.     if (objc == 3) {
  625.         resourceRef = GetRsrcRefFromObj(objv[2], 1, 
  626.                 "get types of", resultPtr);
  627. if (resourceRef == NULL) {
  628.     return TCL_ERROR;
  629. }
  630. saveRef = CurResFile();
  631. UseResFile((short) resourceRef->fileRef);
  632. limitSearch = true;
  633.     }
  634.     if (limitSearch) {
  635. count = Count1Types();
  636.     } else {
  637. count = CountTypes();
  638.     }
  639.     for (i = 1; i <= count; i++) {
  640. if (limitSearch) {
  641.     Get1IndType((ResType *) &rezType, i);
  642. } else {
  643.     GetIndType((ResType *) &rezType, i);
  644. }
  645. objPtr = Tcl_NewOSTypeObj(rezType);
  646. result = Tcl_ListObjAppendElement(interp, resultPtr, objPtr);
  647. if (result != TCL_OK) {
  648.     Tcl_DecrRefCount(objPtr);
  649.     break;
  650. }
  651.     }
  652.     if (limitSearch) {
  653. UseResFile(saveRef);
  654.     }
  655.     return result;
  656. case RESOURCE_WRITE:
  657.     if ((objc < 4) || (objc > 11)) {
  658. Tcl_WrongNumArgs(interp, 2, objv, 
  659. "?-id resourceId? ?-name resourceName? ?-file resourceRef?
  660.  ?-force? resourceType data");
  661. return TCL_ERROR;
  662.     }
  663.     
  664.     i = 2;
  665.     gotInt = false;
  666.     resourceId = NULL;
  667.     limitSearch = false;
  668.     force = 0;
  669.     while (i < (objc - 2)) {
  670. if (Tcl_GetIndexFromObj(interp, objv[i], writeSwitches,
  671. "switch", 0, &index) != TCL_OK) {
  672.     return TCL_ERROR;
  673. }
  674. switch (index) {
  675.     case RESOURCE_WRITE_ID:
  676. if (Tcl_GetLongFromObj(interp, objv[i+1], &rsrcId)
  677. != TCL_OK) {
  678.     return TCL_ERROR;
  679. }
  680. gotInt = true;
  681.         i += 2;
  682. break;
  683.     case RESOURCE_WRITE_NAME:
  684. resourceId = Tcl_GetStringFromObj(objv[i+1], &length);
  685. strcpy((char *) theName, resourceId);
  686. resourceId = (char *) theName;
  687. c2pstr(resourceId);
  688.         i += 2;
  689. break;
  690.     case RESOURCE_WRITE_FILE:
  691.                 resourceRef = GetRsrcRefFromObj(objv[i+1], 0, 
  692.                         "write to", resultPtr);
  693.                         if (resourceRef == NULL) {
  694.                             return TCL_ERROR;
  695.         }
  696. limitSearch = true;
  697.         i += 2;
  698. break;
  699.     case RESOURCE_FORCE:
  700.         force = 1;
  701.         i += 1;
  702.         break;
  703. }
  704.     }
  705.     if (Tcl_GetOSTypeFromObj(interp, objv[i], &rezType) != TCL_OK) {
  706. return TCL_ERROR;
  707.     }
  708.     stringPtr = (char *) Tcl_GetByteArrayFromObj(objv[i+1], &length);
  709.     if (gotInt == false) {
  710. rsrcId = UniqueID(rezType);
  711.     }
  712.     if (resourceId == NULL) {
  713. resourceId = (char *) "p";
  714.     }
  715.     if (limitSearch) {
  716. saveRef = CurResFile();
  717. UseResFile((short) resourceRef->fileRef);
  718.     }
  719.     
  720.     /*
  721.      * If we are adding the resource by number, then we must make sure
  722.      * there is not already a resource of that number.  We are not going
  723.      * load it here, since we want to detect whether we loaded it or
  724.      * not.  Remember that releasing some resources in particular menu
  725.      * related ones, can be fatal.
  726.      */
  727.      
  728.     if (gotInt == true) {
  729.         SetResLoad(false);
  730.         resource = Get1Resource(rezType,rsrcId);
  731.         SetResLoad(true);
  732.     }     
  733.          
  734.     if (resource == NULL) {
  735.         /*
  736.          * We get into this branch either if there was not already a
  737.          * resource of this type & id, or the id was not specified.
  738.          */
  739.          
  740.         resource = NewHandle(length);
  741.         if (resource == NULL) {
  742.             resource = NewHandleSys(length);
  743.             if (resource == NULL) {
  744.                 panic("could not allocate memory to write resource");
  745.             }
  746.         }
  747.         HLock(resource);
  748.         memcpy(*resource, stringPtr, length);
  749.         HUnlock(resource);
  750.         AddResource(resource, rezType, (short) rsrcId,
  751.     (StringPtr) resourceId);
  752. releaseIt = 1;
  753.             } else {
  754.                 /* 
  755.                  * We got here because there was a resource of this type 
  756.                  * & ID in the file. 
  757.                  */ 
  758.                 
  759.                 if (*resource == NULL) {
  760.                     releaseIt = 1;
  761.                 } else {
  762.                     releaseIt = 0;
  763.                 }
  764.                
  765.                 if (!force) {
  766.                     /*
  767.                      *We only overwrite extant resources
  768.                      * when the -force flag has been set.
  769.                      */
  770.                      
  771.                     sprintf(errbuf,"%d", rsrcId);
  772.                   
  773.                     Tcl_AppendStringsToObj(resultPtr, "the resource ",
  774.                           errbuf, " already exists, use "-force"",
  775.                           " to overwrite it.", (char *) NULL);
  776.                     
  777.                     result = TCL_ERROR;
  778.                     goto writeDone;
  779.                 } else if (GetResAttrs(resource) & resProtected) {
  780.                     /*  
  781.                      *  
  782.                      * Next, check to see if it is protected...
  783.                      */
  784.                  
  785.                     sprintf(errbuf,"%d", rsrcId);
  786.                     Tcl_AppendStringsToObj(resultPtr,
  787.     "could not write resource id ",
  788.                             errbuf, " of type ",
  789.                             Tcl_GetStringFromObj(objv[i],&length),
  790.                             ", it was protected.",(char *) NULL);
  791.                     result = TCL_ERROR;
  792.                     goto writeDone;
  793.                 } else {
  794.                     /*
  795.                      * Be careful, the resource might already be in memory
  796.                      * if something else loaded it.
  797.                      */
  798.                      
  799.                     if (*resource == 0) {
  800.                      LoadResource(resource);
  801.                      err = ResError();
  802.                      if (err != noErr) {
  803.                             sprintf(errbuf,"%d", rsrcId);
  804.                             Tcl_AppendStringsToObj(resultPtr,
  805.     "error loading resource ",
  806.                                     errbuf, " of type ",
  807.                                     Tcl_GetStringFromObj(objv[i],&length),
  808.                                     " to overwrite it", (char *) NULL);
  809.                             goto writeDone;
  810.                      }
  811.                     }
  812.                      
  813.                     SetHandleSize(resource, length);
  814.                     if ( MemError() != noErr ) {
  815.                         panic("could not allocate memory to write resource");
  816.                     }
  817.                     HLock(resource);
  818.             memcpy(*resource, stringPtr, length);
  819.             HUnlock(resource);
  820.            
  821.                     ChangedResource(resource);
  822.                 
  823.                     /*
  824.                      * We also may have changed the name...
  825.                      */ 
  826.                  
  827.                     SetResInfo(resource, rsrcId, (StringPtr) resourceId);
  828.                 }
  829.             }
  830.             
  831.     err = ResError();
  832.     if (err != noErr) {
  833. Tcl_AppendStringsToObj(resultPtr,
  834. "error adding resource to resource map",
  835.         (char *) NULL);
  836. result = TCL_ERROR;
  837. goto writeDone;
  838.     }
  839.     
  840.     WriteResource(resource);
  841.     err = ResError();
  842.     if (err != noErr) {
  843. Tcl_AppendStringsToObj(resultPtr,
  844. "error writing resource to disk",
  845.         (char *) NULL);
  846. result = TCL_ERROR;
  847.     }
  848.     
  849.     writeDone:
  850.     
  851.     if (releaseIt) {
  852.         ReleaseResource(resource);
  853.         err = ResError();
  854.         if (err != noErr) {
  855.     Tcl_AppendStringsToObj(resultPtr,
  856.     "error releasing resource",
  857.             (char *) NULL);
  858.     result = TCL_ERROR;
  859.         }
  860.     }
  861.     
  862.     if (limitSearch) {
  863. UseResFile(saveRef);
  864.     }
  865.     return result;
  866. default:
  867.     panic("Tcl_GetIndexFromObj returned unrecognized option");
  868.     return TCL_ERROR; /* Should never be reached. */
  869.     }
  870. }
  871. /*
  872.  *----------------------------------------------------------------------
  873.  *
  874.  * Tcl_MacSourceObjCmd --
  875.  *
  876.  * This procedure is invoked to process the "source" Tcl command.
  877.  * See the user documentation for details on what it does.  In 
  878.  * addition, it supports sourceing from the resource fork of
  879.  * type 'TEXT'.
  880.  *
  881.  * Results:
  882.  * A standard Tcl result.
  883.  *
  884.  * Side effects:
  885.  * See the user documentation.
  886.  *
  887.  *----------------------------------------------------------------------
  888.  */
  889. int
  890. Tcl_MacSourceObjCmd(
  891.     ClientData dummy, /* Not used. */
  892.     Tcl_Interp *interp, /* Current interpreter. */
  893.     int objc, /* Number of arguments. */
  894.     Tcl_Obj *CONST objv[]) /* Argument objects. */
  895. {
  896.     char *errNum = "wrong # args: ";
  897.     char *errBad = "bad argument: ";
  898.     char *errStr;
  899.     char *fileName = NULL, *rsrcName = NULL;
  900.     long rsrcID = -1;
  901.     char *string;
  902.     int length;
  903.     if (objc < 2 || objc > 4)  {
  904.      errStr = errNum;
  905.      goto sourceFmtErr;
  906.     }
  907.     
  908.     if (objc == 2)  {
  909. return Tcl_FSEvalFile(interp, objv[1]);
  910.     }
  911.     
  912.     /*
  913.      * The following code supports a few older forms of this command
  914.      * for backward compatability.
  915.      */
  916.     string = Tcl_GetStringFromObj(objv[1], &length);
  917.     if (!strcmp(string, "-rsrc") || !strcmp(string, "-rsrcname")) {
  918. rsrcName = Tcl_GetStringFromObj(objv[2], &length);
  919.     } else if (!strcmp(string, "-rsrcid")) {
  920. if (Tcl_GetLongFromObj(interp, objv[2], &rsrcID) != TCL_OK) {
  921.     return TCL_ERROR;
  922. }
  923.     } else {
  924.      errStr = errBad;
  925.      goto sourceFmtErr;
  926.     }
  927.     
  928.     if (objc == 4) {
  929. fileName = Tcl_GetStringFromObj(objv[3], &length);
  930.     }
  931.     return Tcl_MacEvalResource(interp, rsrcName, rsrcID, fileName);
  932.     sourceFmtErr:
  933.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), errStr, "should be "",
  934. Tcl_GetString(objv[0]), " fileName" or "",
  935. Tcl_GetString(objv[0]), " -rsrc name ?fileName?" or "", 
  936. Tcl_GetString(objv[0]), " -rsrcid id ?fileName?"",
  937. (char *) NULL);
  938.     return TCL_ERROR;
  939. }
  940. /*
  941.  *----------------------------------------------------------------------
  942.  *
  943.  * Tcl_BeepObjCmd --
  944.  *
  945.  * This procedure makes the beep sound.
  946.  *
  947.  * Results:
  948.  * A standard Tcl result.
  949.  *
  950.  * Side effects:
  951.  * Makes a beep.
  952.  *
  953.  *----------------------------------------------------------------------
  954.  */
  955. int
  956. Tcl_BeepObjCmd(
  957.     ClientData dummy, /* Not used. */
  958.     Tcl_Interp *interp, /* Current interpreter. */
  959.     int objc, /* Number of arguments. */
  960.     Tcl_Obj *CONST objv[]) /* Argument values. */
  961. {
  962.     Tcl_Obj *resultPtr, *objPtr;
  963.     Handle sound;
  964.     Str255 sndName;
  965.     int volume = -1, length;
  966.     char * sndArg = NULL;
  967.     resultPtr = Tcl_GetObjResult(interp);
  968.     if (objc == 1) {
  969. SysBeep(1);
  970. return TCL_OK;
  971.     } else if (objc == 2) {
  972. if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-list")) {
  973.     int count, i;
  974.     short id;
  975.     Str255 theName;
  976.     ResType rezType;
  977.     count = CountResources('snd ');
  978.     for (i = 1; i <= count; i++) {
  979. sound = GetIndResource('snd ', i);
  980. if (sound != NULL) {
  981.     GetResInfo(sound, &id, &rezType, theName);
  982.     if (theName[0] == 0) {
  983. continue;
  984.     }
  985.     objPtr = Tcl_NewStringObj((char *) theName + 1,
  986.     theName[0]);
  987.     Tcl_ListObjAppendElement(interp, resultPtr, objPtr);
  988. }
  989.     }
  990.     return TCL_OK;
  991. } else {
  992.     sndArg = Tcl_GetStringFromObj(objv[1], &length);
  993. }
  994.     } else if (objc == 3) {
  995. if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-volume")) {
  996.     Tcl_GetIntFromObj(interp, objv[2], &volume);
  997. } else {
  998.     goto beepUsage;
  999. }
  1000.     } else if (objc == 4) {
  1001. if (!strcmp(Tcl_GetStringFromObj(objv[1], &length), "-volume")) {
  1002.     Tcl_GetIntFromObj(interp, objv[2], &volume);
  1003.     sndArg = Tcl_GetStringFromObj(objv[3], &length);
  1004. } else {
  1005.     goto beepUsage;
  1006. }
  1007.     } else {
  1008. goto beepUsage;
  1009.     }
  1010.     /*
  1011.      * Play the sound
  1012.      */
  1013.     if (sndArg == NULL) {
  1014. /*
  1015.          * Set Volume for SysBeep
  1016.          */
  1017. if (volume >= 0) {
  1018.     SetSoundVolume(volume, SYS_BEEP_VOLUME);
  1019. }
  1020. SysBeep(1);
  1021. /*
  1022.          * Reset Volume
  1023.          */
  1024. if (volume >= 0) {
  1025.     SetSoundVolume(0, RESET_VOLUME);
  1026. }
  1027.     } else {
  1028. strcpy((char *) sndName + 1, sndArg);
  1029. sndName[0] = length;
  1030. sound = GetNamedResource('snd ', sndName);
  1031. if (sound != NULL) {
  1032.     /*
  1033.              * Set Volume for Default Output device
  1034.              */
  1035.     if (volume >= 0) {
  1036. SetSoundVolume(volume, DEFAULT_SND_VOLUME);
  1037.     }
  1038.     SndPlay(NULL, (SndListHandle) sound, false);
  1039.     /*
  1040.              * Reset Volume
  1041.              */
  1042.     if (volume >= 0) {
  1043. SetSoundVolume(0, RESET_VOLUME);
  1044.     }
  1045. } else {
  1046.     Tcl_AppendStringsToObj(resultPtr, " "", sndArg, 
  1047.     "" is not a valid sound.  (Try ",
  1048.     Tcl_GetString(objv[0]), " -list)", NULL);
  1049.     return TCL_ERROR;
  1050. }
  1051.     }
  1052.     return TCL_OK;
  1053.     beepUsage:
  1054.     Tcl_WrongNumArgs(interp, 1, objv, "[-volume num] [-list | sndName]?");
  1055.     return TCL_ERROR;
  1056. }
  1057. /*
  1058.  *-----------------------------------------------------------------------------
  1059.  *
  1060.  * SetSoundVolume --
  1061.  *
  1062.  * Set the volume for either the SysBeep or the SndPlay call depending
  1063.  * on the value of mode (SYS_BEEP_VOLUME or DEFAULT_SND_VOLUME
  1064.  *      respectively.
  1065.  *
  1066.  *      It also stores the last channel set, and the old value of its 
  1067.  * VOLUME.  If you call SetSoundVolume with a mode of RESET_VOLUME, 
  1068.  * it will undo the last setting.  The volume parameter is
  1069.  *      ignored in this case.
  1070.  *
  1071.  * Side Effects:
  1072.  * Sets the System Volume
  1073.  *
  1074.  * Results:
  1075.  *      None
  1076.  *
  1077.  *-----------------------------------------------------------------------------
  1078.  */
  1079. void
  1080. SetSoundVolume(
  1081.     int volume,              /* This is the new volume */
  1082.     enum WhichVolume mode)   /* This flag says which volume to
  1083.       * set: SysBeep, SndPlay, or instructs us
  1084.       * to reset the volume */
  1085. {
  1086.     static int hasSM3 = -1;
  1087.     static enum WhichVolume oldMode;
  1088.     static long oldVolume = -1;
  1089.     /*
  1090.      * The volume setting calls only work if we have SoundManager
  1091.      * 3.0 or higher.  So we check that here.
  1092.      */
  1093.     
  1094.     if (hasSM3 == -1) {
  1095.      if (GetToolboxTrapAddress(_SoundDispatch) 
  1096. != GetToolboxTrapAddress(_Unimplemented)) {
  1097.     NumVersion SMVers = SndSoundManagerVersion();
  1098.     if (SMVers.majorRev > 2) {
  1099.      hasSM3 = 1;
  1100.     } else {
  1101. hasSM3 = 0;
  1102.     }
  1103. } else {
  1104.     /*
  1105.      * If the SoundDispatch trap is not present, then
  1106.      * we don't have the SoundManager at all.
  1107.      */
  1108.     
  1109.     hasSM3 = 0;
  1110. }
  1111.     }
  1112.     
  1113.     /*
  1114.      * If we don't have Sound Manager 3.0, we can't set the sound volume.
  1115.      * We will just ignore the request rather than raising an error.
  1116.      */
  1117.     
  1118.     if (!hasSM3) {
  1119.      return;
  1120.     }
  1121.     
  1122.     switch (mode) {
  1123.      case SYS_BEEP_VOLUME:
  1124.     GetSysBeepVolume(&oldVolume);
  1125.     SetSysBeepVolume(volume);
  1126.     oldMode = SYS_BEEP_VOLUME;
  1127.     break;
  1128. case DEFAULT_SND_VOLUME:
  1129.     GetDefaultOutputVolume(&oldVolume);
  1130.     SetDefaultOutputVolume(volume);
  1131.     oldMode = DEFAULT_SND_VOLUME;
  1132.     break;
  1133. case RESET_VOLUME:
  1134.     /*
  1135.      * If oldVolume is -1 someone has made a programming error
  1136.      * and called reset before setting the volume.  This is benign
  1137.      * however, so we will just exit.
  1138.      */
  1139.   
  1140.     if (oldVolume != -1) {
  1141.         if (oldMode == SYS_BEEP_VOLUME) {
  1142.          SetSysBeepVolume(oldVolume);
  1143.         } else if (oldMode == DEFAULT_SND_VOLUME) {
  1144.     SetDefaultOutputVolume(oldVolume);
  1145.         }
  1146.     }
  1147.     oldVolume = -1;
  1148.     }
  1149. }
  1150. /*
  1151.  *-----------------------------------------------------------------------------
  1152.  *
  1153.  * Tcl_MacEvalResource --
  1154.  *
  1155.  * Used to extend the source command.  Sources Tcl code from a Text
  1156.  * resource.  Currently only sources the resouce by name file ID may be
  1157.  * supported at a later date.
  1158.  *
  1159.  * Side Effects:
  1160.  * Depends on the Tcl code in the resource.
  1161.  *
  1162.  * Results:
  1163.  *      Returns a Tcl result.
  1164.  *
  1165.  *-----------------------------------------------------------------------------
  1166.  */
  1167. int
  1168. Tcl_MacEvalResource(
  1169.     Tcl_Interp *interp, /* Interpreter in which to process file. */
  1170.     CONST char *resourceName, /* Name of TEXT resource to source,
  1171.    NULL if number should be used. */
  1172.     int resourceNumber, /* Resource id of source. */
  1173.     CONST char *fileName) /* Name of file to process.
  1174.    NULL if application resource. */
  1175. {
  1176.     Handle sourceText;
  1177.     Str255 rezName;
  1178.     char msg[200];
  1179.     int result, iOpenedResFile = false;
  1180.     short saveRef, fileRef = -1;
  1181.     char idStr[64];
  1182.     FSSpec fileSpec;
  1183.     Tcl_DString ds, buffer;
  1184.     CONST char *nativeName;
  1185.     saveRef = CurResFile();
  1186.     if (fileName != NULL) {
  1187. OSErr err;
  1188. if (Tcl_TranslateFileName(interp, fileName, &buffer) == NULL) {
  1189.     return TCL_ERROR;
  1190. }
  1191. nativeName = Tcl_UtfToExternalDString(NULL, Tcl_DStringValue(&buffer), 
  1192.          Tcl_DStringLength(&buffer), &ds);
  1193. err = FSpLocationFromPath(strlen(nativeName), nativeName,
  1194.                 &fileSpec);
  1195. Tcl_DStringFree(&ds);
  1196. Tcl_DStringFree(&buffer);
  1197. if (err != noErr) {
  1198.     Tcl_AppendResult(interp, "Error finding the file: "", 
  1199. fileName, "".", NULL);
  1200.     return TCL_ERROR;
  1201. }
  1202. fileRef = FSpOpenResFileCompat(&fileSpec, fsRdPerm);
  1203. if (fileRef == -1) {
  1204.     Tcl_AppendResult(interp, "Error reading the file: "", 
  1205. fileName, "".", NULL);
  1206.     return TCL_ERROR;
  1207. }
  1208. UseResFile(fileRef);
  1209. iOpenedResFile = true;
  1210.     } else {
  1211. /*
  1212.  * The default behavior will search through all open resource files.
  1213.  * This may not be the behavior you desire.  If you want the behavior
  1214.  * of this call to *only* search the application resource fork, you
  1215.  * must call UseResFile at this point to set it to the application
  1216.  * file.  This means you must have already obtained the application's 
  1217.  * fileRef when the application started up.
  1218.  */
  1219.     }
  1220.     /*
  1221.      * Load the resource by name or ID
  1222.      */
  1223.     if (resourceName != NULL) {
  1224. Tcl_DString ds;
  1225. Tcl_UtfToExternalDString(NULL, resourceName, -1, &ds);
  1226. strcpy((char *) rezName + 1, Tcl_DStringValue(&ds));
  1227. rezName[0] = (unsigned) Tcl_DStringLength(&ds);
  1228. sourceText = GetNamedResource('TEXT', rezName);
  1229. Tcl_DStringFree(&ds);
  1230.     } else {
  1231. sourceText = GetResource('TEXT', (short) resourceNumber);
  1232.     }
  1233.     if (sourceText == NULL) {
  1234. result = TCL_ERROR;
  1235.     } else {
  1236. char *sourceStr = NULL;
  1237. HLock(sourceText);
  1238. sourceStr = Tcl_MacConvertTextResource(sourceText);
  1239. HUnlock(sourceText);
  1240. ReleaseResource(sourceText);
  1241. /*
  1242.  * We now evaluate the Tcl source
  1243.  */
  1244. result = Tcl_Eval(interp, sourceStr);
  1245. ckfree(sourceStr);
  1246. if (result == TCL_RETURN) {
  1247.     result = TCL_OK;
  1248. } else if (result == TCL_ERROR) {
  1249.     sprintf(msg, "n    (rsrc "%.150s" line %d)",
  1250.                     resourceName,
  1251.     interp->errorLine);
  1252.     Tcl_AddErrorInfo(interp, msg);
  1253. }
  1254. goto rezEvalCleanUp;
  1255.     }
  1256.     rezEvalError:
  1257.     sprintf(idStr, "ID=%d", resourceNumber);
  1258.     Tcl_AppendResult(interp, "The resource "",
  1259.     (resourceName != NULL ? resourceName : idStr),
  1260.     "" could not be loaded from ",
  1261.     (fileName != NULL ? fileName : "application"),
  1262.     ".", NULL);
  1263.     rezEvalCleanUp:
  1264.     /* 
  1265.      * TRICKY POINT: The code that you are sourcing here could load a
  1266.      * shared library.  This will go AHEAD of the resource we stored away
  1267.      * in saveRef on the resource path.  
  1268.      * If you restore the saveRef in this case, you will never be able
  1269.      * to get to the resources in the shared library, since you are now
  1270.      * pointing too far down on the resource list.  
  1271.      * So, we only reset the current resource file if WE opened a resource
  1272.      * explicitly, and then only if the CurResFile is still the 
  1273.      * one we opened... 
  1274.      */
  1275.      
  1276.     if (iOpenedResFile && (CurResFile() == fileRef)) {
  1277.         UseResFile(saveRef);
  1278.     }
  1279.     if (fileRef != -1) {
  1280. CloseResFile(fileRef);
  1281.     }
  1282.     return result;
  1283. }
  1284. /*
  1285.  *-----------------------------------------------------------------------------
  1286.  *
  1287.  * Tcl_MacConvertTextResource --
  1288.  *
  1289.  * Converts a TEXT resource into a Tcl suitable string.
  1290.  *
  1291.  * Side Effects:
  1292.  * Mallocs the returned memory, converts 'r' to 'n', and appends a NULL.
  1293.  *
  1294.  * Results:
  1295.  *      A new malloced string.
  1296.  *
  1297.  *-----------------------------------------------------------------------------
  1298.  */
  1299. char *
  1300. Tcl_MacConvertTextResource(
  1301.     Handle resource) /* Handle to TEXT resource. */
  1302. {
  1303.     int i, size;
  1304.     char *resultStr;
  1305.     Tcl_DString dstr;
  1306.     size = GetResourceSizeOnDisk(resource);
  1307.     
  1308.     Tcl_ExternalToUtfDString(NULL, *resource, size, &dstr);
  1309.     size = Tcl_DStringLength(&dstr) + 1;
  1310.     resultStr = (char *) ckalloc((unsigned) size);
  1311.     
  1312.     memcpy((VOID *) resultStr, (VOID *) Tcl_DStringValue(&dstr), (size_t) size);
  1313.     
  1314.     Tcl_DStringFree(&dstr);
  1315.     
  1316.     for (i=0; i<size; i++) {
  1317. if (resultStr[i] == 'r') {
  1318.     resultStr[i] = 'n';
  1319. }
  1320.     }
  1321.     return resultStr;
  1322. }
  1323. /*
  1324.  *-----------------------------------------------------------------------------
  1325.  *
  1326.  * Tcl_MacFindResource --
  1327.  *
  1328.  * Higher level interface for loading resources.
  1329.  *
  1330.  * Side Effects:
  1331.  * Attempts to load a resource.
  1332.  *
  1333.  * Results:
  1334.  *      A handle on success.
  1335.  *
  1336.  *-----------------------------------------------------------------------------
  1337.  */
  1338. Handle
  1339. Tcl_MacFindResource(
  1340.     Tcl_Interp *interp, /* Interpreter in which to process file. */
  1341.     long resourceType, /* Type of resource to load. */
  1342.     CONST char *resourceName, /* Name of resource to find,
  1343.  * NULL if number should be used. */
  1344.     int resourceNumber, /* Resource id of source. */
  1345.     CONST char *resFileRef, /* Registered resource file reference,
  1346.  * NULL if searching all open resource files. */
  1347.     int *releaseIt)         /* Should we release this resource when done. */
  1348. {
  1349.     Tcl_HashEntry *nameHashPtr;
  1350.     OpenResourceFork *resourceRef;
  1351.     int limitSearch = false;
  1352.     short saveRef;
  1353.     Handle resource;
  1354.     if (resFileRef != NULL) {
  1355. nameHashPtr = Tcl_FindHashEntry(&nameTable, resFileRef);
  1356. if (nameHashPtr == NULL) {
  1357.     Tcl_AppendResult(interp, "invalid resource file reference "",
  1358.      resFileRef, """, (char *) NULL);
  1359.     return NULL;
  1360. }
  1361. resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr);
  1362. saveRef = CurResFile();
  1363. UseResFile((short) resourceRef->fileRef);
  1364. limitSearch = true;
  1365.     }
  1366.     /* 
  1367.      * Some system resources (for example system resources) should not 
  1368.      * be released.  So we set autoload to false, and try to get the resource.
  1369.      * If the Master Pointer of the returned handle is null, then resource was 
  1370.      * not in memory, and it is safe to release it.  Otherwise, it is not.
  1371.      */
  1372.     
  1373.     SetResLoad(false);
  1374.  
  1375.     if (resourceName == NULL) {
  1376. if (limitSearch) {
  1377.     resource = Get1Resource(resourceType, resourceNumber);
  1378. } else {
  1379.     resource = GetResource(resourceType, resourceNumber);
  1380. }
  1381.     } else {
  1382.      Str255 rezName;
  1383. Tcl_DString ds;
  1384. Tcl_UtfToExternalDString(NULL, resourceName, -1, &ds);
  1385. strcpy((char *) rezName + 1, Tcl_DStringValue(&ds));
  1386. rezName[0] = (unsigned) Tcl_DStringLength(&ds);
  1387. if (limitSearch) {
  1388.     resource = Get1NamedResource(resourceType,
  1389.     rezName);
  1390. } else {
  1391.     resource = GetNamedResource(resourceType,
  1392.     rezName);
  1393. }
  1394. Tcl_DStringFree(&ds);
  1395.     }
  1396.     
  1397.     if (resource != NULL && *resource == NULL) {
  1398.      *releaseIt = 1;
  1399.      LoadResource(resource);
  1400.     } else {
  1401.      *releaseIt = 0;
  1402.     }
  1403.     
  1404.     SetResLoad(true);
  1405.     
  1406.     if (limitSearch) {
  1407. UseResFile(saveRef);
  1408.     }
  1409.     return resource;
  1410. }
  1411. /*
  1412.  *----------------------------------------------------------------------
  1413.  *
  1414.  * ResourceInit --
  1415.  *
  1416.  * Initialize the structures used for resource management.
  1417.  *
  1418.  * Results:
  1419.  * None.
  1420.  *
  1421.  * Side effects:
  1422.  * Read the code.
  1423.  *
  1424.  *----------------------------------------------------------------------
  1425.  */
  1426. static void
  1427. ResourceInit()
  1428. {
  1429.     initialized = 1;
  1430.     Tcl_InitHashTable(&nameTable, TCL_STRING_KEYS);
  1431.     Tcl_InitHashTable(&resourceTable, TCL_ONE_WORD_KEYS);
  1432.     resourceForkList = Tcl_NewObj();
  1433.     Tcl_IncrRefCount(resourceForkList);
  1434.     BuildResourceForkList();
  1435.     
  1436. }
  1437. /***/
  1438. /*Tcl_RegisterObjType(typePtr) */
  1439. /*
  1440.  *----------------------------------------------------------------------
  1441.  *
  1442.  * Tcl_NewOSTypeObj --
  1443.  *
  1444.  * This procedure is used to create a new resource name type object.
  1445.  *
  1446.  * Results:
  1447.  * The newly created object is returned. This object will have a NULL
  1448.  * string representation. The returned object has ref count 0.
  1449.  *
  1450.  * Side effects:
  1451.  * None.
  1452.  *
  1453.  *----------------------------------------------------------------------
  1454.  */
  1455. Tcl_Obj *
  1456. Tcl_NewOSTypeObj(
  1457.     OSType newOSType) /* Int used to initialize the new object. */
  1458. {
  1459.     register Tcl_Obj *objPtr;
  1460.     if (!osTypeInit) {
  1461. osTypeInit = 1;
  1462. Tcl_RegisterObjType(&osType);
  1463.     }
  1464.     objPtr = Tcl_NewObj();
  1465.     objPtr->bytes = NULL;
  1466.     objPtr->internalRep.longValue = newOSType;
  1467.     objPtr->typePtr = &osType;
  1468.     return objPtr;
  1469. }
  1470. /*
  1471.  *----------------------------------------------------------------------
  1472.  *
  1473.  * Tcl_SetOSTypeObj --
  1474.  *
  1475.  * Modify an object to be a resource type and to have the 
  1476.  * specified long value.
  1477.  *
  1478.  * Results:
  1479.  * None.
  1480.  *
  1481.  * Side effects:
  1482.  * The object's old string rep, if any, is freed. Also, any old
  1483.  * internal rep is freed. 
  1484.  *
  1485.  *----------------------------------------------------------------------
  1486.  */
  1487. void
  1488. Tcl_SetOSTypeObj(
  1489.     Tcl_Obj *objPtr, /* Object whose internal rep to init. */
  1490.     OSType newOSType) /* Integer used to set object's value. */
  1491. {
  1492.     register Tcl_ObjType *oldTypePtr = objPtr->typePtr;
  1493.     if (!osTypeInit) {
  1494. osTypeInit = 1;
  1495. Tcl_RegisterObjType(&osType);
  1496.     }
  1497.     if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
  1498. oldTypePtr->freeIntRepProc(objPtr);
  1499.     }
  1500.     
  1501.     objPtr->internalRep.longValue = newOSType;
  1502.     objPtr->typePtr = &osType;
  1503.     Tcl_InvalidateStringRep(objPtr);
  1504. }
  1505. /*
  1506.  *----------------------------------------------------------------------
  1507.  *
  1508.  * Tcl_GetOSTypeFromObj --
  1509.  *
  1510.  * Attempt to return an int from the Tcl object "objPtr". If the object
  1511.  * is not already an int, an attempt will be made to convert it to one.
  1512.  *
  1513.  * Results:
  1514.  * The return value is a standard Tcl object result. If an error occurs
  1515.  * during conversion, an error message is left in interp->objResult
  1516.  * unless "interp" is NULL.
  1517.  *
  1518.  * Side effects:
  1519.  * If the object is not already an int, the conversion will free
  1520.  * any old internal representation.
  1521.  *
  1522.  *----------------------------------------------------------------------
  1523.  */
  1524. int
  1525. Tcl_GetOSTypeFromObj(
  1526.     Tcl_Interp *interp,  /* Used for error reporting if not NULL. */
  1527.     Tcl_Obj *objPtr, /* The object from which to get a int. */
  1528.     OSType *osTypePtr) /* Place to store resulting int. */
  1529. {
  1530.     register int result;
  1531.     
  1532.     if (!osTypeInit) {
  1533. osTypeInit = 1;
  1534. Tcl_RegisterObjType(&osType);
  1535.     }
  1536.     if (objPtr->typePtr == &osType) {
  1537. *osTypePtr = objPtr->internalRep.longValue;
  1538. return TCL_OK;
  1539.     }
  1540.     result = SetOSTypeFromAny(interp, objPtr);
  1541.     if (result == TCL_OK) {
  1542. *osTypePtr = objPtr->internalRep.longValue;
  1543.     }
  1544.     return result;
  1545. }
  1546. /*
  1547.  *----------------------------------------------------------------------
  1548.  *
  1549.  * DupOSTypeInternalRep --
  1550.  *
  1551.  * Initialize the internal representation of an int Tcl_Obj to a
  1552.  * copy of the internal representation of an existing int object. 
  1553.  *
  1554.  * Results:
  1555.  * None.
  1556.  *
  1557.  * Side effects:
  1558.  * "copyPtr"s internal rep is set to the integer corresponding to
  1559.  * "srcPtr"s internal rep.
  1560.  *
  1561.  *----------------------------------------------------------------------
  1562.  */
  1563. static void
  1564. DupOSTypeInternalRep(
  1565.     Tcl_Obj *srcPtr, /* Object with internal rep to copy. */
  1566.     Tcl_Obj *copyPtr) /* Object with internal rep to set. */
  1567. {
  1568.     copyPtr->internalRep.longValue = srcPtr->internalRep.longValue;
  1569.     copyPtr->typePtr = &osType;
  1570. }
  1571. /*
  1572.  *----------------------------------------------------------------------
  1573.  *
  1574.  * SetOSTypeFromAny --
  1575.  *
  1576.  * Attempt to generate an integer internal form for the Tcl object
  1577.  * "objPtr".
  1578.  *
  1579.  * Results:
  1580.  * The return value is a standard object Tcl result. If an error occurs
  1581.  * during conversion, an error message is left in interp->objResult
  1582.  * unless "interp" is NULL.
  1583.  *
  1584.  * Side effects:
  1585.  * If no error occurs, an int is stored as "objPtr"s internal
  1586.  * representation. 
  1587.  *
  1588.  *----------------------------------------------------------------------
  1589.  */
  1590. static int
  1591. SetOSTypeFromAny(
  1592.     Tcl_Interp *interp, /* Used for error reporting if not NULL. */
  1593.     Tcl_Obj *objPtr) /* The object to convert. */
  1594. {
  1595.     Tcl_ObjType *oldTypePtr = objPtr->typePtr;
  1596.     char *string;
  1597.     int length;
  1598.     long newOSType;
  1599.     /*
  1600.      * Get the string representation. Make it up-to-date if necessary.
  1601.      */
  1602.     string = Tcl_GetStringFromObj(objPtr, &length);
  1603.     if (length != 4) {
  1604. if (interp != NULL) {
  1605.     Tcl_ResetResult(interp);
  1606.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
  1607.     "expected Macintosh OS type but got "", string, """,
  1608.     (char *) NULL);
  1609. }
  1610. return TCL_ERROR;
  1611.     }
  1612.     newOSType =  *((long *) string);
  1613.     
  1614.     /*
  1615.      * The conversion to resource type succeeded. Free the old internalRep 
  1616.      * before setting the new one.
  1617.      */
  1618.     if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
  1619. oldTypePtr->freeIntRepProc(objPtr);
  1620.     }
  1621.     
  1622.     objPtr->internalRep.longValue = newOSType;
  1623.     objPtr->typePtr = &osType;
  1624.     return TCL_OK;
  1625. }
  1626. /*
  1627.  *----------------------------------------------------------------------
  1628.  *
  1629.  * UpdateStringOfOSType --
  1630.  *
  1631.  * Update the string representation for an resource type object.
  1632.  * Note: This procedure does not free an existing old string rep
  1633.  * so storage will be lost if this has not already been done. 
  1634.  *
  1635.  * Results:
  1636.  * None.
  1637.  *
  1638.  * Side effects:
  1639.  * The object's string is set to a valid string that results from
  1640.  * the int-to-string conversion.
  1641.  *
  1642.  *----------------------------------------------------------------------
  1643.  */
  1644. static void
  1645. UpdateStringOfOSType(
  1646.     register Tcl_Obj *objPtr) /* Int object whose string rep to update. */
  1647. {
  1648.     objPtr->bytes = ckalloc(5);
  1649.     sprintf(objPtr->bytes, "%-4.4s", &(objPtr->internalRep.longValue));
  1650.     objPtr->length = 4;
  1651. }
  1652. /*
  1653.  *----------------------------------------------------------------------
  1654.  *
  1655.  * GetRsrcRefFromObj --
  1656.  *
  1657.  * Given a String object containing a resource file token, return
  1658.  * the OpenResourceFork structure that it represents, or NULL if 
  1659.  * the token cannot be found.  If okayOnReadOnly is false, it will 
  1660.  *      also check whether the token corresponds to a read-only file, 
  1661.  *      and return NULL if it is.
  1662.  *
  1663.  * Results:
  1664.  * A pointer to an OpenResourceFork structure, or NULL.
  1665.  *
  1666.  * Side effects:
  1667.  * An error message may be left in resultPtr.
  1668.  *
  1669.  *----------------------------------------------------------------------
  1670.  */
  1671. static OpenResourceFork *
  1672. GetRsrcRefFromObj(
  1673.     register Tcl_Obj *objPtr, /* String obj containing file token     */
  1674.     int okayOnReadOnly,         /* Whether this operation is okay for a *
  1675.                                  * read only file.                      */
  1676.     const char *operation,      /* String containing the operation we   *
  1677.                                  * were trying to perform, used for errors */
  1678.     Tcl_Obj *resultPtr)         /* Tcl_Obj to contain error message     */
  1679. {
  1680.     char *stringPtr;
  1681.     Tcl_HashEntry *nameHashPtr;
  1682.     OpenResourceFork *resourceRef;
  1683.     int length;
  1684.     OSErr err;
  1685.     
  1686.     stringPtr = Tcl_GetStringFromObj(objPtr, &length);
  1687.     nameHashPtr = Tcl_FindHashEntry(&nameTable, stringPtr);
  1688.     if (nameHashPtr == NULL) {
  1689.         Tcl_AppendStringsToObj(resultPtr,
  1690.         "invalid resource file reference "",
  1691.         stringPtr, """, (char *) NULL);
  1692.         return NULL;
  1693.     }
  1694.     resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr);
  1695.     
  1696.     if (!okayOnReadOnly) {
  1697.         err = GetResFileAttrs((short) resourceRef->fileRef);
  1698.         if (err & mapReadOnly) {
  1699.             Tcl_AppendStringsToObj(resultPtr, "cannot ", operation, 
  1700.                     " resource file "",
  1701.                     stringPtr, "", it was opened read only",
  1702.                     (char *) NULL);
  1703.             return NULL;
  1704.         }
  1705.     }
  1706.     return resourceRef;
  1707. }
  1708. /*
  1709.  *----------------------------------------------------------------------
  1710.  *
  1711.  * TclMacRegisterResourceFork --
  1712.  *
  1713.  * Register an open resource fork in the table of open resources 
  1714.  * managed by the procedures in this file.  If the resource file
  1715.  *      is already registered with the table, then no new token is made.
  1716.  *
  1717.  *      The behavior is controlled by the value of tokenPtr, and of the 
  1718.  * flags variable.  For tokenPtr, the possibilities are:
  1719.  *   - NULL: The new token is auto-generated, but not returned.
  1720.  *        - The string value of tokenPtr is the empty string: Then
  1721.  * the new token is auto-generated, and returned in tokenPtr
  1722.  *   - tokenPtr has a value: The string value will be used for the token,
  1723.  * unless it is already in use, in which case a new token will
  1724.  * be generated, and returned in tokenPtr.
  1725.  *
  1726.  *      For the flags variable:  it can be one of:
  1727.  *   - TCL_RESOURCE__INSERT_TAIL: The element is inserted at the
  1728.  *              end of the list of open resources.  Used only in Resource_Init.
  1729.  *   - TCL_RESOURCE_DONT_CLOSE: The resource close command will not close
  1730.  *         this resource.
  1731.  *   - TCL_RESOURCE_CHECK_IF_OPEN: This will check to see if this file's
  1732.  *         resource fork is already opened by this Tcl shell, and return 
  1733.  *         an error without registering the resource fork.
  1734.  *
  1735.  * Results:
  1736.  * Standard Tcl Result
  1737.  *
  1738.  * Side effects:
  1739.  * An entry may be added to the resource name table.
  1740.  *
  1741.  *----------------------------------------------------------------------
  1742.  */
  1743. int
  1744. TclMacRegisterResourceFork(
  1745.     short fileRef,         /* File ref for an open resource fork. */
  1746.     Tcl_Obj *tokenPtr, /* A Tcl Object to which to write the  *
  1747.  * new token */
  1748.     int flags)       /* 1 means insert at the head of the resource
  1749.                                  * fork list, 0 means at the tail */
  1750. {
  1751.     Tcl_HashEntry *resourceHashPtr;
  1752.     Tcl_HashEntry *nameHashPtr;
  1753.     OpenResourceFork *resourceRef;
  1754.     int new;
  1755.     char *resourceId = NULL;
  1756.    
  1757.     if (!initialized) {
  1758.         ResourceInit();
  1759.     }
  1760.     
  1761.     /*
  1762.      * If we were asked to, check that this file has not been opened
  1763.      * already with a different permission.  It it has, then return an error.
  1764.      */
  1765.      
  1766.     new = 1;
  1767.     
  1768.     if (flags & TCL_RESOURCE_CHECK_IF_OPEN) {
  1769.         Tcl_HashSearch search;
  1770.         short oldFileRef, filePermissionFlag;
  1771.         FCBPBRec newFileRec, oldFileRec;
  1772.         OSErr err;
  1773.         
  1774.         oldFileRec.ioCompletion = NULL;
  1775.         oldFileRec.ioFCBIndx = 0;
  1776.         oldFileRec.ioNamePtr = NULL;
  1777.         
  1778.         newFileRec.ioCompletion = NULL;
  1779.         newFileRec.ioFCBIndx = 0;
  1780.         newFileRec.ioNamePtr = NULL;
  1781.         newFileRec.ioVRefNum = 0;
  1782.         newFileRec.ioRefNum = fileRef;
  1783.         err = PBGetFCBInfo(&newFileRec, false);
  1784.         filePermissionFlag = ( newFileRec.ioFCBFlags >> 12 ) & 0x1;
  1785.             
  1786.         
  1787.         resourceHashPtr = Tcl_FirstHashEntry(&resourceTable, &search);
  1788.         while (resourceHashPtr != NULL) {
  1789.             oldFileRef = (short) Tcl_GetHashKey(&resourceTable,
  1790.                     resourceHashPtr);
  1791.             if (oldFileRef == fileRef) {
  1792.                 new = 0;
  1793.                 break;
  1794.             }
  1795.             oldFileRec.ioVRefNum = 0;
  1796.             oldFileRec.ioRefNum = oldFileRef;
  1797.             err = PBGetFCBInfo(&oldFileRec, false);
  1798.             
  1799.             /*
  1800.              * err might not be noErr either because the file has closed 
  1801.              * out from under us somehow, which is bad but we're not going
  1802.              * to fix it here, OR because it is the ROM MAP, which has a 
  1803.              * fileRef, but can't be gotten to by PBGetFCBInfo.
  1804.              */
  1805.             if ((err == noErr) 
  1806.                     && (newFileRec.ioFCBVRefNum == oldFileRec.ioFCBVRefNum)
  1807.                     && (newFileRec.ioFCBFlNm == oldFileRec.ioFCBFlNm)) {
  1808.                 /*
  1809.  * In MacOS 8.1 it seems like we get different file refs even
  1810.                  * though we pass the same file & permissions.  This is not
  1811.                  * what Inside Mac says should happen, but it does, so if it
  1812.                  * does, then close the new res file and return the original
  1813.                  * one...
  1814.  */
  1815.                  
  1816.                 if (filePermissionFlag == ((oldFileRec.ioFCBFlags >> 12) & 0x1)) {
  1817.                     CloseResFile(fileRef);
  1818.                     new = 0;
  1819.                     break;
  1820.                 } else {
  1821.                     if (tokenPtr != NULL) {
  1822.                         Tcl_SetStringObj(tokenPtr, "Resource already open with different permissions.", -1);
  1823.                     }   
  1824.                     return TCL_ERROR;
  1825.                 }
  1826.             }
  1827.             resourceHashPtr = Tcl_NextHashEntry(&search);
  1828.         }
  1829.     }
  1830.        
  1831.     
  1832.     /*
  1833.      * If the file has already been opened with these same permissions, then it
  1834.      * will be in our list and we will have set new to 0 above.
  1835.      * So we will just return the token (if tokenPtr is non-null)
  1836.      */
  1837.      
  1838.     if (new) {
  1839.         resourceHashPtr = Tcl_CreateHashEntry(&resourceTable,
  1840. (char *) fileRef, &new);
  1841.     }
  1842.     
  1843.     if (!new) {
  1844.         if (tokenPtr != NULL) {   
  1845.             resourceId = (char *) Tcl_GetHashValue(resourceHashPtr);
  1846.     Tcl_SetStringObj(tokenPtr, resourceId, -1);
  1847.         }
  1848.         return TCL_OK;
  1849.     }        
  1850.     /*
  1851.      * If we were passed in a result pointer which is not an empty
  1852.      * string, attempt to use that as the key.  If the key already
  1853.      * exists, silently fall back on resource%d...
  1854.      */
  1855.      
  1856.     if (tokenPtr != NULL) {
  1857.         char *tokenVal;
  1858.         int length;
  1859.         tokenVal = Tcl_GetStringFromObj(tokenPtr, &length);
  1860.         if (length > 0) {
  1861.             nameHashPtr = Tcl_FindHashEntry(&nameTable, tokenVal);
  1862.             if (nameHashPtr == NULL) {
  1863.                 resourceId = ckalloc(length + 1);
  1864.                 memcpy(resourceId, tokenVal, length);
  1865.                 resourceId[length] = '';
  1866.             }
  1867.         }
  1868.     }
  1869.     
  1870.     if (resourceId == NULL) {
  1871.         resourceId = (char *) ckalloc(15);
  1872.         sprintf(resourceId, "resource%d", newId);
  1873.     }
  1874.     
  1875.     Tcl_SetHashValue(resourceHashPtr, resourceId);
  1876.     newId++;
  1877.     nameHashPtr = Tcl_CreateHashEntry(&nameTable, resourceId, &new);
  1878.     if (!new) {
  1879. panic("resource id has repeated itself");
  1880.     }
  1881.     
  1882.     resourceRef = (OpenResourceFork *) ckalloc(sizeof(OpenResourceFork));
  1883.     resourceRef->fileRef = fileRef;
  1884.     resourceRef->flags = flags;
  1885.     
  1886.     Tcl_SetHashValue(nameHashPtr, (ClientData) resourceRef);
  1887.     if (tokenPtr != NULL) {
  1888.         Tcl_SetStringObj(tokenPtr, resourceId, -1);
  1889.     }
  1890.     
  1891.     if (flags & TCL_RESOURCE_INSERT_TAIL) {
  1892.         Tcl_ListObjAppendElement(NULL, resourceForkList, tokenPtr);
  1893.     } else {
  1894.         Tcl_ListObjReplace(NULL, resourceForkList, 0, 0, 1, &tokenPtr);
  1895.     }
  1896.     return TCL_OK;
  1897. }
  1898. /*
  1899.  *----------------------------------------------------------------------
  1900.  *
  1901.  * TclMacUnRegisterResourceFork --
  1902.  *
  1903.  * Removes the entry for an open resource fork from the table of 
  1904.  * open resources managed by the procedures in this file.
  1905.  *      If resultPtr is not NULL, it will be used for error reporting.
  1906.  *
  1907.  * Results:
  1908.  * The fileRef for this token, or -1 if an error occured.
  1909.  *
  1910.  * Side effects:
  1911.  * An entry is removed from the resource name table.
  1912.  *
  1913.  *----------------------------------------------------------------------
  1914.  */
  1915. short
  1916. TclMacUnRegisterResourceFork(
  1917.     char *tokenPtr,
  1918.     Tcl_Obj *resultPtr)
  1919. {
  1920.     Tcl_HashEntry *resourceHashPtr;
  1921.     Tcl_HashEntry *nameHashPtr;
  1922.     OpenResourceFork *resourceRef;
  1923.     char *resourceId = NULL;
  1924.     short fileRef;
  1925.     char *bytes;
  1926.     int i, match, index, listLen, length, elemLen;
  1927.     Tcl_Obj **elemPtrs;
  1928.     
  1929.      
  1930.     nameHashPtr = Tcl_FindHashEntry(&nameTable, tokenPtr);
  1931.     if (nameHashPtr == NULL) {
  1932.         if (resultPtr != NULL) {
  1933.     Tcl_AppendStringsToObj(resultPtr,
  1934.     "invalid resource file reference "",
  1935.     tokenPtr, """, (char *) NULL);
  1936.         }
  1937. return -1;
  1938.     }
  1939.     
  1940.     resourceRef = (OpenResourceFork *) Tcl_GetHashValue(nameHashPtr);
  1941.     fileRef = resourceRef->fileRef;
  1942.         
  1943.     if ( resourceRef->flags & TCL_RESOURCE_DONT_CLOSE ) {
  1944.         if (resultPtr != NULL) {
  1945.     Tcl_AppendStringsToObj(resultPtr,
  1946.     "can't close "", tokenPtr, "" resource file", 
  1947.     (char *) NULL);
  1948. }
  1949. return -1;
  1950.     }            
  1951.     Tcl_DeleteHashEntry(nameHashPtr);
  1952.     ckfree((char *) resourceRef);
  1953.     
  1954.     
  1955.     /* 
  1956.      * Now remove the resource from the resourceForkList object 
  1957.      */
  1958.      
  1959.     Tcl_ListObjGetElements(NULL, resourceForkList, &listLen, &elemPtrs);
  1960.     
  1961.  
  1962.     index = -1;
  1963.     length = strlen(tokenPtr);
  1964.     
  1965.     for (i = 0; i < listLen; i++) {
  1966. match = 0;
  1967. bytes = Tcl_GetStringFromObj(elemPtrs[i], &elemLen);
  1968. if (length == elemLen) {
  1969. match = (memcmp(bytes, tokenPtr,
  1970. (size_t) length) == 0);
  1971. }
  1972. if (match) {
  1973.     index = i;
  1974.     break;
  1975. }
  1976.     }
  1977.     if (!match) {
  1978.         panic("the resource Fork List is out of synch!");
  1979.     }
  1980.     
  1981.     Tcl_ListObjReplace(NULL, resourceForkList, index, 1, 0, NULL);
  1982.     
  1983.     resourceHashPtr = Tcl_FindHashEntry(&resourceTable, (char *) fileRef);
  1984.     
  1985.     if (resourceHashPtr == NULL) {
  1986. panic("Resource & Name tables are out of synch in resource command.");
  1987.     }
  1988.     ckfree(Tcl_GetHashValue(resourceHashPtr));
  1989.     Tcl_DeleteHashEntry(resourceHashPtr);
  1990.     
  1991.     return fileRef;
  1992. }
  1993. /*
  1994.  *----------------------------------------------------------------------
  1995.  *
  1996.  * BuildResourceForkList --
  1997.  *
  1998.  * Traverses the list of open resource forks, and builds the 
  1999.  * list of resources forks.  Also creates a resource token for any that 
  2000.  *      are opened but not registered with our resource system.
  2001.  *      This is based on code from Apple DTS.
  2002.  *
  2003.  * Results:
  2004.  * None.
  2005.  *
  2006.  * Side effects:
  2007.  *      The list of resource forks is updated.
  2008.  * The resource name table may be augmented.
  2009.  *
  2010.  *----------------------------------------------------------------------
  2011.  */
  2012. void
  2013. BuildResourceForkList()
  2014. {
  2015.     Handle currentMapHandle, mSysMapHandle;  
  2016.     Ptr tempPtr;
  2017.     FCBPBRec fileRec;
  2018.     char fileName[256];
  2019.     char appName[62];
  2020.     Tcl_Obj *nameObj;
  2021.     OSErr err;
  2022.     ProcessSerialNumber psn;
  2023.     ProcessInfoRec info;
  2024.     FSSpec fileSpec;
  2025.         
  2026.     /* 
  2027.      * Get the application name, so we can substitute
  2028.      * the token "application" for the application's resource.
  2029.      */ 
  2030.      
  2031.     GetCurrentProcess(&psn);
  2032.     info.processInfoLength = sizeof(ProcessInfoRec);
  2033.     info.processName = (StringPtr) &appName;
  2034.     info.processAppSpec = &fileSpec;
  2035.     GetProcessInformation(&psn, &info);
  2036.     p2cstr((StringPtr) appName);
  2037.     
  2038.     fileRec.ioCompletion = NULL;
  2039.     fileRec.ioVRefNum = 0;
  2040.     fileRec.ioFCBIndx = 0;
  2041.     fileRec.ioNamePtr = (StringPtr) &fileName;
  2042.     
  2043.     
  2044.     currentMapHandle = LMGetTopMapHndl();
  2045.     mSysMapHandle = LMGetSysMapHndl();
  2046.     
  2047.     while (1) {
  2048.         /* 
  2049.          * Now do the ones opened after the application.
  2050.          */
  2051.        
  2052.         nameObj = Tcl_NewObj();
  2053.         
  2054.         tempPtr = *currentMapHandle;
  2055.         fileRec.ioRefNum = *((short *) (tempPtr + 20));
  2056.         err = PBGetFCBInfo(&fileRec, false);
  2057.         
  2058.         if (err != noErr) {
  2059.             /*
  2060.              * The ROM resource map does not correspond to an opened file...
  2061.              */
  2062.              Tcl_SetStringObj(nameObj, "ROM Map", -1);
  2063.         } else {
  2064.             p2cstr((StringPtr) fileName);
  2065.             if (strcmp(fileName,appName) == 0) {
  2066.                 Tcl_SetStringObj(nameObj, "application", -1);
  2067.             } else {
  2068.                 Tcl_SetStringObj(nameObj, fileName, -1);
  2069.             }
  2070.             c2pstr(fileName);
  2071.         }
  2072.         
  2073.         TclMacRegisterResourceFork(fileRec.ioRefNum, nameObj, 
  2074.             TCL_RESOURCE_DONT_CLOSE | TCL_RESOURCE_INSERT_TAIL);
  2075.        
  2076.         if (currentMapHandle == mSysMapHandle) {
  2077.             break;
  2078.         }
  2079.         
  2080.         currentMapHandle = *((Handle *) (tempPtr + 16));
  2081.     }
  2082. }