PageRenderTime 25ms CodeModel.GetById 12ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llvfs/lldiriterator.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 228 lines | 158 code | 31 blank | 39 comment | 15 complexity | 7710f26467df4dce8a9e6c349c04855e MD5 | raw file
  1/**
  2 * @file lldiriterator.cpp
  3 * @brief Iterator through directory entries matching the search pattern.
  4 *
  5 * $LicenseInfo:firstyear=2010&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 *
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 *
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 *
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 *
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "lldiriterator.h"
 28
 29#include <boost/filesystem.hpp>
 30#include <boost/regex.hpp>
 31
 32namespace fs = boost::filesystem;
 33
 34static std::string glob_to_regex(const std::string& glob);
 35
 36class LLDirIterator::Impl
 37{
 38public:
 39	Impl(const std::string &dirname, const std::string &mask);
 40	~Impl();
 41
 42	bool next(std::string &fname);
 43
 44private:
 45	boost::regex			mFilterExp;
 46	fs::directory_iterator	mIter;
 47	bool					mIsValid;
 48};
 49
 50LLDirIterator::Impl::Impl(const std::string &dirname, const std::string &mask)
 51	: mIsValid(false)
 52{
 53	fs::path dir_path(dirname);
 54
 55	bool is_dir = false;
 56
 57	// Check if path is a directory.
 58	try
 59	{
 60		is_dir = fs::is_directory(dir_path);
 61	}
 62	catch (fs::basic_filesystem_error<fs::path>& e)
 63	{
 64		llwarns << e.what() << llendl;
 65		return;
 66	}
 67
 68	if (!is_dir)
 69	{
 70		llwarns << "Invalid path: \"" << dir_path.string() << "\"" << llendl;
 71		return;
 72	}
 73
 74	// Initialize the directory iterator for the given path.
 75	try
 76	{
 77		mIter = fs::directory_iterator(dir_path);
 78	}
 79	catch (fs::basic_filesystem_error<fs::path>& e)
 80	{
 81		llwarns << e.what() << llendl;
 82		return;
 83	}
 84
 85	// Convert the glob mask to a regular expression
 86	std::string exp = glob_to_regex(mask);
 87
 88	// Initialize boost::regex with the expression converted from
 89	// the glob mask.
 90	// An exception is thrown if the expression is not valid.
 91	try
 92	{
 93		mFilterExp.assign(exp);
 94	}
 95	catch (boost::regex_error& e)
 96	{
 97		llwarns << "\"" << exp << "\" is not a valid regular expression: "
 98				<< e.what() << llendl;
 99		return;
100	}
101
102	mIsValid = true;
103}
104
105LLDirIterator::Impl::~Impl()
106{
107}
108
109bool LLDirIterator::Impl::next(std::string &fname)
110{
111	fname = "";
112
113	if (!mIsValid)
114	{
115		llwarns << "The iterator is not correctly initialized." << llendl;
116		return false;
117	}
118
119	fs::directory_iterator end_itr; // default construction yields past-the-end
120	bool found = false;
121	while (mIter != end_itr && !found)
122	{
123		boost::smatch match;
124		std::string name = mIter->path().filename();
125		if (found = boost::regex_match(name, match, mFilterExp))
126		{
127			fname = name;
128		}
129
130		++mIter;
131	}
132
133	return found;
134}
135
136/**
137Converts the incoming glob into a regex. This involves
138converting incoming glob expressions to regex equivilents and
139at the same time, escaping any regex meaningful characters which
140do not have glob meaning, i.e.
141            .()+|^$ 
142in the input.
143*/
144std::string glob_to_regex(const std::string& glob)
145{
146	std::string regex;
147	regex.reserve(glob.size()<<1);
148	S32 braces = 0;
149	bool escaped = false;
150	bool square_brace_open = false;
151
152	for (std::string::const_iterator i = glob.begin(); i != glob.end(); ++i)
153	{
154		char c = *i;
155
156		switch (c)
157		{
158			case '*':
159				if (glob.begin() == i)
160				{
161					regex+="[^.].*";
162				}
163				else
164				{
165					regex+= escaped ? "*" : ".*";
166				}
167				break;
168			case '?':
169				regex+= escaped ? '?' : '.';
170				break;
171			case '{':
172				braces++;
173				regex+='(';
174				break;
175			case '}':
176				if (!braces)
177				{
178					llerrs << "glob_to_regex: Closing brace without an equivalent opening brace: " << glob << llendl;
179				}
180
181				regex+=')';
182				braces--;
183				break;
184			case ',':
185				regex+= braces ? '|' : c;
186				break;
187			case '!':
188				regex+= square_brace_open ? '^' : c;
189				break;
190			case '.': // This collection have different regex meaning
191			case '^': // and so need escaping.
192			case '(': 
193			case ')':
194			case '+':
195			case '|':
196			case '$':
197				regex += '\\'; 
198			default:
199				regex += c;
200				break;
201		}
202
203		escaped = ('\\' == c);
204		square_brace_open = ('[' == c);
205	}
206
207	if (braces)
208	{
209		llerrs << "glob_to_regex: Unterminated brace expression: " << glob << llendl;
210	}
211
212	return regex;
213}
214
215LLDirIterator::LLDirIterator(const std::string &dirname, const std::string &mask)
216{
217	mImpl = new Impl(dirname, mask);
218}
219
220LLDirIterator::~LLDirIterator()
221{
222	delete mImpl;
223}
224
225bool LLDirIterator::next(std::string &fname)
226{
227	return mImpl->next(fname);
228}