5 * Created by Bryan Donlan
6 * Copyright (c) 2005 Bryan Donlan. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
20 #include "PathResolver.h"
22 #include <boost/filesystem/path.hpp>
23 #include <boost/filesystem/operations.hpp>
24 #include <boost/regex.hpp>
36 using namespace boost::filesystem
;
38 static set
<string
> dircache
;
39 static map
<string
, string
> cache
;
41 static bool checkDirCache(path
&dir
);
42 static bool doCacheDir(path
&dir
);
44 /* C++ is far too verbose for its own good */
45 static string
toLowerCase(string in
) {
46 transform(in
.begin(), in
.end(), in
.begin(), (int(*)(int))tolower
);
50 static path
lcpath(path
&orig
) {
51 return path(toLowerCase(orig
.string()), native
);
54 static path
lcleaf(path
&orig
) {
56 br
= orig
.branch_path();
57 leaf
= path(toLowerCase(orig
.leaf()), native
);
61 bool resolveFile(path
&p
) {
63 string s
= p
.string();
64 if (!resolveFile(s
)) {
65 // Maybe something changed underneath us; reset the cache and try again
78 bool resolveFile_(string
&srcPath
) {
79 path
orig(srcPath
, native
);
84 path dir
= orig
.branch_path();
85 path leaf
= path(orig
.leaf(), native
);
87 if (!checkDirCache(dir
))
90 orig
= dir
/ lcpath(leaf
);
91 string fn
= orig
.string();
98 map
<string
, string
>::iterator i
= cache
.find(fn
);
99 if (i
== cache
.end()) {
100 assert(!exists(orig
));
107 bool resolveFile(std::string
&path
) {
108 std::string orig
= path
;
110 bool res
= resolveFile_(path
);
118 /* If dir is cached, do nothing.
119 * If dir exists, cache it.
120 * If dir does not exist, see if there's one with different capitalization.
122 * If we find a dir, return true. Else, false.
124 bool checkDirCache(path
&dir
) {
127 // std::cerr << "checkDirCache: " << dir.string() << std::endl;
128 if (dircache
.end() != dircache
.find(dir
.string())) {
132 return doCacheDir(dir
);
135 bool res
= resolveFile(dir
);
138 return checkDirCache(dir
);
141 /* Cache a dir. Return true for success.
143 bool doCacheDir(path
&dir
) {
144 // std::cerr << "cacheing: " << dir.string() << std::endl;
145 directory_iterator
it(dir
);
146 directory_iterator fsend
;
147 while (it
!= fsend
) {
151 val
= lcleaf(cur
).string();
152 // std::cerr << "Cache put: " << val << " -> " << key << std::endl;
155 dircache
.insert(dir
.string());
159 static boost::regex
constructSearchPattern(const std::string
&wild
) {
160 std::ostringstream matchbuf
;
162 for (size_t i
= 0; i
< wild
.size(); i
++) {
165 else if (wild
[i
] == '?')
167 else if (!(isalpha(wild
[i
]) || isdigit(wild
[i
]) || wild
[i
] == ' '))
168 matchbuf
<< "[" << wild
[i
] << "]";
173 std::string matchstr
= matchbuf
.str();
174 return boost::regex(matchstr
.c_str());
177 std::vector
<std::string
> findByWildcard(std::string dir
, std::string wild
) {
181 wild
= toLowerCase(wild
);
183 path
dirp(dir
, native
);
185 if (!resolveFile(dirp
))
186 return std::vector
<std::string
>();
189 if (!doCacheDir(dirp
))
190 return std::vector
<std::string
>();
191 std::vector
<std::string
> results
;
192 boost::regex l
= constructSearchPattern(wild
);
194 std::string lcdir
= toLowerCase(dir
);
195 std::map
<string
, string
>::iterator skey
= cache
.lower_bound(dir
);
196 for (; skey
!= cache
.end(); skey
++) {
197 if (skey
->first
.length() < lcdir
.length())
199 std::string dirpart
, filepart
; // XXX: we should use boost fops, maybe
200 dirpart
= skey
->first
.substr(0, lcdir
.length());
203 if (skey
->first
.length() < lcdir
.length() + 2)
205 filepart
= toLowerCase(skey
->first
.substr(lcdir
.length() + 1));
206 if (!boost::regex_match(filepart
, l
))
208 results
.push_back(skey
->second
);