Ninja
includes_normalize-win32.cc
Go to the documentation of this file.
1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "includes_normalize.h"
16 
17 #include "string_piece.h"
18 #include "string_piece_util.h"
19 #include "util.h"
20 
21 #include <algorithm>
22 #include <iterator>
23 #include <sstream>
24 
25 #include <windows.h>
26 
27 using namespace std;
28 
29 namespace {
30 
31 bool InternalGetFullPathName(const StringPiece& file_name, char* buffer,
32  size_t buffer_length, string *err) {
33  DWORD result_size = GetFullPathNameA(file_name.AsString().c_str(),
34  buffer_length, buffer, NULL);
35  if (result_size == 0) {
36  *err = "GetFullPathNameA(" + file_name.AsString() + "): " +
37  GetLastErrorString();
38  return false;
39  } else if (result_size > buffer_length) {
40  *err = "path too long";
41  return false;
42  }
43  return true;
44 }
45 
46 bool IsPathSeparator(char c) {
47  return c == '/' || c == '\\';
48 }
49 
50 // Return true if paths a and b are on the same windows drive.
51 // Return false if this function cannot check
52 // whether or not on the same windows drive.
53 bool SameDriveFast(StringPiece a, StringPiece b) {
54  if (a.size() < 3 || b.size() < 3) {
55  return false;
56  }
57 
58  if (!islatinalpha(a[0]) || !islatinalpha(b[0])) {
59  return false;
60  }
61 
62  if (ToLowerASCII(a[0]) != ToLowerASCII(b[0])) {
63  return false;
64  }
65 
66  if (a[1] != ':' || b[1] != ':') {
67  return false;
68  }
69 
70  return IsPathSeparator(a[2]) && IsPathSeparator(b[2]);
71 }
72 
73 // Return true if paths a and b are on the same Windows drive.
74 bool SameDrive(StringPiece a, StringPiece b, string* err) {
75  if (SameDriveFast(a, b)) {
76  return true;
77  }
78 
79  char a_absolute[_MAX_PATH];
80  char b_absolute[_MAX_PATH];
81  if (!InternalGetFullPathName(a, a_absolute, sizeof(a_absolute), err)) {
82  return false;
83  }
84  if (!InternalGetFullPathName(b, b_absolute, sizeof(b_absolute), err)) {
85  return false;
86  }
87  char a_drive[_MAX_DIR];
88  char b_drive[_MAX_DIR];
89  _splitpath(a_absolute, a_drive, NULL, NULL, NULL);
90  _splitpath(b_absolute, b_drive, NULL, NULL, NULL);
91  return _stricmp(a_drive, b_drive) == 0;
92 }
93 
94 // Check path |s| is FullPath style returned by GetFullPathName.
95 // This ignores difference of path separator.
96 // This is used not to call very slow GetFullPathName API.
97 bool IsFullPathName(StringPiece s) {
98  if (s.size() < 3 ||
99  !islatinalpha(s[0]) ||
100  s[1] != ':' ||
101  !IsPathSeparator(s[2])) {
102  return false;
103  }
104 
105  // Check "." or ".." is contained in path.
106  for (size_t i = 2; i < s.size(); ++i) {
107  if (!IsPathSeparator(s[i])) {
108  continue;
109  }
110 
111  // Check ".".
112  if (i + 1 < s.size() && s[i+1] == '.' &&
113  (i + 2 >= s.size() || IsPathSeparator(s[i+2]))) {
114  return false;
115  }
116 
117  // Check "..".
118  if (i + 2 < s.size() && s[i+1] == '.' && s[i+2] == '.' &&
119  (i + 3 >= s.size() || IsPathSeparator(s[i+3]))) {
120  return false;
121  }
122  }
123 
124  return true;
125 }
126 
127 } // anonymous namespace
128 
129 IncludesNormalize::IncludesNormalize(const string& relative_to) {
130  string err;
131  relative_to_ = AbsPath(relative_to, &err);
132  if (!err.empty()) {
133  Fatal("Initializing IncludesNormalize(): %s", err.c_str());
134  }
135  split_relative_to_ = SplitStringPiece(relative_to_, '/');
136 }
137 
138 string IncludesNormalize::AbsPath(StringPiece s, string* err) {
139  if (IsFullPathName(s)) {
140  string result = s.AsString();
141  for (size_t i = 0; i < result.size(); ++i) {
142  if (result[i] == '\\') {
143  result[i] = '/';
144  }
145  }
146  return result;
147  }
148 
149  char result[_MAX_PATH];
150  if (!InternalGetFullPathName(s, result, sizeof(result), err)) {
151  return "";
152  }
153  for (char* c = result; *c; ++c)
154  if (*c == '\\')
155  *c = '/';
156  return result;
157 }
158 
160  StringPiece path, const vector<StringPiece>& start_list, string* err) {
161  string abs_path = AbsPath(path, err);
162  if (!err->empty())
163  return "";
164  vector<StringPiece> path_list = SplitStringPiece(abs_path, '/');
165  int i;
166  for (i = 0; i < static_cast<int>(min(start_list.size(), path_list.size()));
167  ++i) {
168  if (!EqualsCaseInsensitiveASCII(start_list[i], path_list[i])) {
169  break;
170  }
171  }
172 
173  vector<StringPiece> rel_list;
174  rel_list.reserve(start_list.size() - i + path_list.size() - i);
175  for (int j = 0; j < static_cast<int>(start_list.size() - i); ++j)
176  rel_list.push_back("..");
177  for (int j = i; j < static_cast<int>(path_list.size()); ++j)
178  rel_list.push_back(path_list[j]);
179  if (rel_list.size() == 0)
180  return ".";
181  return JoinStringPiece(rel_list, '/');
182 }
183 
184 bool IncludesNormalize::Normalize(const string& input,
185  string* result, string* err) const {
186  char copy[_MAX_PATH + 1];
187  size_t len = input.size();
188  if (len > _MAX_PATH) {
189  *err = "path too long";
190  return false;
191  }
192  strncpy(copy, input.c_str(), input.size() + 1);
193  uint64_t slash_bits;
194  CanonicalizePath(copy, &len, &slash_bits);
195  StringPiece partially_fixed(copy, len);
196  string abs_input = AbsPath(partially_fixed, err);
197  if (!err->empty())
198  return false;
199 
200  if (!SameDrive(abs_input, relative_to_, err)) {
201  if (!err->empty())
202  return false;
203  *result = partially_fixed.AsString();
204  return true;
205  }
206  *result = Relativize(abs_input, split_relative_to_, err);
207  if (!err->empty())
208  return false;
209  return true;
210 }
Definition: hash_map.h:26
vector< StringPiece > SplitStringPiece(StringPiece input, char sep)
string JoinStringPiece(const vector< StringPiece > &list, char sep)
bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b)
char ToLowerASCII(char c)
static std::string AbsPath(StringPiece s, std::string *err)
bool Normalize(const std::string &input, std::string *result, std::string *err) const
Normalize by fixing slashes style, fixing redundant .
static std::string Relativize(StringPiece path, const std::vector< StringPiece > &start_list, std::string *err)
IncludesNormalize(const std::string &relative_to)
Normalize path relative to |relative_to|.
StringPiece represents a slice of a string whose memory is managed externally.
Definition: string_piece.h:25
size_t size() const
Definition: string_piece.h:62
std::string AsString() const
Convert the slice into a full-fledged std::string, copying the data into a new string.
Definition: string_piece.h:46
bool islatinalpha(int c)
Definition: util.cc:566
static bool IsPathSeparator(char c)
Definition: util.cc:133
void CanonicalizePath(string *path, uint64_t *slash_bits)
Definition: util.cc:124
void Fatal(const char *msg,...)
Log a fatal message and exit.
Definition: util.cc:67
unsigned long long uint64_t
Definition: win32port.h:29