#include "osxMacTcl.h" #if TARGET_RT_MAC_MACHO #ifndef __CARBON__ #include #endif // ------------------------------------------------------------------------ static Tcl_Encoding gFSpPathMacRomanEncoding=NULL; #define SetupFSpPathEncoding() { \ if(!gFSpPathMacRomanEncoding) \ gFSpPathMacRomanEncoding = Tcl_GetEncoding(NULL,"macRoman"); \ } /*============================== CFStrings ==============================*/ /* * CFStringGetCString[Path]() with kCFStringEncodingUTF8 do not work. * They return fully decomposed Utf8 characters which Tcl does not * understand. See Bug 587 and associated discussion on * AlphaTcl-developers * * !!! conversion through macRoman is grotesque and should not be * necessary, but Tcl appears not to properly handle accented character * encodings. See Bug 587 and the associated discussion on * AlphaTcl-developers. !!! */ #ifndef MAC_OS_X_VERSION_10_2 /* define constants from 10.2 CFString.h to allow compilation in 10.1 */ typedef enum { kCFStringNormalizationFormD = 0, // Canonical Decomposition kCFStringNormalizationFormKD, // Compatibility Decomposition kCFStringNormalizationFormC, // Canonical Decomposition followed by Canonical Composition kCFStringNormalizationFormKC // Compatibility Decomposition followed by Canonical Composition } CFStringNormalizationForm; #endif /* *---------------------------------------------------------------------- * * TryCFStringNormalize -- * * call the 10.2 only CFStringNormalize() in a backwards compatible way. * * Results: * normalized mutable copy of string (retained, needs to be released!) * or NULL if CFStringNormalize not available. * * Side effects: * None. * *---------------------------------------------------------------------- */ #include static CFMutableStringRef TryCFStringNormalize(CFStringRef theString, CFStringNormalizationForm theForm) { static Boolean initialized = FALSE; static void (*cfstringnormalize)(CFMutableStringRef, CFStringNormalizationForm) = NULL; if(!initialized) { if(NSIsSymbolNameDefinedWithHint("_CFStringNormalize", "CoreFoundation")) { NSSymbol nsSymbol = NSLookupAndBindSymbolWithHint("_CFStringNormalize", "CoreFoundation"); if(nsSymbol) cfstringnormalize = NSAddressOfSymbol(nsSymbol); } initialized = TRUE; } if(cfstringnormalize) { CFMutableStringRef theMutableString = CFStringCreateMutableCopy(NULL, 0, theString); if (theMutableString) { cfstringnormalize(theMutableString, theForm); return(theMutableString); } } return(NULL); } /* *---------------------------------------------------------------------- * * CFStringToDString -- * * This helper function converts a CFString into a DString, * first transforming to canonical composed or decomposed unicode * depending on the 'compose' flag then transforming to external * (macRoman) encoding if 'external' is set. * * Uses the most direct transformation possible on the current * system, e.g. CFStringNormalize if available, or by transcoding * to/from macRoman otherwise (potentially lossy!). * * Results: * Tcl error code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int CFStringToDString(Tcl_Interp * interp, CFStringRef strRef, Tcl_DString * dsPtr, Boolean compose, Boolean external) { CONST84 char * str; Boolean success; int len; int result = TCL_ERROR; CFStringRef theStrRef = NULL; CFStringEncoding theEncoding; Tcl_DString ds, *theDsPtr = dsPtr; Boolean usedNormalize = FALSE, needConvert; if (compose) theStrRef = TryCFStringNormalize(strRef, kCFStringNormalizationFormC); else theStrRef = TryCFStringNormalize(strRef, kCFStringNormalizationFormD); if(theStrRef) { usedNormalize = TRUE; } else { theStrRef = strRef; theEncoding = kCFStringEncodingMacRoman; } if (usedNormalize && !external) theEncoding = kCFStringEncodingUTF8; else theEncoding = kCFStringEncodingMacRoman; if(!usedNormalize && !external) theDsPtr = &ds; // will need ExternalToUtf conversion len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(theStrRef), theEncoding); Tcl_DStringInit(theDsPtr); Tcl_DStringSetLength(theDsPtr, len); success = CFStringGetCString(theStrRef, Tcl_DStringValue(theDsPtr), len+1, theEncoding); if (success) { /* len was only a guess */ Tcl_DStringSetLength(theDsPtr, strlen(Tcl_DStringValue(theDsPtr))); result = TCL_OK; } else if (interp) Tcl_SetResult(interp, "Can't extract string from CFString", TCL_STATIC); if (!usedNormalize && !external) { // need ExternalToUtf conversion if(success) { SetupFSpPathEncoding(); Tcl_ExternalToUtfDString(gFSpPathMacRomanEncoding, Tcl_DStringValue(theDsPtr), Tcl_DStringLength(theDsPtr), dsPtr); } Tcl_DStringFree(theDsPtr); } if(usedNormalize) CFRelease(theStrRef); return result; } /* *---------------------------------------------------------------------- * * BufferToDString -- * * This helper function converts a text buffer of given lenth into * a DString (if length == -1, buffer is assumed to be a C string), * first transforming from external (macRoman) encoding if * 'fromExternal' is set, then transforming to canonical composed * or decomposed unicode depending on the 'compose' flag and finally * transforming to external (macRoman) encoding if 'external' is set. * * Tries to use the most efficient transformations possible on the * current system, e.g. CFStringNormalize if available, and * CFStringCreateWithCStringNoCopy if given a C string. * * Results: * Tcl error code. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int BufferToDString(Tcl_Interp * interp, CONST84 char *buffer, int length, Tcl_DString * dsPtr, Boolean compose, Boolean toExternal, Boolean fromExternal) { int result; CFStringRef theString; CFStringEncoding theEncoding; theEncoding = (fromExternal ? kCFStringEncodingMacRoman : kCFStringEncodingUTF8); if(length < 0) //assume buffer is a C string theString = CFStringCreateWithCStringNoCopy(NULL, buffer, theEncoding, kCFAllocatorNull); else theString = CFStringCreateWithBytes(NULL, buffer, length, theEncoding, FALSE); if(theString) { result = CFStringToDString(interp, theString, dsPtr, compose, toExternal); CFRelease(theString); // bug 671 } else { if (interp) Tcl_SetResult(interp, "Can't create CFString from buffer", TCL_STATIC); } return result; } /* CFString to external DString */ int CFStringToExternalDString(Tcl_Interp * interp, CFStringRef strRef, Tcl_DString * dsPtr) { return CFStringToDString(interp, strRef, dsPtr, TRUE, TRUE); } /* CFString to DString */ int CFStringToUtfDString(Tcl_Interp * interp, CFStringRef strRef, Tcl_DString * dsPtr) { return CFStringToDString(interp, strRef, dsPtr, TRUE, FALSE); } /* decomposed utf8 buffer to external DString */ int DUtfToExternalDString(Tcl_Interp * interp, CONST84 char * src, int length, Tcl_DString * dsPtr) { return BufferToDString(interp, src, length, dsPtr, TRUE, TRUE, FALSE); } /* decomposed utf8 buffer to DString */ int DUtfToUtfDString(Tcl_Interp * interp, CONST84 char * src, int length, Tcl_DString * dsPtr) { return BufferToDString(interp, src, length, dsPtr, TRUE, FALSE, FALSE); } /* external buffer to decomposed utf8 DString */ int ExternalToDUtfDString(Tcl_Interp * interp, CONST84 char * src, int length, Tcl_DString * dsPtr) { return BufferToDString(interp, src, length, dsPtr, FALSE, FALSE, TRUE); } /* utf8 buffer to decomposed utf8 DString */ int UtfToDUtfDString(Tcl_Interp * interp, CONST84 char * src, int length, Tcl_DString * dsPtr) { return BufferToDString(interp, src, length, dsPtr, FALSE, FALSE, FALSE); } static Tcl_Obj * _CFStringToTclObj(CFStringRef strRef) { Tcl_Obj * outObj; CFIndex len = CFStringGetLength(strRef); const UniChar * chars = CFStringGetCharactersPtr(strRef); if (chars != NULL) { outObj = Tcl_NewUnicodeObj(chars, len); } else { UniChar * buffer = (UniChar*) ckalloc(len * sizeof(UniChar)); CFStringGetCharacters(strRef, CFRangeMake(0, len), buffer); outObj = Tcl_NewUnicodeObj(buffer, len); ckfree((char*) buffer); } return outObj; } /* CFStringRef to decomposed Unicode Tcl_Obj */ Tcl_Obj * CFStringToTclObj(CFStringRef strRef) { Tcl_Obj * outObj; CFStringRef theStrRef = NULL; theStrRef = TryCFStringNormalize(strRef, kCFStringNormalizationFormC); /* * if (theStrRef != NULL) { * const UniChar * chars = CFStringGetCharactersPtr(theStrRef); * CFIndex len = CFStringGetLength(theStrRef); * outObj = Tcl_NewUnicodeObj(chars, len); * outObj = Tcl_NewUnicodeObj(CFStringGetCharactersPtr(theStrRef), CFStringGetLength(theStrRef)); * CFRelease(theStrRef); * } else { * outObj = Tcl_NewUnicodeObj(CFStringGetCharactersPtr(strRef), CFStringGetLength(strRef)); * } */ if (theStrRef != NULL) { outObj = _CFStringToTclObj(theStrRef); CFRelease(theStrRef); } else { outObj = _CFStringToTclObj(strRef); } return outObj; } /* Unicode Tcl_Obj * to CFStringRef */ CFStringRef TclObjToCFString(Tcl_Obj * inObj) { if (inObj == NULL) { return CFSTR(""); } else { return CFStringCreateWithCharacters(NULL, Tcl_GetUnicode(inObj), Tcl_GetCharLength(inObj)); } } /*============================== ==============================*/ // das 091200 reimplemented the following routines from scratch // for Tcl on OSX using modern FileMgr APIs and FSRefs // they are analogous to the MacTcl routines in tclMacUtil.c // // on OSX these routines are used in oldEndre.c instead // of the crufty old Alpha versions #define MAXPATHLEN 1024 /* *---------------------------------------------------------------------- * * FSpLocationFromPath -- * * This function obtains an FSSpec for a given macintosh path. * Unlike the More Files function FSpLocationFromFullPath, this * function will also accept partial paths and resolve any aliases * along the path. It will also create an FSSpec for a path that * does not yet exist. * * Results: * OSErr code. * * Side effects: * None. * *---------------------------------------------------------------------- */ OSErr FSpLocationFromPath( int length, /* Length of path. */ CONST84 char *path, /* The path to convert. */ FSSpecPtr fileSpecPtr) /* On return the spec for the path. */ { UInt8 fileName[MAXPATHLEN]; unsigned int fileNameLen; OSErr err; unsigned int pos, cur; Boolean isDirectory=TRUE, filenotexist=FALSE, wasAlias, done; FSRef fsref; Tcl_DString ds; // FSRefMakePath et al use deomposed UTF8 on OSX if(ExternalToDUtfDString(NULL, path, length, &ds) == TCL_ERROR) { err = coreFoundationUnknownErr; goto done; } path = Tcl_DStringValue(&ds); length = Tcl_DStringLength(&ds); pos = 0; fileName[0] = 0; fileNameLen = 0; /* * Check to see if this is a full path. If partial * we assume that path starts with the current working * directory. (Ie. volume & dir = 0) */ if ((done=(length == 0)) || (path[0] != '/')) { // start at current directory { CFBundleRef appBundle=CFBundleGetMainBundle(); CFURLRef appURL=NULL, parentURL=NULL; CFURLPathStyle pathStyle=kCFURLPOSIXPathStyle; err = coreFoundationUnknownErr; if(appBundle) { appURL=CFBundleCopyBundleURL(appBundle); if(appURL) { parentURL=CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault,appURL); CFRelease(appURL); if(parentURL) { if(CFURLGetFSRef(parentURL, &fsref)) err=noErr; CFRelease(parentURL); } } } } if (err != noErr) goto done; if(!done){ err = FSRefMakePath(&fsref,fileName,MAXPATHLEN); if (err != noErr) goto done; fileNameLen=strlen(fileName); } } else { if(path[0] == '/') { if((done=(length == 1))) { /* * If path = "/", just get root directory. */ err = FSPathMakeRef((UInt8 *) path, &fsref, &isDirectory); if (err != noErr) goto done; } else { pos++; } } } if(!done) { fileName[fileNameLen++] = '/'; fileName[fileNameLen] = 0; while (pos < length) { if (!isDirectory || filenotexist) {err=dirNFErr; goto done;} cur = pos; while (path[pos] != '/' && pos < length) { pos++; } if (fileNameLen+pos-cur > MAXPATHLEN) { err=bdNamErr; goto done; } else { strncpy(fileName+fileNameLen, &path[cur], pos - cur); fileNameLen += pos - cur; } fileName[fileNameLen] = 0; err = FSPathMakeRef(fileName, &fsref, &isDirectory); if ((err != noErr) && !(filenotexist=(err == fnfErr))) goto done; if (!filenotexist) { err = FSResolveAliasFile(&fsref, true, &isDirectory, &wasAlias); if (err != noErr) goto done; if(wasAlias){ err = FSRefMakePath(&fsref,fileName,MAXPATHLEN); if (err != noErr) goto done; fileNameLen=strlen(fileName); } } if (path[pos] == '/') { if (!isDirectory || filenotexist) {err=dirNFErr; goto done;} pos++; fileName[fileNameLen++] = '/'; fileName[fileNameLen] = 0; } } } if(!filenotexist) { err = FSGetCatalogInfo(&fsref,kFSCatInfoNone,NULL,NULL,fileSpecPtr,NULL); } else { FSCatalogInfo catalogInfo; Tcl_DString ds; if(pos - cur>sizeof(StrFileName)) {err=bdNamErr; goto done;} err = FSGetCatalogInfo(&fsref, kFSCatInfoNodeID | kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); if (err != noErr) goto done; fileSpecPtr->vRefNum = catalogInfo.volume; fileSpecPtr->parID = catalogInfo.nodeID; if(DUtfToExternalDString(NULL, &path[cur], pos - cur, &ds) == TCL_ERROR) { err = coreFoundationUnknownErr; goto done; } strncpy(fileSpecPtr->name+1, Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); fileSpecPtr->name[0] = Tcl_DStringLength(&ds); Tcl_DStringFree(&ds); err = fnfErr; } done: Tcl_DStringFree(&ds); return err; } /* *---------------------------------------------------------------------- * * FSpPathFromLocation -- * * This function obtains a full path name for a given macintosh * FSSpec. Unlike the More Files function FSpGetFullPath, this * function will return a C string in the Handle. It also will * create paths for FSSpec that do not yet exist. * * Results: * OSErr code. * * Side effects: * None. * *---------------------------------------------------------------------- */ OSErr FSpPathFromLocation( FSSpecPtr spec, /* The location we want a path for. */ int *length, /* Length of the resulting path. */ Handle *fullPath) /* Handle to path. */ { OSErr err; FSSpec tempSpec; UInt8 fileName[MAXPATHLEN]; unsigned int fileNameLen; Boolean filenotexist=FALSE; FSRef fsref; *fullPath = NULL; err=FSpMakeFSRef(spec,&fsref); if ((err == noErr) || (filenotexist=(err == fnfErr))) { if (filenotexist) { // file does not exist, find parent dir memmove(&tempSpec, spec, sizeof(FSSpec)); tempSpec.name[0]=0; err=FSpMakeFSRef(&tempSpec,&fsref); } if (err == noErr) { err = FSRefMakePath(&fsref,fileName,MAXPATHLEN); if (err == noErr) { fileNameLen=strlen(fileName); if(filenotexist) { // add file name manually // need to convert spec name from macroman to utf8d before adding to fileName Tcl_DString ds; if (ExternalToDUtfDString(NULL,&spec->name[1], spec->name[0], &ds) == TCL_OK) { // bug 671 if(fileNameLen+Tcl_DStringLength(&ds)= 0) { SetSoundVolume(volume, SYS_BEEP_VOLUME); } SysBeep(1); /* * Reset Volume */ if (volume >= 0) { SetSoundVolume(0, RESET_VOLUME); } } else { strcpy((char *) sndName + 1, sndArg); sndName[0] = length; sound = GetNamedResource('snd ', sndName); if (sound != NULL) { /* * Set Volume for Default Output device */ if (volume >= 0) { SetSoundVolume(volume, DEFAULT_SND_VOLUME); } SndPlay(NULL, (SndListHandle) sound, false); /* * Reset Volume */ if (volume >= 0) { SetSoundVolume(0, RESET_VOLUME); } } else { Tcl_AppendStringsToObj(resultPtr, " \"", sndArg, "\" is not a valid sound. (Try ", Tcl_GetString(objv[0]), " -list)", NULL); return TCL_ERROR; } } return TCL_OK; beepUsage: Tcl_WrongNumArgs(interp, 1, objv, "[-volume num] [-list | sndName]?"); return TCL_ERROR; } /* *----------------------------------------------------------------------------- * * SetSoundVolume -- * * Set the volume for either the SysBeep or the SndPlay call depending * on the value of mode (SYS_BEEP_VOLUME or DEFAULT_SND_VOLUME * respectively. * * It also stores the last channel set, and the old value of its * VOLUME. If you call SetSoundVolume with a mode of RESET_VOLUME, * it will undo the last setting. The volume parameter is * ignored in this case. * * Side Effects: * Sets the System Volume * * Results: * None * *----------------------------------------------------------------------------- */ void SetSoundVolume( int volume, /* This is the new volume */ enum WhichVolume mode) /* This flag says which volume to * set: SysBeep, SndPlay, or instructs us * to reset the volume */ { static int hasSM3 = 1; static enum WhichVolume oldMode; static long oldVolume = -1; /* * If we don't have Sound Manager 3.0, we can't set the sound volume. * We will just ignore the request rather than raising an error. */ if (!hasSM3) { return; } switch (mode) { case SYS_BEEP_VOLUME: GetSysBeepVolume(&oldVolume); SetSysBeepVolume(volume); oldMode = SYS_BEEP_VOLUME; break; case DEFAULT_SND_VOLUME: GetDefaultOutputVolume(&oldVolume); SetDefaultOutputVolume(volume); oldMode = DEFAULT_SND_VOLUME; break; case RESET_VOLUME: /* * If oldVolume is -1 someone has made a programming error * and called reset before setting the volume. This is benign * however, so we will just exit. */ if (oldVolume != -1) { if (oldMode == SYS_BEEP_VOLUME) { SetSysBeepVolume(oldVolume); } else if (oldMode == DEFAULT_SND_VOLUME) { SetDefaultOutputVolume(oldVolume); } } oldVolume = -1; } } #endif