• 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 using namespace std;
28 
29 namespace {
30 
InternalGetFullPathName(const StringPiece & file_name,char * buffer,size_t buffer_length,string * err)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 
IsPathSeparator(char c)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.
SameDriveFast(StringPiece a,StringPiece b)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.
SameDrive(StringPiece a,StringPiece b,string * err)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.
IsFullPathName(StringPiece s)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 
IncludesNormalize(const string & relative_to)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 
AbsPath(StringPiece s,string * err)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 
Relativize(StringPiece path,const vector<StringPiece> & start_list,string * err)159 string IncludesNormalize::Relativize(
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 
Normalize(const string & input,string * result,string * err) const184 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 }
211