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_POSIX)
28 bool PathProviderPosix(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 #if defined(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 #if defined(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_POSIX) && !defined(OS_MACOSX)
84 static Provider base_provider_posix = {
85 base::PathProviderPosix,
86 &base_provider,
87 #ifndef NDEBUG
88 0,
89 0,
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
PathData__anon8ba544c90111::PathData102 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_POSIX)
108 providers = &base_provider_posix;
109 #endif
110 }
111
~PathData__anon8ba544c90111::PathData112 ~PathData() {
113 Provider* p = providers;
114 while (p) {
115 Provider* next = p->next;
116 if (!p->is_static)
117 delete p;
118 p = next;
119 }
120 }
121 };
122
GetPathData()123 static PathData* GetPathData() {
124 return Singleton<PathData>::get();
125 }
126
127 } // namespace
128
129
130 // static
GetFromCache(int key,FilePath * result)131 bool PathService::GetFromCache(int key, FilePath* result) {
132 PathData* path_data = GetPathData();
133 AutoLock scoped_lock(path_data->lock);
134
135 // check for a cached version
136 PathMap::const_iterator it = path_data->cache.find(key);
137 if (it != path_data->cache.end()) {
138 *result = it->second;
139 return true;
140 }
141 return false;
142 }
143
144 // static
AddToCache(int key,const FilePath & path)145 void PathService::AddToCache(int key, const FilePath& path) {
146 PathData* path_data = GetPathData();
147 AutoLock scoped_lock(path_data->lock);
148 // Save the computed path in our cache.
149 path_data->cache[key] = path;
150 }
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
Get(int key,FilePath * result)156 bool PathService::Get(int key, FilePath* result) {
157 PathData* path_data = GetPathData();
158 DCHECK(path_data);
159 DCHECK(result);
160 DCHECK(key >= base::DIR_CURRENT);
161
162 // special case the current directory because it can never be cached
163 if (key == base::DIR_CURRENT)
164 return file_util::GetCurrentDirectory(result);
165
166 if (GetFromCache(key, result))
167 return true;
168
169 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 Provider* provider = path_data->providers;
175 while (provider) {
176 if (provider->func(key, &path))
177 break;
178 DCHECK(path.empty()) << "provider should not have modified path";
179 provider = provider->next;
180 }
181
182 if (path.empty())
183 return false;
184
185 AddToCache(key, path);
186
187 *result = path;
188 return true;
189 }
190
191 #if defined(OS_WIN)
192 // static
Get(int key,std::wstring * result)193 bool PathService::Get(int key, std::wstring* result) {
194 // Deprecated compatibility function.
195 FilePath path;
196 if (!Get(key, &path))
197 return false;
198 *result = path.ToWStringHack();
199 return true;
200 }
201 #endif
202
IsOverridden(int key)203 bool PathService::IsOverridden(int key) {
204 PathData* path_data = GetPathData();
205 DCHECK(path_data);
206
207 AutoLock scoped_lock(path_data->lock);
208 return path_data->overrides.find(key) != path_data->overrides.end();
209 }
210
Override(int key,const FilePath & path)211 bool PathService::Override(int key, const FilePath& path) {
212 PathData* path_data = GetPathData();
213 DCHECK(path_data);
214 DCHECK(key > base::DIR_CURRENT) << "invalid path key";
215
216 FilePath file_path = path;
217
218 // Make sure the directory exists. We need to do this before we translate
219 // this to the absolute path because on POSIX, AbsolutePath fails if called
220 // on a non-existant path.
221 if (!file_util::PathExists(file_path) &&
222 !file_util::CreateDirectory(file_path))
223 return false;
224
225 // We need to have an absolute path, as extensions and plugins don't like
226 // relative paths, and will glady crash the browser in CHECK()s if they get a
227 // relative path.
228 if (!file_util::AbsolutePath(&file_path))
229 return false;
230
231 AutoLock scoped_lock(path_data->lock);
232 path_data->cache[key] = file_path;
233 path_data->overrides.insert(key);
234 return true;
235 }
236
RegisterProvider(ProviderFunc func,int key_start,int key_end)237 void PathService::RegisterProvider(ProviderFunc func, int key_start,
238 int key_end) {
239 PathData* path_data = GetPathData();
240 DCHECK(path_data);
241 DCHECK(key_end > key_start);
242
243 AutoLock scoped_lock(path_data->lock);
244
245 Provider* p;
246
247 #ifndef NDEBUG
248 p = path_data->providers;
249 while (p) {
250 DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
251 "path provider collision";
252 p = p->next;
253 }
254 #endif
255
256 p = new Provider;
257 p->is_static = false;
258 p->func = func;
259 p->next = path_data->providers;
260 #ifndef NDEBUG
261 p->key_start = key_start;
262 p->key_end = key_end;
263 #endif
264 path_data->providers = p;
265 }
266