blob: 56f254c6d7c73558728905d00331045ba43e1e4d [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// 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.
initial.commitd7cae122008-07-26 21:49:384
[email protected]09ad1e622008-08-07 20:23:095#include "base/path_service.h"
[email protected]355cc2742008-08-06 16:01:256
7#ifdef OS_WIN
initial.commitd7cae122008-07-26 21:49:388#include <windows.h>
9#include <shellapi.h>
10#include <shlobj.h>
[email protected]355cc2742008-08-06 16:01:2511#endif
initial.commitd7cae122008-07-26 21:49:3812
[email protected]640517f2008-10-30 23:54:0413#include "base/file_path.h"
[email protected]09ad1e622008-08-07 20:23:0914#include "base/file_util.h"
[email protected]640517f2008-10-30 23:54:0415#include "base/hash_tables.h"
initial.commitd7cae122008-07-26 21:49:3816#include "base/lock.h"
17#include "base/logging.h"
[email protected]1265917f2008-08-12 17:33:5218#include "base/singleton.h"
[email protected]355cc2742008-08-06 16:01:2519#include "base/string_util.h"
initial.commitd7cae122008-07-26 21:49:3820
21namespace base {
[email protected]4792a262008-11-19 16:50:0322 bool PathProvider(int key, FilePath* result);
[email protected]5af2edb92008-08-08 20:16:0823#if defined(OS_WIN)
[email protected]4792a262008-11-19 16:50:0324 bool PathProviderWin(int key, FilePath* result);
[email protected]4c0040c2008-08-15 01:04:1125#elif defined(OS_MACOSX)
[email protected]4792a262008-11-19 16:50:0326 bool PathProviderMac(int key, FilePath* result);
[email protected]4c0040c2008-08-15 01:04:1127#elif defined(OS_LINUX)
[email protected]4792a262008-11-19 16:50:0328 bool PathProviderLinux(int key, FilePath* result);
[email protected]ac510e12008-08-05 19:46:3129#endif
initial.commitd7cae122008-07-26 21:49:3830}
31
32namespace {
33
[email protected]640517f2008-10-30 23:54:0434typedef base::hash_map<int, FilePath> PathMap;
[email protected]355cc2742008-08-06 16:01:2535typedef base::hash_set<int> PathSet;
initial.commitd7cae122008-07-26 21:49:3836
37// We keep a linked list of providers. In a debug build we ensure that no two
38// providers claim overlapping keys.
39struct Provider {
40 PathService::ProviderFunc func;
41 struct Provider* next;
42#ifndef NDEBUG
43 int key_start;
44 int key_end;
45#endif
[email protected]173cb8a02008-08-20 15:47:3946 bool is_static;
initial.commitd7cae122008-07-26 21:49:3847};
48
49static Provider base_provider = {
50 base::PathProvider,
51 NULL,
52#ifndef NDEBUG
53 base::PATH_START,
[email protected]173cb8a02008-08-20 15:47:3954 base::PATH_END,
initial.commitd7cae122008-07-26 21:49:3855#endif
[email protected]173cb8a02008-08-20 15:47:3956 true
initial.commitd7cae122008-07-26 21:49:3857};
58
[email protected]ac510e12008-08-05 19:46:3159#ifdef OS_WIN
60static Provider base_provider_win = {
61 base::PathProviderWin,
62 &base_provider,
63#ifndef NDEBUG
64 base::PATH_WIN_START,
[email protected]173cb8a02008-08-20 15:47:3965 base::PATH_WIN_END,
[email protected]ac510e12008-08-05 19:46:3166#endif
[email protected]173cb8a02008-08-20 15:47:3967 true
[email protected]ac510e12008-08-05 19:46:3168};
69#endif
70
[email protected]5af2edb92008-08-08 20:16:0871#ifdef OS_MACOSX
[email protected]173cb8a02008-08-20 15:47:3972static Provider base_provider_mac = {
[email protected]5af2edb92008-08-08 20:16:0873 base::PathProviderMac,
74 &base_provider,
75#ifndef NDEBUG
76 base::PATH_MAC_START,
[email protected]173cb8a02008-08-20 15:47:3977 base::PATH_MAC_END,
[email protected]5af2edb92008-08-08 20:16:0878#endif
[email protected]173cb8a02008-08-20 15:47:3979 true
80};
[email protected]5af2edb92008-08-08 20:16:0881#endif
[email protected]4c0040c2008-08-15 01:04:1182
83#if defined(OS_LINUX)
84static Provider base_provider_linux = {
85 base::PathProviderLinux,
86 &base_provider,
87#ifndef NDEBUG
88 base::PATH_LINUX_START,
[email protected]173cb8a02008-08-20 15:47:3989 base::PATH_LINUX_END,
[email protected]4c0040c2008-08-15 01:04:1190#endif
[email protected]173cb8a02008-08-20 15:47:3991 true
92};
[email protected]4c0040c2008-08-15 01:04:1193#endif
94
95
initial.commitd7cae122008-07-26 21:49:3896struct 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
[email protected]ac510e12008-08-05 19:46:31102 PathData() {
103#if defined(OS_WIN)
104 providers = &base_provider_win;
[email protected]5af2edb92008-08-08 20:16:08105#elif defined(OS_MACOSX)
106 providers = &base_provider_mac;
107#elif defined(OS_LINUX)
[email protected]4c0040c2008-08-15 01:04:11108 providers = &base_provider_linux;
[email protected]ac510e12008-08-05 19:46:31109#endif
initial.commitd7cae122008-07-26 21:49:38110 }
[email protected]173cb8a02008-08-20 15:47:39111
112 ~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 }
initial.commitd7cae122008-07-26 21:49:38121};
[email protected]1265917f2008-08-12 17:33:52122
123static PathData* GetPathData() {
124 return Singleton<PathData>::get();
125}
initial.commitd7cae122008-07-26 21:49:38126
127} // namespace
128
[email protected]6723f832008-08-11 15:38:27129
[email protected]1265917f2008-08-12 17:33:52130// static
[email protected]640517f2008-10-30 23:54:04131bool PathService::GetFromCache(int key, FilePath* result) {
[email protected]1265917f2008-08-12 17:33:52132 PathData* path_data = GetPathData();
[email protected]6723f832008-08-11 15:38:27133 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
[email protected]1265917f2008-08-12 17:33:52144// static
[email protected]640517f2008-10-30 23:54:04145void PathService::AddToCache(int key, const FilePath& path) {
[email protected]1265917f2008-08-12 17:33:52146 PathData* path_data = GetPathData();
[email protected]6723f832008-08-11 15:38:27147 AutoLock scoped_lock(path_data->lock);
148 // Save the computed path in our cache.
149 path_data->cache[key] = path;
150}
151
initial.commitd7cae122008-07-26 21:49:38152// 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
[email protected]640517f2008-10-30 23:54:04156bool PathService::Get(int key, FilePath* result) {
[email protected]1265917f2008-08-12 17:33:52157 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38158 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
[email protected]5af2edb92008-08-08 20:16:08163 if (key == base::DIR_CURRENT)
[email protected]1010f7d2008-08-06 16:29:44164 return file_util::GetCurrentDirectory(result);
initial.commitd7cae122008-07-26 21:49:38165
[email protected]6723f832008-08-11 15:38:27166 if (GetFromCache(key, result))
initial.commitd7cae122008-07-26 21:49:38167 return true;
[email protected]6723f832008-08-11 15:38:27168
[email protected]4792a262008-11-19 16:50:03169 FilePath path;
initial.commitd7cae122008-07-26 21:49:38170
171 // search providers for the requested path
[email protected]6723f832008-08-11 15:38:27172 // NOTE: it should be safe to iterate here without the lock
173 // since RegisterProvider always prepends.
initial.commitd7cae122008-07-26 21:49:38174 Provider* provider = path_data->providers;
175 while (provider) {
[email protected]4792a262008-11-19 16:50:03176 if (provider->func(key, &path))
initial.commitd7cae122008-07-26 21:49:38177 break;
[email protected]4792a262008-11-19 16:50:03178 DCHECK(path.value().empty()) << "provider should not have modified path";
initial.commitd7cae122008-07-26 21:49:38179 provider = provider->next;
180 }
181
[email protected]4792a262008-11-19 16:50:03182 if (path.value().empty())
initial.commitd7cae122008-07-26 21:49:38183 return false;
184
[email protected]6723f832008-08-11 15:38:27185 AddToCache(key, path);
186
[email protected]640517f2008-10-30 23:54:04187 *result = path;
188 return true;
189}
190
191// static
192bool PathService::Get(int key, std::wstring* result) {
193 // Deprecated compatibility function.
194 FilePath path;
195 if (!Get(key, &path))
196 return false;
197 *result = path.ToWStringHack();
initial.commitd7cae122008-07-26 21:49:38198 return true;
199}
200
201bool PathService::IsOverridden(int key) {
[email protected]1265917f2008-08-12 17:33:52202 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38203 DCHECK(path_data);
204
205 AutoLock scoped_lock(path_data->lock);
206 return path_data->overrides.find(key) != path_data->overrides.end();
207}
208
209bool PathService::Override(int key, const std::wstring& path) {
[email protected]1265917f2008-08-12 17:33:52210 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38211 DCHECK(path_data);
212 DCHECK(key > base::DIR_CURRENT) << "invalid path key";
213
[email protected]1265917f2008-08-12 17:33:52214 std::wstring file_path = path;
215 if (!file_util::AbsolutePath(&file_path))
initial.commitd7cae122008-07-26 21:49:38216 return false;
initial.commitd7cae122008-07-26 21:49:38217
218 // make sure the directory exists:
[email protected]806b9c62008-09-11 16:09:11219 if (!file_util::CreateDirectory(file_path))
initial.commitd7cae122008-07-26 21:49:38220 return false;
221
222 file_util::TrimTrailingSeparator(&file_path);
223
224 AutoLock scoped_lock(path_data->lock);
[email protected]640517f2008-10-30 23:54:04225 path_data->cache[key] = FilePath::FromWStringHack(file_path);
initial.commitd7cae122008-07-26 21:49:38226 path_data->overrides.insert(key);
227 return true;
228}
229
230bool PathService::SetCurrentDirectory(const std::wstring& current_directory) {
[email protected]1010f7d2008-08-06 16:29:44231 return file_util::SetCurrentDirectory(current_directory);
initial.commitd7cae122008-07-26 21:49:38232}
233
234void PathService::RegisterProvider(ProviderFunc func, int key_start,
235 int key_end) {
[email protected]1265917f2008-08-12 17:33:52236 PathData* path_data = GetPathData();
initial.commitd7cae122008-07-26 21:49:38237 DCHECK(path_data);
238 DCHECK(key_end > key_start);
239
240 AutoLock scoped_lock(path_data->lock);
241
242 Provider* p;
243
244#ifndef NDEBUG
245 p = path_data->providers;
246 while (p) {
247 DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
248 "path provider collision";
249 p = p->next;
250 }
251#endif
252
253 p = new Provider;
[email protected]173cb8a02008-08-20 15:47:39254 p->is_static = false;
initial.commitd7cae122008-07-26 21:49:38255 p->func = func;
256 p->next = path_data->providers;
257#ifndef NDEBUG
258 p->key_start = key_start;
259 p->key_end = key_end;
260#endif
261 path_data->providers = p;
262}