blob: b3d413f159edd36f8e40be156ebed53f0f42293a [file] [log] [blame]
initial.commitd7cae122008-07-26 21:49:381// Copyright 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include <windows.h>
31#include <shellapi.h>
32#include <shlobj.h>
33
34#include <hash_map>
35#include <hash_set>
36
37#include "base/path_service.h"
38
39#include "base/lock.h"
40#include "base/logging.h"
41#include "base/file_util.h"
42
43namespace base {
44 bool PathProvider(int key, std::wstring* result);
[email protected]ac510e12008-08-05 19:46:3145#if OS_WIN
46 bool PathProviderWin(int key, std::wstring* result);
47#endif
initial.commitd7cae122008-07-26 21:49:3848}
49
50namespace {
51
52typedef stdext::hash_map<int,std::wstring> PathMap;
53typedef stdext::hash_set<int> PathSet;
54
55// We keep a linked list of providers. In a debug build we ensure that no two
56// providers claim overlapping keys.
57struct Provider {
58 PathService::ProviderFunc func;
59 struct Provider* next;
60#ifndef NDEBUG
61 int key_start;
62 int key_end;
63#endif
64};
65
66static Provider base_provider = {
67 base::PathProvider,
68 NULL,
69#ifndef NDEBUG
70 base::PATH_START,
71 base::PATH_END
72#endif
73};
74
[email protected]ac510e12008-08-05 19:46:3175#ifdef OS_WIN
76static Provider base_provider_win = {
77 base::PathProviderWin,
78 &base_provider,
79#ifndef NDEBUG
80 base::PATH_WIN_START,
81 base::PATH_WIN_END
82#endif
83};
84#endif
85
initial.commitd7cae122008-07-26 21:49:3886struct PathData {
87 Lock lock;
88 PathMap cache; // Track mappings from path key to path value.
89 PathSet overrides; // Track whether a path has been overridden.
90 Provider* providers; // Linked list of path service providers.
91
[email protected]ac510e12008-08-05 19:46:3192 PathData() {
93#if defined(OS_WIN)
94 providers = &base_provider_win;
95#elif defined(OS_MACOSX)
96 providers = &base_provider;
97#elif defined(OS_LINUX)
98 providers = &base_provider;
99#endif
initial.commitd7cae122008-07-26 21:49:38100 }
101};
102
103// We rely on the path service not being used prior to 'main' execution, and
104// we are happy to let this data structure leak at process exit.
105PathData* path_data = new PathData();
106
107} // namespace
108
109// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
110// characters). This isn't supported very well by Windows right now, so it is
111// moot, but we should keep this in mind for the future.
112// static
113bool PathService::Get(int key, std::wstring* result) {
114 DCHECK(path_data);
115 DCHECK(result);
116 DCHECK(key >= base::DIR_CURRENT);
117
118 // special case the current directory because it can never be cached
119 if (key == base::DIR_CURRENT) {
120 wchar_t system_buffer[MAX_PATH];
121 system_buffer[0] = 0;
122 DWORD len = GetCurrentDirectory(MAX_PATH, system_buffer);
123 if (len == 0 || len > MAX_PATH)
124 return false;
125 *result = system_buffer;
126 file_util::TrimTrailingSeparator(result);
127 return true;
128 }
129
130 // TODO(darin): it would be nice to avoid holding this lock while calling out
131 // to the path providers.
132 AutoLock scoped_lock(path_data->lock);
133
134 // check for a cached version
135 PathMap::const_iterator it = path_data->cache.find(key);
136 if (it != path_data->cache.end()) {
137 *result = it->second;
138 return true;
139 }
140
141 std::wstring path;
142
143 // search providers for the requested path
144 Provider* provider = path_data->providers;
145 while (provider) {
146 if (provider->func(key, &path))
147 break;
148 DCHECK(path.empty()) << "provider should not have modified path";
149 provider = provider->next;
150 }
151
152 if (path.empty())
153 return false;
154
155 // Save the computed path in our cache.
156 path_data->cache[key] = path;
157
158 result->swap(path);
159 return true;
160}
161
162bool PathService::IsOverridden(int key) {
163 DCHECK(path_data);
164
165 AutoLock scoped_lock(path_data->lock);
166 return path_data->overrides.find(key) != path_data->overrides.end();
167}
168
169bool PathService::Override(int key, const std::wstring& path) {
170 DCHECK(path_data);
171 DCHECK(key > base::DIR_CURRENT) << "invalid path key";
172
173 wchar_t file_path_buf[MAX_PATH];
174 if (!_wfullpath(file_path_buf, path.c_str(), MAX_PATH))
175 return false;
176 std::wstring file_path(file_path_buf);
177
178 // make sure the directory exists:
179 if (!file_util::PathExists(file_path) &&
180 // TODO(darin): what if this path is not that of a directory?
181 !file_util::CreateDirectory(file_path))
182 return false;
183
184 file_util::TrimTrailingSeparator(&file_path);
185
186 AutoLock scoped_lock(path_data->lock);
187 path_data->cache[key] = file_path;
188 path_data->overrides.insert(key);
189 return true;
190}
191
192bool PathService::SetCurrentDirectory(const std::wstring& current_directory) {
193 BOOL ret = ::SetCurrentDirectory(current_directory.c_str());
194 return (ret ? true : false);
195}
196
197void PathService::RegisterProvider(ProviderFunc func, int key_start,
198 int key_end) {
199 DCHECK(path_data);
200 DCHECK(key_end > key_start);
201
202 AutoLock scoped_lock(path_data->lock);
203
204 Provider* p;
205
206#ifndef NDEBUG
207 p = path_data->providers;
208 while (p) {
209 DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
210 "path provider collision";
211 p = p->next;
212 }
213#endif
214
215 p = new Provider;
216 p->func = func;
217 p->next = path_data->providers;
218#ifndef NDEBUG
219 p->key_start = key_start;
220 p->key_end = key_end;
221#endif
222 path_data->providers = p;
223}