• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace {
28 
InternalGetFullPathName(const StringPiece & file_name,char * buffer,size_t buffer_length,string * err)29 bool InternalGetFullPathName(const StringPiece& file_name, char* buffer,
30                              size_t buffer_length, string *err) {
31   DWORD result_size = GetFullPathNameA(file_name.AsString().c_str(),
32                                        buffer_length, buffer, NULL);
33   if (result_size == 0) {
34     *err = "GetFullPathNameA(" + file_name.AsString() + "): " +
35         GetLastErrorString();
36     return false;
37   } else if (result_size > buffer_length) {
38     *err = "path too long";
39     return false;
40   }
41   return true;
42 }
43 
IsPathSeparator(char c)44 bool IsPathSeparator(char c) {
45   return c == '/' ||  c == '\\';
46 }
47 
48 // Return true if paths a and b are on the same windows drive.
49 // Return false if this funcation cannot check
50 // whether or not on the same windows drive.
SameDriveFast(StringPiece a,StringPiece b)51 bool SameDriveFast(StringPiece a, StringPiece b) {
52   if (a.size() < 3 || b.size() < 3) {
53     return false;
54   }
55 
56   if (!islatinalpha(a[0]) || !islatinalpha(b[0])) {
57     return false;
58   }
59 
60   if (ToLowerASCII(a[0]) != ToLowerASCII(b[0])) {
61     return false;
62   }
63 
64   if (a[1] != ':' || b[1] != ':') {
65     return false;
66   }
67 
68   return IsPathSeparator(a[2]) && IsPathSeparator(b[2]);
69 }
70 
71 // Return true if paths a and b are on the same Windows drive.
SameDrive(StringPiece a,StringPiece b,string * err)72 bool SameDrive(StringPiece a, StringPiece b, string* err)  {
73   if (SameDriveFast(a, b)) {
74     return true;
75   }
76 
77   char a_absolute[_MAX_PATH];
78   char b_absolute[_MAX_PATH];
79   if (!InternalGetFullPathName(a, a_absolute, sizeof(a_absolute), err)) {
80     return false;
81   }
82   if (!InternalGetFullPathName(b, b_absolute, sizeof(b_absolute), err)) {
83     return false;
84   }
85   char a_drive[_MAX_DIR];
86   char b_drive[_MAX_DIR];
87   _splitpath(a_absolute, a_drive, NULL, NULL, NULL);
88   _splitpath(b_absolute, b_drive, NULL, NULL, NULL);
89   return _stricmp(a_drive, b_drive) == 0;
90 }
91 
92 // Check path |s| is FullPath style returned by GetFullPathName.
93 // This ignores difference of path separator.
94 // This is used not to call very slow GetFullPathName API.
IsFullPathName(StringPiece s)95 bool IsFullPathName(StringPiece s) {
96   if (s.size() < 3 ||
97       !islatinalpha(s[0]) ||
98       s[1] != ':' ||
99       !IsPathSeparator(s[2])) {
100     return false;
101   }
102 
103   // Check "." or ".." is contained in path.
104   for (size_t i = 2; i < s.size(); ++i) {
105     if (!IsPathSeparator(s[i])) {
106       continue;
107     }
108 
109     // Check ".".
110     if (i + 1 < s.size() && s[i+1] == '.' &&
111         (i + 2 >= s.size() || IsPathSeparator(s[i+2]))) {
112       return false;
113     }
114 
115     // Check "..".
116     if (i + 2 < s.size() && s[i+1] == '.' && s[i+2] == '.' &&
117         (i + 3 >= s.size() || IsPathSeparator(s[i+3]))) {
118       return false;
119     }
120   }
121 
122   return true;
123 }
124 
125 }  // anonymous namespace
126 
IncludesNormalize(const string & relative_to)127 IncludesNormalize::IncludesNormalize(const string& relative_to) {
128   string err;
129   relative_to_ = AbsPath(relative_to, &err);
130   if (!err.empty()) {
131     Fatal("Initializing IncludesNormalize(): %s", err.c_str());
132   }
133   split_relative_to_ = SplitStringPiece(relative_to_, '/');
134 }
135 
AbsPath(StringPiece s,string * err)136 string IncludesNormalize::AbsPath(StringPiece s, string* err) {
137   if (IsFullPathName(s)) {
138     string result = s.AsString();
139     for (size_t i = 0; i < result.size(); ++i) {
140       if (result[i] == '\\') {
141         result[i] = '/';
142       }
143     }
144     return result;
145   }
146 
147   char result[_MAX_PATH];
148   if (!InternalGetFullPathName(s, result, sizeof(result), err)) {
149     return "";
150   }
151   for (char* c = result; *c; ++c)
152     if (*c == '\\')
153       *c = '/';
154   return result;
155 }
156 
Relativize(StringPiece path,const vector<StringPiece> & start_list,string * err)157 string IncludesNormalize::Relativize(
158     StringPiece path, const vector<StringPiece>& start_list, string* err) {
159   string abs_path = AbsPath(path, err);
160   if (!err->empty())
161     return "";
162   vector<StringPiece> path_list = SplitStringPiece(abs_path, '/');
163   int i;
164   for (i = 0; i < static_cast<int>(min(start_list.size(), path_list.size()));
165        ++i) {
166     if (!EqualsCaseInsensitiveASCII(start_list[i], path_list[i])) {
167       break;
168     }
169   }
170 
171   vector<StringPiece> rel_list;
172   rel_list.reserve(start_list.size() - i + path_list.size() - i);
173   for (int j = 0; j < static_cast<int>(start_list.size() - i); ++j)
174     rel_list.push_back("..");
175   for (int j = i; j < static_cast<int>(path_list.size()); ++j)
176     rel_list.push_back(path_list[j]);
177   if (rel_list.size() == 0)
178     return ".";
179   return JoinStringPiece(rel_list, '/');
180 }
181 
Normalize(const string & input,string * result,string * err) const182 bool IncludesNormalize::Normalize(const string& input,
183                                   string* result, string* err) const {
184   char copy[_MAX_PATH + 1];
185   size_t len = input.size();
186   if (len > _MAX_PATH) {
187     *err = "path too long";
188     return false;
189   }
190   strncpy(copy, input.c_str(), input.size() + 1);
191   uint64_t slash_bits;
192   if (!CanonicalizePath(copy, &len, &slash_bits, err))
193     return false;
194   StringPiece partially_fixed(copy, len);
195   string abs_input = AbsPath(partially_fixed, err);
196   if (!err->empty())
197     return false;
198 
199   if (!SameDrive(abs_input, relative_to_, err)) {
200     if (!err->empty())
201       return false;
202     *result = partially_fixed.AsString();
203     return true;
204   }
205   *result = Relativize(abs_input, split_relative_to_, err);
206   if (!err->empty())
207     return false;
208   return true;
209 }
210