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
27using namespace std;
28
29namespace {
30
31bool 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
46bool 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.
53bool 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.
74bool 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.
97bool 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
129IncludesNormalize::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 }
136}
137
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
184bool 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
string JoinStringPiece(const vector< StringPiece > &list, char sep)
bool EqualsCaseInsensitiveASCII(StringPiece a, StringPiece b)
vector< StringPiece > SplitStringPiece(StringPiece input, char sep)
char ToLowerASCII(char c)
static std::string AbsPath(StringPiece s, std::string *err)
std::vector< StringPiece > split_relative_to_
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.
size_t size() const
std::string AsString() const
Convert the slice into a full-fledged std::string, copying the data into a new string.
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