Blame xpcom/io/nsLocalFileCommon.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Packit f0b94e
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "nsIServiceManager.h"
Packit f0b94e
Packit f0b94e
#include "nsLocalFile.h"  // includes platform-specific headers
Packit f0b94e
Packit f0b94e
#include "nsString.h"
Packit f0b94e
#include "nsCOMPtr.h"
Packit f0b94e
#include "nsReadableUtils.h"
Packit f0b94e
#include "nsPrintfCString.h"
Packit f0b94e
#include "nsCRT.h"
Packit f0b94e
#include "nsNativeCharsetUtils.h"
Packit f0b94e
#include "nsUTF8Utils.h"
Packit f0b94e
#include "nsArray.h"
Packit f0b94e
Packit f0b94e
#ifdef XP_WIN
Packit f0b94e
#include <string.h>
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
#if !defined(MOZ_WIDGET_COCOA) && !defined(XP_WIN)
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
nsLocalFile::InitWithFile(nsIFile* aFile) {
Packit f0b94e
  if (NS_WARN_IF(!aFile)) {
Packit f0b94e
    return NS_ERROR_INVALID_ARG;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nsAutoCString path;
Packit f0b94e
  aFile->GetNativePath(path);
Packit f0b94e
  if (path.IsEmpty()) {
Packit f0b94e
    return NS_ERROR_INVALID_ARG;
Packit f0b94e
  }
Packit f0b94e
  return InitWithNativePath(path);
Packit f0b94e
}
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
#define kMaxFilenameLength 255
Packit f0b94e
#define kMaxExtensionLength 100
Packit f0b94e
#define kMaxSequenceNumberLength 5  // "-9999"
Packit f0b94e
// requirement: kMaxExtensionLength <
Packit f0b94e
//                kMaxFilenameLength - kMaxSequenceNumberLength
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
nsLocalFile::CreateUnique(uint32_t aType, uint32_t aAttributes) {
Packit f0b94e
  nsresult rv;
Packit f0b94e
  bool longName;
Packit f0b94e
Packit f0b94e
#ifdef XP_WIN
Packit f0b94e
  nsAutoString pathName, leafName, rootName, suffix;
Packit f0b94e
  rv = GetPath(pathName);
Packit f0b94e
#else
Packit f0b94e
  nsAutoCString pathName, leafName, rootName, suffix;
Packit f0b94e
  rv = GetNativePath(pathName);
Packit f0b94e
#endif
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  auto FailedBecauseExists = [&](nsresult aRv) {
Packit f0b94e
    if (aRv == NS_ERROR_FILE_ACCESS_DENIED) {
Packit f0b94e
      bool exists;
Packit f0b94e
      return NS_SUCCEEDED(Exists(&exists)) && exists;
Packit f0b94e
    }
Packit f0b94e
    return aRv == NS_ERROR_FILE_ALREADY_EXISTS;
Packit f0b94e
  };
Packit f0b94e
Packit f0b94e
  longName =
Packit f0b94e
      (pathName.Length() + kMaxSequenceNumberLength > kMaxFilenameLength);
Packit f0b94e
  if (!longName) {
Packit f0b94e
    rv = Create(aType, aAttributes);
Packit f0b94e
    if (!FailedBecauseExists(rv)) {
Packit f0b94e
      return rv;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
#ifdef XP_WIN
Packit f0b94e
  rv = GetLeafName(leafName);
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  const int32_t lastDot = leafName.RFindChar(char16_t('.'));
Packit f0b94e
#else
Packit f0b94e
  rv = GetNativeLeafName(leafName);
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  const int32_t lastDot = leafName.RFindChar('.');
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
  if (lastDot == kNotFound) {
Packit f0b94e
    rootName = leafName;
Packit f0b94e
  } else {
Packit f0b94e
    suffix = Substring(leafName, lastDot);       // include '.'
Packit f0b94e
    rootName = Substring(leafName, 0, lastDot);  // strip suffix and dot
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (longName) {
Packit f0b94e
    int32_t maxRootLength =
Packit f0b94e
        (kMaxFilenameLength - (pathName.Length() - leafName.Length()) -
Packit f0b94e
         suffix.Length() - kMaxSequenceNumberLength);
Packit f0b94e
Packit f0b94e
    // We cannot create an item inside a directory whose name is too long.
Packit f0b94e
    // Also, ensure that at least one character remains after we truncate
Packit f0b94e
    // the root name, as we don't want to end up with an empty leaf name.
Packit f0b94e
    if (maxRootLength < 2) {
Packit f0b94e
      return NS_ERROR_FILE_UNRECOGNIZED_PATH;
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
#ifdef XP_WIN
Packit f0b94e
    // ensure that we don't cut the name in mid-UTF16-character
Packit f0b94e
    rootName.SetLength(NS_IS_LOW_SURROGATE(rootName[maxRootLength])
Packit f0b94e
                           ? maxRootLength - 1
Packit f0b94e
                           : maxRootLength);
Packit f0b94e
    SetLeafName(rootName + suffix);
Packit f0b94e
#else
Packit f0b94e
    if (NS_IsNativeUTF8()) {
Packit f0b94e
      // ensure that we don't cut the name in mid-UTF8-character
Packit f0b94e
      // (assume the name is valid UTF8 to begin with)
Packit f0b94e
      while (UTF8traits::isInSeq(rootName[maxRootLength])) {
Packit f0b94e
        --maxRootLength;
Packit f0b94e
      }
Packit f0b94e
Packit f0b94e
      // Another check to avoid ending up with an empty leaf name.
Packit f0b94e
      if (maxRootLength == 0 && suffix.IsEmpty()) {
Packit f0b94e
        return NS_ERROR_FILE_UNRECOGNIZED_PATH;
Packit f0b94e
      }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    rootName.SetLength(maxRootLength);
Packit f0b94e
    SetNativeLeafName(rootName + suffix);
Packit f0b94e
#endif
Packit f0b94e
    nsresult rvCreate = Create(aType, aAttributes);
Packit f0b94e
    if (!FailedBecauseExists(rvCreate)) {
Packit f0b94e
      return rvCreate;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  for (int indx = 1; indx < 10000; ++indx) {
Packit f0b94e
  // start with "Picture-1.jpg" after "Picture.jpg" exists
Packit f0b94e
#ifdef XP_WIN
Packit f0b94e
    SetLeafName(rootName +
Packit f0b94e
                NS_ConvertASCIItoUTF16(nsPrintfCString("-%d", indx)) + suffix);
Packit f0b94e
#else
Packit f0b94e
    SetNativeLeafName(rootName + nsPrintfCString("-%d", indx) + suffix);
Packit f0b94e
#endif
Packit f0b94e
    rv = Create(aType, aAttributes);
Packit f0b94e
    if (NS_SUCCEEDED(rv) || !FailedBecauseExists(rv)) {
Packit f0b94e
      return rv;
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // The disk is full, sort of
Packit f0b94e
  return NS_ERROR_FILE_TOO_BIG;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
#if defined(XP_WIN)
Packit f0b94e
static const char16_t kPathSeparatorChar = '\\';
Packit f0b94e
#elif defined(XP_UNIX)
Packit f0b94e
static const char16_t kPathSeparatorChar = '/';
Packit f0b94e
#else
Packit f0b94e
#error Need to define file path separator for your platform
Packit f0b94e
#endif
Packit f0b94e
Packit f0b94e
static void SplitPath(char16_t* aPath, nsTArray<char16_t*>& aNodeArray) {
Packit f0b94e
  if (*aPath == 0) {
Packit f0b94e
    return;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  if (*aPath == kPathSeparatorChar) {
Packit f0b94e
    aPath++;
Packit f0b94e
  }
Packit f0b94e
  aNodeArray.AppendElement(aPath);
Packit f0b94e
Packit f0b94e
  for (char16_t* cp = aPath; *cp != 0; ++cp) {
Packit f0b94e
    if (*cp == kPathSeparatorChar) {
Packit f0b94e
      *cp++ = 0;
Packit f0b94e
      if (*cp == 0) {
Packit f0b94e
        break;
Packit f0b94e
      }
Packit f0b94e
      aNodeArray.AppendElement(cp);
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
nsLocalFile::GetRelativeDescriptor(nsIFile* aFromFile, nsACString& aResult) {
Packit f0b94e
  if (NS_WARN_IF(!aFromFile)) {
Packit f0b94e
    return NS_ERROR_INVALID_ARG;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  //
Packit f0b94e
  // aResult will be UTF-8 encoded
Packit f0b94e
  //
Packit f0b94e
Packit f0b94e
  nsresult rv;
Packit f0b94e
  aResult.Truncate(0);
Packit f0b94e
Packit f0b94e
  nsAutoString thisPath, fromPath;
Packit f0b94e
  AutoTArray<char16_t*, 32> thisNodes;
Packit f0b94e
  AutoTArray<char16_t*, 32> fromNodes;
Packit f0b94e
Packit f0b94e
  rv = GetPath(thisPath);
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
  rv = aFromFile->GetPath(fromPath);
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // get raw pointer to mutable string buffer
Packit f0b94e
  char16_t* thisPathPtr;
Packit f0b94e
  thisPath.BeginWriting(thisPathPtr);
Packit f0b94e
  char16_t* fromPathPtr;
Packit f0b94e
  fromPath.BeginWriting(fromPathPtr);
Packit f0b94e
Packit f0b94e
  SplitPath(thisPathPtr, thisNodes);
Packit f0b94e
  SplitPath(fromPathPtr, fromNodes);
Packit f0b94e
Packit f0b94e
  size_t nodeIndex;
Packit f0b94e
  for (nodeIndex = 0;
Packit f0b94e
       nodeIndex < thisNodes.Length() && nodeIndex < fromNodes.Length();
Packit f0b94e
       ++nodeIndex) {
Packit f0b94e
#ifdef XP_WIN
Packit f0b94e
    if (_wcsicmp(char16ptr_t(thisNodes[nodeIndex]),
Packit f0b94e
                 char16ptr_t(fromNodes[nodeIndex]))) {
Packit f0b94e
      break;
Packit f0b94e
    }
Packit f0b94e
#else
Packit f0b94e
    if (nsCRT::strcmp(thisNodes[nodeIndex], fromNodes[nodeIndex])) {
Packit f0b94e
      break;
Packit f0b94e
    }
Packit f0b94e
#endif
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  size_t branchIndex = nodeIndex;
Packit f0b94e
  for (nodeIndex = branchIndex; nodeIndex < fromNodes.Length(); ++nodeIndex) {
Packit f0b94e
    aResult.AppendLiteral("../");
Packit f0b94e
  }
Packit f0b94e
  for (nodeIndex = branchIndex; nodeIndex < thisNodes.Length(); ++nodeIndex) {
Packit f0b94e
    NS_ConvertUTF16toUTF8 nodeStr(thisNodes[nodeIndex]);
Packit f0b94e
    aResult.Append(nodeStr);
Packit f0b94e
    if (nodeIndex + 1 < thisNodes.Length()) {
Packit f0b94e
      aResult.Append('/');
Packit f0b94e
    }
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
nsLocalFile::SetRelativeDescriptor(nsIFile* aFromFile,
Packit f0b94e
                                   const nsACString& aRelativeDesc) {
Packit f0b94e
  NS_NAMED_LITERAL_CSTRING(kParentDirStr, "../");
Packit f0b94e
Packit f0b94e
  nsCOMPtr<nsIFile> targetFile;
Packit f0b94e
  nsresult rv = aFromFile->Clone(getter_AddRefs(targetFile));
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    return rv;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  //
Packit f0b94e
  // aRelativeDesc is UTF-8 encoded
Packit f0b94e
  //
Packit f0b94e
Packit f0b94e
  nsCString::const_iterator strBegin, strEnd;
Packit f0b94e
  aRelativeDesc.BeginReading(strBegin);
Packit f0b94e
  aRelativeDesc.EndReading(strEnd);
Packit f0b94e
Packit f0b94e
  nsCString::const_iterator nodeBegin(strBegin), nodeEnd(strEnd);
Packit f0b94e
  nsCString::const_iterator pos(strBegin);
Packit f0b94e
Packit f0b94e
  nsCOMPtr<nsIFile> parentDir;
Packit f0b94e
  while (FindInReadable(kParentDirStr, nodeBegin, nodeEnd)) {
Packit f0b94e
    rv = targetFile->GetParent(getter_AddRefs(parentDir));
Packit f0b94e
    if (NS_FAILED(rv)) {
Packit f0b94e
      return rv;
Packit f0b94e
    }
Packit f0b94e
    if (!parentDir) {
Packit f0b94e
      return NS_ERROR_FILE_UNRECOGNIZED_PATH;
Packit f0b94e
    }
Packit f0b94e
    targetFile = parentDir;
Packit f0b94e
Packit f0b94e
    nodeBegin = nodeEnd;
Packit f0b94e
    pos = nodeEnd;
Packit f0b94e
    nodeEnd = strEnd;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  nodeBegin = nodeEnd = pos;
Packit f0b94e
  while (nodeEnd != strEnd) {
Packit f0b94e
    FindCharInReadable('/', nodeEnd, strEnd);
Packit f0b94e
    targetFile->Append(NS_ConvertUTF8toUTF16(Substring(nodeBegin, nodeEnd)));
Packit f0b94e
    if (nodeEnd != strEnd) {  // If there's more left in the string, inc over
Packit f0b94e
                              // the '/' nodeEnd is on.
Packit f0b94e
      ++nodeEnd;
Packit f0b94e
    }
Packit f0b94e
    nodeBegin = nodeEnd;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return InitWithFile(targetFile);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
nsLocalFile::GetRelativePath(nsIFile* aFromFile, nsACString& aResult) {
Packit f0b94e
  return GetRelativeDescriptor(aFromFile, aResult);
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
nsLocalFile::SetRelativePath(nsIFile* aFromFile,
Packit f0b94e
                             const nsACString& aRelativePath) {
Packit f0b94e
  return SetRelativeDescriptor(aFromFile, aRelativePath);
Packit f0b94e
}