diff --git a/xpcom/io/nsLocalFileCommon.cpp b/xpcom/io/nsLocalFileCommon.cpp index 32f855dd8e5..3002046c8b5 100644 --- a/xpcom/io/nsLocalFileCommon.cpp +++ b/xpcom/io/nsLocalFileCommon.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -45,6 +45,8 @@ #include "nsReadableUtils.h" #include "nsPrintfCString.h" #include "nsCRT.h" +#include "nsNativeCharsetUtils.h" +#include "nsUTF8Utils.h" #ifdef XP_WIN #include @@ -77,40 +79,91 @@ nsLocalFile::InitWithFile(nsILocalFile *aFile) #define kMaxFilenameLength 255 #define kMaxExtensionLength 100 -// requirement: kMaxExtensionLength < kMaxFilenameLength - 4 +#define kMaxSequenceNumberLength 5 // "-9999" +// requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength NS_IMETHODIMP nsLocalFile::CreateUnique(PRUint32 type, PRUint32 attributes) { - nsresult rv = Create(type, attributes); - if (rv != NS_ERROR_FILE_ALREADY_EXISTS) + nsresult rv; + PRBool longName; + +#ifdef XP_WIN + nsAutoString pathName, leafName, rootName, suffix; + rv = GetPath(pathName); +#else + nsCAutoString pathName, leafName, rootName, suffix; + rv = GetNativePath(pathName); +#endif + if (NS_FAILED(rv)) return rv; - nsCAutoString leafName; + longName = (pathName.Length() + kMaxSequenceNumberLength > + kMaxFilenameLength); + if (!longName) + { + rv = Create(type, attributes); + if (rv != NS_ERROR_FILE_ALREADY_EXISTS) + return rv; + } + +#ifdef XP_WIN + rv = GetLeafName(leafName); + if (NS_FAILED(rv)) + return rv; + + const PRInt32 lastDot = leafName.RFindChar(PRUnichar('.')); +#else rv = GetNativeLeafName(leafName); if (NS_FAILED(rv)) return rv; - const char* lastDot = strrchr(leafName.get(), '.'); - char suffix[kMaxExtensionLength] = ""; - if (lastDot) + const PRInt32 lastDot = leafName.RFindChar('.'); +#endif + + if (lastDot == kNotFound) { - PL_strncpyz(suffix, lastDot, sizeof(suffix)); // include '.' - leafName.SetLength(lastDot - leafName.get()); // strip suffix and dot. + rootName = leafName; + } + else + { + suffix = Substring(leafName, lastDot); // include '.' + rootName = Substring(leafName, 0, lastDot); // strip suffix and dot } - PRUint32 maxRootLength = (kMaxFilenameLength - 4) - strlen(suffix) - 1; - - if (leafName.Length() > maxRootLength) - leafName.SetLength(maxRootLength); + if (longName) + { + PRUint32 maxRootLength = (kMaxFilenameLength - + (pathName.Length() - leafName.Length()) - + suffix.Length() - kMaxSequenceNumberLength); +#ifdef XP_WIN + // ensure that we don't cut the name in mid-UTF16-character + rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ? + maxRootLength - 1 : maxRootLength); + SetLeafName(rootName + suffix); +#else + if (NS_IsNativeUTF8()) + // ensure that we don't cut the name in mid-UTF8-character + while (UTF8traits::isInSeq(rootName[maxRootLength])) + --maxRootLength; + rootName.SetLength(maxRootLength); + SetNativeLeafName(rootName + suffix); +#endif + nsresult rv = Create(type, attributes); + if (rv != NS_ERROR_FILE_ALREADY_EXISTS) + return rv; + } for (int indx = 1; indx < 10000; indx++) { // start with "Picture-1.jpg" after "Picture.jpg" exists - SetNativeLeafName(leafName + - nsPrintfCString("-%d", indx) + - nsDependentCString(suffix)); - +#ifdef XP_WIN + SetLeafName(rootName + + NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) + + suffix); +#else + SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix); +#endif rv = Create(type, attributes); if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS) return rv; diff --git a/xpcom/tests/unit/test_bug364285-1.js b/xpcom/tests/unit/test_bug364285-1.js new file mode 100644 index 00000000000..347fcfe81c6 --- /dev/null +++ b/xpcom/tests/unit/test_bug364285-1.js @@ -0,0 +1,51 @@ +const Ci = Components.interfaces; +const Cc = Components.classes; + +var nameArray = [ + "ascii", // ASCII + "fran\u00E7ais", // Latin-1 + "\u0420\u0443\u0441\u0441\u043A\u0438\u0439", // Cyrillic + "\u65E5\u672C\u8A9E", // Japanese + "\u4E2D\u6587", // Chinese + "\uD55C\uAD6D\uC5B4", // Korean + "\uD801\uDC0F\uD801\uDC2D\uD801\uDC3B\uD801\uDC2B" // Deseret +]; + +function getTempDir() +{ + var dirService = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + return dirService.get("TmpD", Ci.nsILocalFile); +} + +function create_file(fileName) +{ + var outFile = getTempDir(); + outFile.append(fileName); + outFile.createUnique(outFile.NORMAL_FILE_TYPE, 0600); + + var stream = Cc["@mozilla.org/network/file-output-stream;1"] + .createInstance(Ci.nsIFileOutputStream); + stream.init(outFile, 0x02 | 0x08 | 0x20, 0600, 0); + stream.write("foo", 3); + stream.close(); + + do_check_eq(outFile.leafName.substr(0, fileName.length), fileName); + + return outFile; +} + +function test_create(fileName) +{ + var file1 = create_file(fileName); + var file2 = create_file(fileName); + file1.remove(false); + file2.remove(false); +} + +function run_test() +{ + for (var i = 0; i < nameArray.length; ++i) { + test_create(nameArray[i]); + } +}