1 : // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "base/path_service.h"
6 :
7 : #ifdef OS_WIN
8 : #include <windows.h>
9 : #include <shellapi.h>
10 : #include <shlobj.h>
11 : #endif
12 :
13 : #include "base/file_path.h"
14 : #include "base/file_util.h"
15 : #include "base/hash_tables.h"
16 : #include "base/lock.h"
17 : #include "base/logging.h"
18 : #include "base/singleton.h"
19 : #include "base/string_util.h"
20 :
21 : namespace base {
22 : bool PathProvider(int key, FilePath* result);
23 : #if defined(OS_WIN)
24 : bool PathProviderWin(int key, FilePath* result);
25 : #elif defined(OS_MACOSX)
26 : bool PathProviderMac(int key, FilePath* result);
27 : #elif defined(OS_LINUX)
28 : bool PathProviderLinux(int key, FilePath* result);
29 : #endif
30 : }
31 :
32 : namespace {
33 :
34 : typedef base::hash_map<int, FilePath> PathMap;
35 : typedef base::hash_set<int> PathSet;
36 :
37 : // We keep a linked list of providers. In a debug build we ensure that no two
38 : // providers claim overlapping keys.
39 : struct Provider {
40 : PathService::ProviderFunc func;
41 : struct Provider* next;
42 : #ifndef NDEBUG
43 : int key_start;
44 : int key_end;
45 : #endif
46 : bool is_static;
47 : };
48 :
49 : static Provider base_provider = {
50 : base::PathProvider,
51 : NULL,
52 : #ifndef NDEBUG
53 : base::PATH_START,
54 : base::PATH_END,
55 : #endif
56 : true
57 : };
58 :
59 : #ifdef OS_WIN
60 : static Provider base_provider_win = {
61 : base::PathProviderWin,
62 : &base_provider,
63 : #ifndef NDEBUG
64 : base::PATH_WIN_START,
65 : base::PATH_WIN_END,
66 : #endif
67 : true
68 : };
69 : #endif
70 :
71 : #ifdef OS_MACOSX
72 : static Provider base_provider_mac = {
73 : base::PathProviderMac,
74 : &base_provider,
75 : #ifndef NDEBUG
76 : base::PATH_MAC_START,
77 : base::PATH_MAC_END,
78 : #endif
79 : true
80 : };
81 : #endif
82 :
83 : #if defined(OS_LINUX)
84 : static Provider base_provider_linux = {
85 : base::PathProviderLinux,
86 : &base_provider,
87 : #ifndef NDEBUG
88 : base::PATH_LINUX_START,
89 : base::PATH_LINUX_END,
90 : #endif
91 : true
92 : };
93 : #endif
94 :
95 :
96 : struct PathData {
97 : Lock lock;
98 : PathMap cache; // Track mappings from path key to path value.
99 : PathSet overrides; // Track whether a path has been overridden.
100 : Provider* providers; // Linked list of path service providers.
101 :
102 0 : PathData() {
103 : #if defined(OS_WIN)
104 : providers = &base_provider_win;
105 : #elif defined(OS_MACOSX)
106 : providers = &base_provider_mac;
107 : #elif defined(OS_LINUX)
108 0 : providers = &base_provider_linux;
109 : #endif
110 0 : }
111 :
112 0 : ~PathData() {
113 0 : Provider* p = providers;
114 0 : while (p) {
115 0 : Provider* next = p->next;
116 0 : if (!p->is_static)
117 : delete p;
118 0 : p = next;
119 : }
120 0 : }
121 : };
122 :
123 0 : static PathData* GetPathData() {
124 0 : return Singleton<PathData>::get();
125 : }
126 :
127 : } // namespace
128 :
129 :
130 : // static
131 0 : bool PathService::GetFromCache(int key, FilePath* result) {
132 0 : PathData* path_data = GetPathData();
133 0 : AutoLock scoped_lock(path_data->lock);
134 :
135 : // check for a cached version
136 0 : PathMap::const_iterator it = path_data->cache.find(key);
137 0 : if (it != path_data->cache.end()) {
138 0 : *result = it->second;
139 0 : return true;
140 : }
141 0 : return false;
142 : }
143 :
144 : // static
145 0 : void PathService::AddToCache(int key, const FilePath& path) {
146 0 : PathData* path_data = GetPathData();
147 0 : AutoLock scoped_lock(path_data->lock);
148 : // Save the computed path in our cache.
149 0 : path_data->cache[key] = path;
150 0 : }
151 :
152 : // TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
153 : // characters). This isn't supported very well by Windows right now, so it is
154 : // moot, but we should keep this in mind for the future.
155 : // static
156 0 : bool PathService::Get(int key, FilePath* result) {
157 0 : PathData* path_data = GetPathData();
158 0 : DCHECK(path_data);
159 0 : DCHECK(result);
160 0 : DCHECK(key >= base::DIR_CURRENT);
161 :
162 : // special case the current directory because it can never be cached
163 0 : if (key == base::DIR_CURRENT)
164 0 : return file_util::GetCurrentDirectory(result);
165 :
166 0 : if (GetFromCache(key, result))
167 0 : return true;
168 :
169 0 : FilePath path;
170 :
171 : // search providers for the requested path
172 : // NOTE: it should be safe to iterate here without the lock
173 : // since RegisterProvider always prepends.
174 0 : Provider* provider = path_data->providers;
175 0 : while (provider) {
176 0 : if (provider->func(key, &path))
177 0 : break;
178 0 : DCHECK(path.empty()) << "provider should not have modified path";
179 0 : provider = provider->next;
180 : }
181 :
182 0 : if (path.empty())
183 0 : return false;
184 :
185 0 : AddToCache(key, path);
186 :
187 0 : *result = path;
188 0 : return true;
189 : }
190 :
191 : // static
192 0 : bool PathService::Get(int key, std::wstring* result) {
193 : // Deprecated compatibility function.
194 0 : FilePath path;
195 0 : if (!Get(key, &path))
196 0 : return false;
197 0 : *result = path.ToWStringHack();
198 0 : return true;
199 : }
200 :
201 0 : bool PathService::IsOverridden(int key) {
202 0 : PathData* path_data = GetPathData();
203 0 : DCHECK(path_data);
204 :
205 0 : AutoLock scoped_lock(path_data->lock);
206 0 : return path_data->overrides.find(key) != path_data->overrides.end();
207 : }
208 :
209 0 : bool PathService::Override(int key, const std::wstring& path) {
210 0 : PathData* path_data = GetPathData();
211 0 : DCHECK(path_data);
212 0 : DCHECK(key > base::DIR_CURRENT) << "invalid path key";
213 :
214 0 : std::wstring file_path = path;
215 : #if defined(OS_WIN)
216 : // On Windows we switch the current working directory to load plugins (at
217 : // least). That's not the case on POSIX.
218 : // Also, on POSIX, AbsolutePath fails if called on a non-existant path.
219 : if (!file_util::AbsolutePath(&file_path))
220 : return false;
221 : #endif
222 :
223 : // make sure the directory exists:
224 0 : if (!file_util::CreateDirectory(file_path))
225 0 : return false;
226 :
227 0 : file_util::TrimTrailingSeparator(&file_path);
228 :
229 0 : AutoLock scoped_lock(path_data->lock);
230 0 : path_data->cache[key] = FilePath::FromWStringHack(file_path);
231 0 : path_data->overrides.insert(key);
232 0 : return true;
233 : }
234 :
235 0 : bool PathService::SetCurrentDirectory(const std::wstring& current_directory) {
236 0 : return file_util::SetCurrentDirectory(current_directory);
237 : }
238 :
239 0 : void PathService::RegisterProvider(ProviderFunc func, int key_start,
240 : int key_end) {
241 0 : PathData* path_data = GetPathData();
242 0 : DCHECK(path_data);
243 0 : DCHECK(key_end > key_start);
244 :
245 0 : AutoLock scoped_lock(path_data->lock);
246 :
247 : Provider* p;
248 :
249 : #ifndef NDEBUG
250 0 : p = path_data->providers;
251 0 : while (p) {
252 0 : DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
253 0 : "path provider collision";
254 0 : p = p->next;
255 : }
256 : #endif
257 :
258 0 : p = new Provider;
259 0 : p->is_static = false;
260 0 : p->func = func;
261 0 : p->next = path_data->providers;
262 : #ifndef NDEBUG
263 0 : p->key_start = key_start;
264 0 : p->key_end = key_end;
265 : #endif
266 0 : path_data->providers = p;
267 0 : }
|