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 102 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 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 } 121 }; 122 123 static PathData* GetPathData() { 124 return Singleton<PathData>::get(); 125 } 126 127 } // namespace 128 129 130 // static 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 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 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 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 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 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 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