summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync/util/path_helpers.cc
blob: 1cf8d4ea1907d655010a4c82870a7cf008a7741d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/sync/util/path_helpers.h"

#include <Shlwapi.h>
#include <stdlib.h>

#include "base/logging.h"
#include "base/port.h"
#include "chrome/browser/sync/syncable/syncable.h"

#ifndef OS_WINDOWS
#error Compile this file on Windows only.
#endif

using std::string;

#if OS_WINDOWS
const char PATH_SEPARATOR = '\\';
#else
const char PATH_SEPARATOR = '/';
#endif  // OS_WINDOWS


static PathString RemoveTrailingSlashes16(PathString str) {
  while ((str.length() > 0) && (str.at(str.length() - 1) == kPathSeparator[0]))
    str.resize(str.length() - 1);
  return str;
}

static string RemoveTrailingSlashes(string str) {
  while ((str.length() > 0) && (str.at(str.length() - 1) == PATH_SEPARATOR))
    str.resize(str.length() - 1);
  return str;
}

PathString LastPathSegment(const PathString& path) {
  return RemoveTrailingSlashes16(PathFindFileNameW(path.c_str()));
}

string LastPathSegment(const string& path) {
  return RemoveTrailingSlashes(PathFindFileNameA(path.c_str()));
}

PathString GetFullPath(const PathString& path) {
  PathChar buffer[MAX_PATH];
  PathChar* file_part;
  DWORD const size = GetFullPathName(path.c_str(), ARRAYSIZE(buffer), buffer,
                                     &file_part);
  return PathString(buffer, size);
}

PathString AppendSlash(const PathString& path) {
  PathString result(path);
  if (!result.empty()) {
    if (*result.rbegin() == '/')
      *result.rbegin() = '\\';
    else if (*result.rbegin() != '\\')
      result.push_back('\\');
  }
  return result;
}

PathString ExpandTilde(const PathString& path) {
  // Do nothing on windows.
  return path;
}

// Returns a string with length or fewer elements, careful to
// not truncate a string mid-surrogate pair.
PathString TruncatePathString(const PathString& original, int length) {
  if (original.size() <= length)
    return original;
  if (length <= 0)
    return original;
  PathString ret(original.begin(), original.begin() + length);
  COMPILE_ASSERT(sizeof(PathChar) == sizeof(uint16), PathStringNotUTF16);
  PathChar last_char = *ret.rbegin();

  // Values taken from
  // http://en.wikipedia.org/w/index.php?title=UTF-16/UCS-2&oldid=248072840
  if (last_char >= 0xD800 && last_char <= 0xDBFF)
    ret.resize(ret.size() - 1);
  return ret;
}

namespace {
const PathString kWindowsIllegalBaseFilenames[] = {
    L"CON", L"PRN", L"AUX", L"NUL", L"COM1", L"COM2",
    L"COM3", L"COM4", L"COM5", L"COM6", L"COM7",
    L"COM8", L"COM9", L"LPT1", L"LPT2", L"LPT3",
    L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8",
    L"LPT9" };
}

// See: http://msdn.microsoft.com/library/default.asp?url=/library/
// en-us/fileio/fs/naming_a_file.asp
// note that * and ? are not listed on the page as illegal characters,
// but they are.
PathString MakePathComponentOSLegal(const PathString& component) {
  CHECK(!component.empty());
  PathString mutable_component = component;

  // Remove illegal characters.
  for (PathString::iterator i = mutable_component.begin();
       i != mutable_component.end();) {
    if ((PathString::npos != PathString(L"<>:\"/\\|*?").find(*i)) ||
        ((static_cast<unsigned short>(*i) >= 0) &&
         (static_cast<unsigned short>(*i) <= 31))) {
      mutable_component.erase(i);
    } else {
      ++i;
    }
  }

  // Remove trailing spaces or periods.
  while (mutable_component.size() &&
         ((mutable_component.at(mutable_component.size() - 1) == L' ') ||
          (mutable_component.at(mutable_component.size() - 1) == L'.')))
    mutable_component.resize(mutable_component.size() - 1, L' ');

  // Remove a bunch of forbidden names. windows only seems to mind if
  // a forbidden name matches our name exactly (e.g. "prn") or if the
  // name is the forbidden name, followed by a dot, followed by anything
  // (e.g., "prn.anything.foo.bar")

  // From this point out, we break mutable_component into two strings, and
  // use them this way: we save anything after and including the first dot
  // (usually the extension) and only mess with stuff before the first dot.
  PathString::size_type first_dot = mutable_component.find_first_of(L'.');
  if (PathString::npos == first_dot)
    first_dot = mutable_component.size();
  PathString sub = mutable_component.substr(0, first_dot);
  PathString postsub = mutable_component.substr(first_dot);
  CHECK(sub + postsub == mutable_component);
  for (int i = 0; i < ARRAYSIZE(kWindowsIllegalBaseFilenames); i++) {
    // ComparePathNames(a, b) == 0 -> same
    if (syncable::ComparePathNames(kWindowsIllegalBaseFilenames[i], sub) == 0) {
      sub.append(L"~1");
      break;
    }
  }
  if ((L"" == sub) && (L"" == postsub)) {
    sub = L"~1";
  }

  // Return the new name, only if it differs from the original.
  if ((sub + postsub) == component)
    return L"";
  return (sub + postsub);
}