Back to index

lightning-sunbird  0.9+nobinonly
nsFileSpecUnix.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *  Henry Sobotka <sobotka@axess.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038  
00039 //    This file is included by nsFileSpec.cpp, and includes the Unix-specific
00040 //    implementations.
00041 
00042 #include <sys/stat.h>
00043 #include <sys/param.h>
00044 #include <errno.h>
00045 #include <dirent.h>
00046 #include <unistd.h>
00047 #include <stdlib.h>
00048 #include <limits.h>
00049 #include "xpcom-private.h"
00050 #include "nsError.h"
00051 #include "prio.h"   /* for PR_Rename */
00052 #include "nsAutoBuffer.h"
00053 
00054 #if defined(_SCO_DS)
00055 #define _SVID3  /* for statvfs.h */
00056 #endif
00057 
00058 #ifdef HAVE_SYS_STATVFS_H
00059 #include <sys/statvfs.h>
00060 #endif
00061 
00062 #ifdef HAVE_SYS_VFS_H
00063 #include <sys/vfs.h>
00064 #endif
00065 
00066 #ifdef HAVE_SYS_STATFS_H
00067 #include <sys/statfs.h>
00068 #endif
00069 
00070 #ifdef HAVE_SYS_MOUNT_H
00071 #include <sys/mount.h>
00072 #undef Free
00073 #endif
00074 
00075 #ifdef HAVE_STATVFS
00076 #define STATFS       statvfs
00077 #else
00078 #define STATFS       statfs
00079 #endif
00080 
00081 #ifndef MAXPATHLEN
00082 #define MAXPATHLEN   1024  /* Guessing this is okay.  Works for SCO. */
00083 #endif
00084  
00085 #if defined(__QNX__)
00086 #include <unix.h>    /* for realpath */
00087 #define f_bavail     f_bfree
00088 extern "C" int truncate(const char *, off_t);
00089 #endif
00090 
00091 #if defined(SUNOS4)
00092 extern "C" int statfs(char *, struct statfs *);
00093 #endif
00094 
00095 #if defined(OSF1)
00096 extern "C" int statvfs(const char *, struct statvfs *);
00097 #endif
00098 
00099 #ifdef XP_MACOSX
00100 static void  CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult);
00101 #endif
00102 
00103 //----------------------------------------------------------------------------------------
00104 void nsFileSpecHelpers::Canonify(nsSimpleCharString& ioPath, PRBool inMakeDirs)
00105 // Canonify, make absolute, and check whether directories exist
00106 //----------------------------------------------------------------------------------------
00107 {
00108     if (ioPath.IsEmpty())
00109         return;
00110     if (inMakeDirs)
00111     {
00112         const mode_t mode = 0755;
00113         nsFileSpecHelpers::MakeAllDirectories((const char*)ioPath, mode);
00114     }
00115 
00116     errno = 0;  // needed?
00117 
00118     if (ioPath[0] != '/')
00119     {
00120         // the ioPath that was passed in is relative.  We must cat it to the cwd.
00121         char buffer[MAXPATHLEN];
00122 
00123         (void) getcwd(buffer, MAXPATHLEN);
00124 
00125         strcat(buffer, "/");
00126         strcat(buffer, ioPath);
00127 
00128         ioPath = buffer;
00129     }
00130 } // nsFileSpecHelpers::Canonify
00131 
00132 //----------------------------------------------------------------------------------------
00133 void nsFileSpec::SetLeafName(const char* inLeafName)
00134 //----------------------------------------------------------------------------------------
00135 {
00136     mPath.LeafReplace('/', inLeafName);
00137 } // nsFileSpec::SetLeafName
00138 
00139 //----------------------------------------------------------------------------------------
00140 char* nsFileSpec::GetLeafName() const
00141 //----------------------------------------------------------------------------------------
00142 {
00143 #ifndef XP_MACOSX
00144     return mPath.GetLeaf('/');
00145 #else
00146     char *name = mPath.GetLeaf('/');
00147     if (!name || !*name)
00148         return name;
00149     nsAutoString nameInNFC;
00150     CopyUTF8toUTF16NFC(nsDependentCString(name), nameInNFC);
00151     nsCRT::free(name);
00152     return nsCRT::strdup(NS_ConvertUTF16toUTF8(nameInNFC).get());
00153 #endif
00154 } // nsFileSpec::GetLeafName
00155 
00156 //----------------------------------------------------------------------------------------
00157 PRBool nsFileSpec::Exists() const
00158 //----------------------------------------------------------------------------------------
00159 {
00160     return !mPath.IsEmpty() && 0 == access(mPath, F_OK); 
00161 } // nsFileSpec::Exists
00162 
00163 //----------------------------------------------------------------------------------------
00164 void nsFileSpec::GetModDate(TimeStamp& outStamp) const
00165 //----------------------------------------------------------------------------------------
00166 {
00167        struct stat st;
00168     if (!mPath.IsEmpty() && stat(mPath, &st) == 0) 
00169         outStamp = st.st_mtime; 
00170     else
00171         outStamp = 0;
00172 } // nsFileSpec::GetModDate
00173 
00174 //----------------------------------------------------------------------------------------
00175 PRUint32 nsFileSpec::GetFileSize() const
00176 //----------------------------------------------------------------------------------------
00177 {
00178        struct stat st;
00179     if (!mPath.IsEmpty() && stat(mPath, &st) == 0) 
00180         return (PRUint32)st.st_size; 
00181     return 0;
00182 } // nsFileSpec::GetFileSize
00183 
00184 //----------------------------------------------------------------------------------------
00185 PRBool nsFileSpec::IsFile() const
00186 //----------------------------------------------------------------------------------------
00187 {
00188     struct stat st;
00189     return !mPath.IsEmpty() && stat(mPath, &st) == 0 && S_ISREG(st.st_mode); 
00190 } // nsFileSpec::IsFile
00191 
00192 //----------------------------------------------------------------------------------------
00193 PRBool nsFileSpec::IsDirectory() const
00194 //----------------------------------------------------------------------------------------
00195 {
00196     struct stat st;
00197     return !mPath.IsEmpty() && 0 == stat(mPath, &st) && S_ISDIR(st.st_mode); 
00198 } // nsFileSpec::IsDirectory
00199 
00200 //----------------------------------------------------------------------------------------
00201 PRBool nsFileSpec::IsHidden() const
00202 //----------------------------------------------------------------------------------------
00203 {
00204     PRBool hidden = PR_FALSE;
00205     char *leafname = GetLeafName();
00206     if (nsnull != leafname)
00207     {
00208        // rjc: don't return ".", "..", or any file/directory that begins with a "."
00209 
00210        /*        if ((!strcmp(leafname, ".")) || (!strcmp(leafname, "..")))  */
00211        if (leafname[0] == '.')
00212         {
00213             hidden = PR_TRUE;
00214         }
00215         nsCRT::free(leafname);
00216     }
00217     return hidden;
00218 } // nsFileSpec::IsHidden
00219 
00220 //----------------------------------------------------------------------------------------
00221 PRBool nsFileSpec::IsSymlink() const
00222 //----------------------------------------------------------------------------------------
00223 {
00224     struct stat st;
00225     if (!mPath.IsEmpty() && stat(mPath, &st) == 0 && S_ISLNK(st.st_mode))
00226         return PR_TRUE;
00227 
00228     return PR_FALSE;
00229 } // nsFileSpec::IsSymlink
00230 
00231 //----------------------------------------------------------------------------------------
00232 nsresult nsFileSpec::ResolveSymlink(PRBool& wasAliased)
00233 //----------------------------------------------------------------------------------------
00234 {
00235     wasAliased = PR_FALSE;
00236 
00237     char resolvedPath[MAXPATHLEN];
00238     int charCount = readlink(mPath, (char*)&resolvedPath, MAXPATHLEN);
00239     if (0 < charCount)
00240     {
00241         if (MAXPATHLEN > charCount)
00242             resolvedPath[charCount] = '\0';
00243         
00244         wasAliased = PR_TRUE;
00245 
00246        /* if it's not an absolute path,
00247               replace the leaf with what got resolved */  
00248         if (resolvedPath[0] != '/') {
00249               SetLeafName(resolvedPath);
00250         }
00251         else {
00252               mPath = (char*)&resolvedPath;
00253         }
00254        
00255        char* canonicalPath = realpath((const char *)mPath, resolvedPath);
00256        NS_ASSERTION(canonicalPath, "realpath failed");
00257        if (canonicalPath) {
00258               mPath = (char*)&resolvedPath;
00259        }
00260        else {
00261               return NS_ERROR_FAILURE;
00262        }
00263     }
00264     
00265     return NS_OK;
00266 } // nsFileSpec::ResolveSymlink
00267 
00268 
00269 //----------------------------------------------------------------------------------------
00270 void nsFileSpec::GetParent(nsFileSpec& outSpec) const
00271 //----------------------------------------------------------------------------------------
00272 {
00273     outSpec.mPath = mPath;
00274        char* chars = (char*)outSpec.mPath;
00275        chars[outSpec.mPath.Length() - 1] = '\0'; // avoid trailing separator, if any
00276     char* cp = strrchr(chars, '/');
00277     if (cp++)
00278            outSpec.mPath.SetLength(cp - chars); // truncate.
00279 } // nsFileSpec::GetParent
00280 
00281 //----------------------------------------------------------------------------------------
00282 void nsFileSpec::operator += (const char* inRelativePath)
00283 //----------------------------------------------------------------------------------------
00284 {
00285        NS_ASSERTION(inRelativePath, "Attempt to do += with a null string");
00286 
00287     if (!inRelativePath || mPath.IsEmpty())
00288         return;
00289     
00290     char endChar = mPath[(int)(strlen(mPath) - 1)];
00291     if (endChar == '/')
00292         mPath += "x";
00293     else
00294         mPath += "/x";
00295     SetLeafName(inRelativePath);
00296 } // nsFileSpec::operator +=
00297 
00298 //----------------------------------------------------------------------------------------
00299 void nsFileSpec::CreateDirectory(int mode)
00300 //----------------------------------------------------------------------------------------
00301 {
00302     // Note that mPath is canonical!
00303     if (mPath.IsEmpty())
00304         return;
00305     mkdir(mPath, mode);
00306 } // nsFileSpec::CreateDirectory
00307 
00308 //----------------------------------------------------------------------------------------
00309 void nsFileSpec::Delete(PRBool inRecursive) const
00310 // To check if this worked, call Exists() afterwards, see?
00311 //----------------------------------------------------------------------------------------
00312 {
00313     if (IsDirectory())
00314     {
00315         if (inRecursive)
00316         {
00317             for (nsDirectoryIterator i(*this, PR_FALSE); i.Exists(); i++)
00318             {
00319                 nsFileSpec& child = (nsFileSpec&)i;
00320                 child.Delete(inRecursive);
00321             }        
00322         }
00323         rmdir(mPath);
00324     }
00325     else if (!mPath.IsEmpty())
00326         remove(mPath);
00327 } // nsFileSpec::Delete
00328 
00329 //----------------------------------------------------------------------------------------
00330 void nsFileSpec::RecursiveCopy(nsFileSpec newDir) const
00331 //----------------------------------------------------------------------------------------
00332 {
00333     if (IsDirectory())
00334     {
00335               if (!(newDir.Exists()))
00336               {
00337                      newDir.CreateDirectory();
00338               }
00339 
00340               for (nsDirectoryIterator i(*this, PR_FALSE); i.Exists(); i++)
00341               {
00342                      nsFileSpec& child = (nsFileSpec&)i;
00343 
00344                      if (child.IsDirectory())
00345                      {
00346                             nsFileSpec tmpDirSpec(newDir);
00347 
00348                             char *leafname = child.GetLeafName();
00349                             tmpDirSpec += leafname;
00350                             nsCRT::free(leafname);
00351 
00352                             child.RecursiveCopy(tmpDirSpec);
00353                      }
00354                      else
00355                      {
00356                             child.RecursiveCopy(newDir);
00357                      }
00358               }
00359     }
00360     else if (!mPath.IsEmpty())
00361     {
00362               nsFileSpec& filePath = (nsFileSpec&) *this;
00363 
00364               if (!(newDir.Exists()))
00365               {
00366                      newDir.CreateDirectory();
00367               }
00368 
00369         filePath.CopyToDir(newDir);
00370     }
00371 } // nsFileSpec::RecursiveCopy
00372 
00373 
00374 //----------------------------------------------------------------------------------------
00375 nsresult nsFileSpec::Truncate(PRInt32 offset) const
00376 //----------------------------------------------------------------------------------------
00377 {
00378     char* Path = nsCRT::strdup(mPath);
00379 
00380     int rv = truncate(Path, offset) ;
00381 
00382     nsCRT::free(Path) ;
00383 
00384     if(!rv) 
00385         return NS_OK ;
00386     else
00387         return NS_ERROR_FAILURE ;
00388 } // nsFileSpec::Truncate
00389 
00390 //----------------------------------------------------------------------------------------
00391 nsresult nsFileSpec::Rename(const char* inNewName)
00392 //----------------------------------------------------------------------------------------
00393 {
00394        NS_ASSERTION(inNewName, "Attempt to Rename with a null string");
00395 
00396     // This function should not be used to move a file on disk. 
00397     if (mPath.IsEmpty() || strchr(inNewName, '/')) 
00398         return NS_FILE_FAILURE;
00399 
00400     char* oldPath = nsCRT::strdup(mPath);
00401     
00402     SetLeafName(inNewName); 
00403 
00404     if (PR_Rename(oldPath, mPath) != NS_OK)
00405     {
00406         // Could not rename, set back to the original.
00407         mPath = oldPath;
00408         nsCRT::free(oldPath);
00409         return NS_FILE_FAILURE;
00410     }
00411     
00412     nsCRT::free(oldPath);
00413 
00414     return NS_OK;
00415 } // nsFileSpec::Rename
00416 
00417 //----------------------------------------------------------------------------------------
00418 static int CrudeFileCopy(const char* in, const char* out)
00419 //----------------------------------------------------------------------------------------
00420 {
00421        struct stat in_stat;
00422        int stat_result = -1;
00423 
00424        char   buf [1024];
00425        FILE   *ifp, *ofp;
00426        int    rbytes, wbytes;
00427 
00428        if (!in || !out)
00429               return -1;
00430 
00431        stat_result = stat (in, &in_stat);
00432 
00433        ifp = fopen (in, "r");
00434        if (!ifp) 
00435        {
00436               return -1;
00437        }
00438 
00439        ofp = fopen (out, "w");
00440        if (!ofp)
00441        {
00442               fclose (ifp);
00443               return -1;
00444        }
00445 
00446        while ((rbytes = fread (buf, 1, sizeof(buf), ifp)) > 0)
00447        {
00448               while (rbytes > 0)
00449               {
00450                      if ( (wbytes = fwrite (buf, 1, rbytes, ofp)) < 0 )
00451                      {
00452                             fclose (ofp);
00453                             fclose (ifp);
00454                             unlink(out);
00455                             return -1;
00456                      }
00457                      rbytes -= wbytes;
00458               }
00459        }
00460        fclose (ofp);
00461        fclose (ifp);
00462 
00463        if (stat_result == 0)
00464               chmod (out, in_stat.st_mode & 0777);
00465 
00466        return 0;
00467 } // nsFileSpec::Rename
00468 
00469 //----------------------------------------------------------------------------------------
00470 nsresult nsFileSpec::CopyToDir(const nsFileSpec& inParentDirectory) const
00471 //----------------------------------------------------------------------------------------
00472 {
00473     // We can only copy into a directory, and (for now) can not copy entire directories
00474     nsresult result = NS_FILE_FAILURE;
00475 
00476     if (inParentDirectory.IsDirectory() && (! IsDirectory() ) )
00477     {
00478         char *leafname = GetLeafName();
00479         nsSimpleCharString destPath(inParentDirectory.GetCString());
00480         destPath += "/";
00481         destPath += leafname;
00482         nsCRT::free(leafname);
00483         result = NS_FILE_RESULT(CrudeFileCopy(GetCString(), destPath));
00484     }
00485     return result;
00486 } // nsFileSpec::CopyToDir
00487 
00488 //----------------------------------------------------------------------------------------
00489 nsresult nsFileSpec::MoveToDir(const nsFileSpec& inNewParentDirectory)
00490 //----------------------------------------------------------------------------------------
00491 {
00492     // We can only copy into a directory, and (for now) can not copy entire directories
00493     nsresult result = NS_FILE_FAILURE;
00494 
00495     if (inNewParentDirectory.IsDirectory() && !IsDirectory())
00496     {
00497         char *leafname = GetLeafName();
00498         nsSimpleCharString destPath(inNewParentDirectory.GetCString());
00499         destPath += "/";
00500         destPath += leafname;
00501         nsCRT::free(leafname);
00502 
00503         result = NS_FILE_RESULT(CrudeFileCopy(GetCString(), (const char*)destPath));
00504         if (result == NS_OK)
00505         {
00506             // cast to fix const-ness
00507                   ((nsFileSpec*)this)->Delete(PR_FALSE);
00508         
00509             *this = inNewParentDirectory + GetLeafName(); 
00510        }
00511     }
00512     return result;
00513 } 
00514 
00515 //----------------------------------------------------------------------------------------
00516 nsresult nsFileSpec::Execute(const char* inArgs ) const
00517 //----------------------------------------------------------------------------------------
00518 {
00519     nsresult result = NS_FILE_FAILURE;
00520     
00521     if (!mPath.IsEmpty() && !IsDirectory())
00522     {
00523         nsSimpleCharString fileNameWithArgs = mPath + " " + inArgs;
00524         result = NS_FILE_RESULT(system(fileNameWithArgs));
00525     } 
00526 
00527     return result;
00528 
00529 } // nsFileSpec::Execute
00530 
00531 //----------------------------------------------------------------------------------------
00532 PRInt64 nsFileSpec::GetDiskSpaceAvailable() const
00533 //----------------------------------------------------------------------------------------
00534 {
00535     PRInt64 bytes; /* XXX dougt needs to fix this */
00536     LL_I2L(bytes , LONG_MAX); // initialize to all the space in the world?
00537 
00538 
00539 #if defined(HAVE_SYS_STATFS_H) || defined(HAVE_SYS_STATVFS_H)
00540 
00541     char curdir [MAXPATHLEN];
00542     if (mPath.IsEmpty())
00543     {
00544         (void) getcwd(curdir, MAXPATHLEN);
00545         if (!curdir)
00546             return bytes;  /* hope for the best as we did in cheddar */
00547     }
00548     else
00549         sprintf(curdir, "%.200s", (const char*)mPath);
00550  
00551     struct STATFS fs_buf;
00552 #if defined(__QNX__) && !defined(HAVE_STATVFS) /* Maybe this should be handled differently? */
00553     if (STATFS(curdir, &fs_buf, 0, 0) < 0)
00554 #else
00555     if (STATFS(curdir, &fs_buf) < 0)
00556 #endif
00557         return bytes; /* hope for the best as we did in cheddar */
00558  
00559 #ifdef DEBUG_DISK_SPACE
00560     printf("DiskSpaceAvailable: %d bytes\n", 
00561        fs_buf.f_bsize * (fs_buf.f_bavail - 1));
00562 #endif
00563 
00564     PRInt64 bsize,bavail;
00565     LL_I2L( bsize,  fs_buf.f_bsize );
00566     LL_I2L( bavail, fs_buf.f_bavail - 1 );
00567     LL_MUL( bytes, bsize, bavail );
00568     return bytes;
00569 
00570 #else 
00571     /*
00572     ** This platform doesn't have statfs or statvfs, so we don't have much
00573     ** choice but to "hope for the best as we did in cheddar".
00574     */
00575     return bytes;
00576 #endif /* HAVE_SYS_STATFS_H or HAVE_SYS_STATVFS_H */
00577 
00578 } // nsFileSpec::GetDiskSpace()
00579 
00580 //========================================================================================
00581 //                                nsDirectoryIterator
00582 //========================================================================================
00583 
00584 //----------------------------------------------------------------------------------------
00585 nsDirectoryIterator::nsDirectoryIterator(const nsFileSpec& inDirectory, PRBool resolveSymLinks)
00586 //----------------------------------------------------------------------------------------
00587     : mCurrent(inDirectory)
00588     , mExists(PR_FALSE)
00589     , mResoveSymLinks(resolveSymLinks)
00590     , mStarting(inDirectory)
00591     , mDir(nsnull)
00592 
00593 {
00594     mStarting += "sysygy"; // save off the starting directory 
00595     mCurrent += "sysygy"; // prepare the path for SetLeafName
00596     mDir = opendir((const char*)nsFilePath(inDirectory));
00597     ++(*this);
00598 } // nsDirectoryIterator::nsDirectoryIterator
00599 
00600 //----------------------------------------------------------------------------------------
00601 nsDirectoryIterator::~nsDirectoryIterator()
00602 //----------------------------------------------------------------------------------------
00603 {
00604     if (mDir)
00605         closedir(mDir);
00606 } // nsDirectoryIterator::nsDirectoryIterator
00607 
00608 //----------------------------------------------------------------------------------------
00609 nsDirectoryIterator& nsDirectoryIterator::operator ++ ()
00610 //----------------------------------------------------------------------------------------
00611 {
00612     mExists = PR_FALSE;
00613     if (!mDir)
00614         return *this;
00615     const char dot[]    = ".";
00616     const char dotdot[] = "..";
00617     struct dirent* entry = readdir(mDir);
00618     if (entry && strcmp(entry->d_name, dot) == 0)
00619         entry = readdir(mDir);
00620     if (entry && strcmp(entry->d_name, dotdot) == 0)
00621         entry = readdir(mDir);
00622     if (entry)
00623     {
00624         mExists = PR_TRUE;
00625        mCurrent = mStarting;       // restore mCurrent to be the starting directory.  ResolveSymlink() may have taken us to another directory
00626         mCurrent.SetLeafName(entry->d_name);
00627         if (mResoveSymLinks)
00628         {   
00629             PRBool ignore;
00630             mCurrent.ResolveSymlink(ignore);
00631         }
00632     }
00633     return *this;
00634 } // nsDirectoryIterator::operator ++
00635 
00636 //----------------------------------------------------------------------------------------
00637 nsDirectoryIterator& nsDirectoryIterator::operator -- ()
00638 //----------------------------------------------------------------------------------------
00639 {
00640     return ++(*this); // can't do it backwards.
00641 } // nsDirectoryIterator::operator --
00642 
00643 // Convert a UTF-8 string to a UTF-16 string while normalizing to
00644 // Normalization Form C (composed Unicode). We need this because
00645 // Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode)
00646 // while most other OS', server-side programs usually expect NFC.
00647 
00648 #ifdef XP_MACOSX
00649 typedef void (*UnicodeNormalizer) (CFMutableStringRef, CFStringNormalizationForm);
00650 static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult)
00651 {
00652     static PRBool sChecked = PR_FALSE;
00653     static UnicodeNormalizer sUnicodeNormalizer = NULL;
00654 
00655     // CFStringNormalize was not introduced until Mac OS 10.2
00656     if (!sChecked) {
00657         CFBundleRef carbonBundle =
00658             CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon"));
00659         if (carbonBundle)
00660             sUnicodeNormalizer = (UnicodeNormalizer)
00661                 ::CFBundleGetFunctionPointerForName(carbonBundle,
00662                                                     CFSTR("CFStringNormalize"));
00663         sChecked = PR_TRUE;
00664     }
00665 
00666     if (!sUnicodeNormalizer) {  // OS X 10.1 or earlier
00667         CopyUTF8toUTF16(aSrc, aResult);
00668         return;  
00669     }
00670 
00671     const nsAFlatCString &inFlatSrc = PromiseFlatCString(aSrc);
00672 
00673     // The number of 16bit code units in a UTF-16 string will never be
00674     // larger than the number of bytes in the corresponding UTF-8 string.
00675     CFMutableStringRef inStr =
00676         ::CFStringCreateMutable(NULL, inFlatSrc.Length());
00677 
00678     if (!inStr) {
00679         CopyUTF8toUTF16(aSrc, aResult);
00680         return;  
00681     }
00682      
00683     ::CFStringAppendCString(inStr, inFlatSrc.get(), kCFStringEncodingUTF8); 
00684 
00685     sUnicodeNormalizer(inStr, kCFStringNormalizationFormC);
00686 
00687     CFIndex length = CFStringGetLength(inStr);
00688     const UniChar* chars = CFStringGetCharactersPtr(inStr);
00689 
00690     if (chars) 
00691         aResult.Assign(chars, length);
00692     else {
00693         nsAutoBuffer<UniChar, 512> buffer;
00694         if (buffer.EnsureElemCapacity(length)) {
00695             CFStringGetCharacters(inStr, CFRangeMake(0, length), buffer.get());
00696             aResult.Assign(buffer.get(), length);
00697         }
00698         else 
00699             CopyUTF8toUTF16(aSrc, aResult);
00700     }
00701     CFRelease(inStr);
00702 }
00703 #endif