add wraparound support to C2 physics
[openc2e.git] / PathResolver.cpp
blob0d6905dfbc7664aae6dfd1c4657f2327e1912cec
1 /*
2 * PathResolver.cpp
3 * openc2e
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>
25 #include <set>
26 #include <map>
27 #include <cctype>
28 #include <string>
29 #include <algorithm>
30 #include <iostream>
31 #include <sstream>
33 using std::map;
34 using std::set;
35 using std::string;
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);
47 return in;
50 static path lcpath(path &orig) {
51 return path(toLowerCase(orig.string()), native);
54 static path lcleaf(path &orig) {
55 path br, leaf;
56 br = orig.branch_path();
57 leaf = path(toLowerCase(orig.leaf()), native);
58 return br / leaf;
61 bool resolveFile(path &p) {
62 #ifndef _WIN32
63 string s = p.string();
64 if (!resolveFile(s)) {
65 // Maybe something changed underneath us; reset the cache and try again
66 cache.clear();
67 dircache.clear();
68 if (!resolveFile(s))
69 return false;
71 p = path(s, native);
72 return true;
73 #else
74 return exists(p);
75 #endif
78 bool resolveFile_(string &srcPath) {
79 path orig(srcPath, native);
80 if (exists(orig))
81 return true;
83 orig.normalize();
84 path dir = orig.branch_path();
85 path leaf = path(orig.leaf(), native);
87 if (!checkDirCache(dir))
88 return false;
90 orig = dir / lcpath(leaf);
91 string fn = orig.string();
93 if (exists(orig)) {
94 srcPath = fn;
95 return true;
98 map<string, string>::iterator i = cache.find(fn);
99 if (i == cache.end()) {
100 assert(!exists(orig));
101 return false;
103 srcPath = cache[fn];
104 return true;
107 bool resolveFile(std::string &path) {
108 std::string orig = path;
109 #ifndef _WIN32
110 bool res = resolveFile_(path);
111 return res;
112 #else
113 return exists(path);
114 #endif
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) {
125 if (dir == path())
126 dir = path(".");
127 // std::cerr << "checkDirCache: " << dir.string() << std::endl;
128 if (dircache.end() != dircache.find(dir.string())) {
129 return true;
131 if (exists(dir))
132 return doCacheDir(dir);
133 if (dir.empty())
134 return false;
135 bool res = resolveFile(dir);
136 if (!res)
137 return false;
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) {
148 path cur = *it++;
149 string key, val;
150 key = cur.string();
151 val = lcleaf(cur).string();
152 // std::cerr << "Cache put: " << val << " -> " << key << std::endl;
153 cache[val] = key;
155 dircache.insert(dir.string());
156 return true;
159 static boost::regex constructSearchPattern(const std::string &wild) {
160 std::ostringstream matchbuf;
161 matchbuf << "^";
162 for (size_t i = 0; i < wild.size(); i++) {
163 if (wild[i] == '*')
164 matchbuf << ".*";
165 else if (wild[i] == '?')
166 matchbuf << ".";
167 else if (!(isalpha(wild[i]) || isdigit(wild[i]) || wild[i] == ' '))
168 matchbuf << "[" << wild[i] << "]";
169 else
170 matchbuf << wild[i];
172 matchbuf << "$";
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) {
178 cache.clear();
179 dircache.clear();
181 wild = toLowerCase(wild);
183 path dirp(dir, native);
184 dirp.normalize();
185 if (!resolveFile(dirp))
186 return std::vector<std::string>();
187 dir = dirp.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())
198 break;
199 std::string dirpart, filepart; // XXX: we should use boost fops, maybe
200 dirpart = skey->first.substr(0, lcdir.length());
201 if (dirpart != dir)
202 break;
203 if (skey->first.length() < lcdir.length() + 2)
204 continue;
205 filepart = toLowerCase(skey->first.substr(lcdir.length() + 1));
206 if (!boost::regex_match(filepart, l))
207 continue;
208 results.push_back(skey->second);
210 return results;
213 /* vim: set noet: */