Home | History | Annotate | Download | only in base
      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