1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/io/nsLocalFileCommon.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,292 @@ 1.4 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 +#include "nsIServiceManager.h" 1.9 + 1.10 +#include "nsLocalFile.h" // includes platform-specific headers 1.11 + 1.12 +#include "nsString.h" 1.13 +#include "nsCOMPtr.h" 1.14 +#include "nsReadableUtils.h" 1.15 +#include "nsPrintfCString.h" 1.16 +#include "nsCRT.h" 1.17 +#include "nsNativeCharsetUtils.h" 1.18 +#include "nsUTF8Utils.h" 1.19 + 1.20 +#ifdef XP_WIN 1.21 +#include <string.h> 1.22 +#endif 1.23 + 1.24 + 1.25 +void NS_StartupLocalFile() 1.26 +{ 1.27 + nsLocalFile::GlobalInit(); 1.28 +} 1.29 + 1.30 +void NS_ShutdownLocalFile() 1.31 +{ 1.32 + nsLocalFile::GlobalShutdown(); 1.33 +} 1.34 + 1.35 +#if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN) 1.36 +NS_IMETHODIMP 1.37 +nsLocalFile::InitWithFile(nsIFile *aFile) 1.38 +{ 1.39 + if (NS_WARN_IF(!aFile)) 1.40 + return NS_ERROR_INVALID_ARG; 1.41 + 1.42 + nsAutoCString path; 1.43 + aFile->GetNativePath(path); 1.44 + if (path.IsEmpty()) 1.45 + return NS_ERROR_INVALID_ARG; 1.46 + return InitWithNativePath(path); 1.47 +} 1.48 +#endif 1.49 + 1.50 +#define kMaxFilenameLength 255 1.51 +#define kMaxExtensionLength 100 1.52 +#define kMaxSequenceNumberLength 5 // "-9999" 1.53 +// requirement: kMaxExtensionLength < kMaxFilenameLength - kMaxSequenceNumberLength 1.54 + 1.55 +NS_IMETHODIMP 1.56 +nsLocalFile::CreateUnique(uint32_t type, uint32_t attributes) 1.57 +{ 1.58 + nsresult rv; 1.59 + bool longName; 1.60 + 1.61 +#ifdef XP_WIN 1.62 + nsAutoString pathName, leafName, rootName, suffix; 1.63 + rv = GetPath(pathName); 1.64 +#else 1.65 + nsAutoCString pathName, leafName, rootName, suffix; 1.66 + rv = GetNativePath(pathName); 1.67 +#endif 1.68 + if (NS_FAILED(rv)) 1.69 + return rv; 1.70 + 1.71 + longName = (pathName.Length() + kMaxSequenceNumberLength > 1.72 + kMaxFilenameLength); 1.73 + if (!longName) 1.74 + { 1.75 + rv = Create(type, attributes); 1.76 + if (rv != NS_ERROR_FILE_ALREADY_EXISTS) 1.77 + return rv; 1.78 + } 1.79 + 1.80 +#ifdef XP_WIN 1.81 + rv = GetLeafName(leafName); 1.82 + if (NS_FAILED(rv)) 1.83 + return rv; 1.84 + 1.85 + const int32_t lastDot = leafName.RFindChar(char16_t('.')); 1.86 +#else 1.87 + rv = GetNativeLeafName(leafName); 1.88 + if (NS_FAILED(rv)) 1.89 + return rv; 1.90 + 1.91 + const int32_t lastDot = leafName.RFindChar('.'); 1.92 +#endif 1.93 + 1.94 + if (lastDot == kNotFound) 1.95 + { 1.96 + rootName = leafName; 1.97 + } 1.98 + else 1.99 + { 1.100 + suffix = Substring(leafName, lastDot); // include '.' 1.101 + rootName = Substring(leafName, 0, lastDot); // strip suffix and dot 1.102 + } 1.103 + 1.104 + if (longName) 1.105 + { 1.106 + int32_t maxRootLength = (kMaxFilenameLength - 1.107 + (pathName.Length() - leafName.Length()) - 1.108 + suffix.Length() - kMaxSequenceNumberLength); 1.109 + 1.110 + // We cannot create an item inside a directory whose name is too long. 1.111 + // Also, ensure that at least one character remains after we truncate 1.112 + // the root name, as we don't want to end up with an empty leaf name. 1.113 + if (maxRootLength < 2) 1.114 + return NS_ERROR_FILE_UNRECOGNIZED_PATH; 1.115 + 1.116 +#ifdef XP_WIN 1.117 + // ensure that we don't cut the name in mid-UTF16-character 1.118 + rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength]) ? 1.119 + maxRootLength - 1 : maxRootLength); 1.120 + SetLeafName(rootName + suffix); 1.121 +#else 1.122 + if (NS_IsNativeUTF8()) 1.123 + { 1.124 + // ensure that we don't cut the name in mid-UTF8-character 1.125 + // (assume the name is valid UTF8 to begin with) 1.126 + while (UTF8traits::isInSeq(rootName[maxRootLength])) 1.127 + --maxRootLength; 1.128 + 1.129 + // Another check to avoid ending up with an empty leaf name. 1.130 + if (maxRootLength == 0 && suffix.IsEmpty()) 1.131 + return NS_ERROR_FILE_UNRECOGNIZED_PATH; 1.132 + } 1.133 + 1.134 + rootName.SetLength(maxRootLength); 1.135 + SetNativeLeafName(rootName + suffix); 1.136 +#endif 1.137 + nsresult rv = Create(type, attributes); 1.138 + if (rv != NS_ERROR_FILE_ALREADY_EXISTS) 1.139 + return rv; 1.140 + } 1.141 + 1.142 + for (int indx = 1; indx < 10000; indx++) 1.143 + { 1.144 + // start with "Picture-1.jpg" after "Picture.jpg" exists 1.145 +#ifdef XP_WIN 1.146 + SetLeafName(rootName + 1.147 + NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) + 1.148 + suffix); 1.149 +#else 1.150 + SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix); 1.151 +#endif 1.152 + rv = Create(type, attributes); 1.153 + if (NS_SUCCEEDED(rv) || rv != NS_ERROR_FILE_ALREADY_EXISTS) 1.154 + return rv; 1.155 + } 1.156 + 1.157 + // The disk is full, sort of 1.158 + return NS_ERROR_FILE_TOO_BIG; 1.159 +} 1.160 + 1.161 +#if defined(XP_WIN) 1.162 +static const char16_t kPathSeparatorChar = '\\'; 1.163 +#elif defined(XP_UNIX) 1.164 +static const char16_t kPathSeparatorChar = '/'; 1.165 +#else 1.166 +#error Need to define file path separator for your platform 1.167 +#endif 1.168 + 1.169 +static int32_t SplitPath(char16_t *path, char16_t **nodeArray, int32_t arrayLen) 1.170 +{ 1.171 + if (*path == 0) 1.172 + return 0; 1.173 + 1.174 + char16_t **nodePtr = nodeArray; 1.175 + if (*path == kPathSeparatorChar) 1.176 + path++; 1.177 + *nodePtr++ = path; 1.178 + 1.179 + for (char16_t *cp = path; *cp != 0; cp++) { 1.180 + if (*cp == kPathSeparatorChar) { 1.181 + *cp++ = 0; 1.182 + if (*cp == 0) 1.183 + break; 1.184 + if (nodePtr - nodeArray >= arrayLen) 1.185 + return -1; 1.186 + *nodePtr++ = cp; 1.187 + } 1.188 + } 1.189 + return nodePtr - nodeArray; 1.190 +} 1.191 + 1.192 + 1.193 +NS_IMETHODIMP 1.194 +nsLocalFile::GetRelativeDescriptor(nsIFile *fromFile, nsACString& _retval) 1.195 +{ 1.196 + if (NS_WARN_IF(!fromFile)) 1.197 + return NS_ERROR_INVALID_ARG; 1.198 + const int32_t kMaxNodesInPath = 32; 1.199 + 1.200 + // 1.201 + // _retval will be UTF-8 encoded 1.202 + // 1.203 + 1.204 + nsresult rv; 1.205 + _retval.Truncate(0); 1.206 + 1.207 + nsAutoString thisPath, fromPath; 1.208 + char16_t *thisNodes[kMaxNodesInPath], *fromNodes[kMaxNodesInPath]; 1.209 + int32_t thisNodeCnt, fromNodeCnt, nodeIndex; 1.210 + 1.211 + rv = GetPath(thisPath); 1.212 + if (NS_FAILED(rv)) 1.213 + return rv; 1.214 + rv = fromFile->GetPath(fromPath); 1.215 + if (NS_FAILED(rv)) 1.216 + return rv; 1.217 + 1.218 + // get raw pointer to mutable string buffer 1.219 + char16_t *thisPathPtr; thisPath.BeginWriting(thisPathPtr); 1.220 + char16_t *fromPathPtr; fromPath.BeginWriting(fromPathPtr); 1.221 + 1.222 + thisNodeCnt = SplitPath(thisPathPtr, thisNodes, kMaxNodesInPath); 1.223 + fromNodeCnt = SplitPath(fromPathPtr, fromNodes, kMaxNodesInPath); 1.224 + if (thisNodeCnt < 0 || fromNodeCnt < 0) 1.225 + return NS_ERROR_FAILURE; 1.226 + 1.227 + for (nodeIndex = 0; nodeIndex < thisNodeCnt && nodeIndex < fromNodeCnt; ++nodeIndex) { 1.228 +#ifdef XP_WIN 1.229 + if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]), char16ptr_t(fromNodes[nodeIndex]))) 1.230 + break; 1.231 +#else 1.232 + if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) 1.233 + break; 1.234 +#endif 1.235 + } 1.236 + 1.237 + int32_t branchIndex = nodeIndex; 1.238 + for (nodeIndex = branchIndex; nodeIndex < fromNodeCnt; nodeIndex++) 1.239 + _retval.AppendLiteral("../"); 1.240 + for (nodeIndex = branchIndex; nodeIndex < thisNodeCnt; nodeIndex++) { 1.241 + NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]); 1.242 + _retval.Append(nodeStr); 1.243 + if (nodeIndex + 1 < thisNodeCnt) 1.244 + _retval.Append('/'); 1.245 + } 1.246 + 1.247 + return NS_OK; 1.248 +} 1.249 + 1.250 +NS_IMETHODIMP 1.251 +nsLocalFile::SetRelativeDescriptor(nsIFile *fromFile, const nsACString& relativeDesc) 1.252 +{ 1.253 + NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../"); 1.254 + 1.255 + nsCOMPtr<nsIFile> targetFile; 1.256 + nsresult rv = fromFile->Clone(getter_AddRefs(targetFile)); 1.257 + if (NS_FAILED(rv)) 1.258 + return rv; 1.259 + 1.260 + // 1.261 + // relativeDesc is UTF-8 encoded 1.262 + // 1.263 + 1.264 + nsCString::const_iterator strBegin, strEnd; 1.265 + relativeDesc.BeginReading(strBegin); 1.266 + relativeDesc.EndReading(strEnd); 1.267 + 1.268 + nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd); 1.269 + nsCString::const_iterator pos(strBegin); 1.270 + 1.271 + nsCOMPtr<nsIFile> parentDir; 1.272 + while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) { 1.273 + rv = targetFile->GetParent(getter_AddRefs(parentDir)); 1.274 + if (NS_FAILED(rv)) 1.275 + return rv; 1.276 + if (!parentDir) 1.277 + return NS_ERROR_FILE_UNRECOGNIZED_PATH; 1.278 + targetFile = parentDir; 1.279 + 1.280 + nodeBegin = nodeEnd; 1.281 + pos = nodeEnd; 1.282 + nodeEnd = strEnd; 1.283 + } 1.284 + 1.285 + nodeBegin = nodeEnd = pos; 1.286 + while (nodeEnd != strEnd) { 1.287 + FindCharInReadable('/', nodeEnd, strEnd); 1.288 + targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd))); 1.289 + if (nodeEnd != strEnd) // If there's more left in the string, inc over the '/' nodeEnd is on. 1.290 + ++nodeEnd; 1.291 + nodeBegin = nodeEnd; 1.292 + } 1.293 + 1.294 + return InitWithFile(targetFile); 1.295 +}