Two utility functions:
/** A wrapper for the Posix function 'glob'.
 *  \param pattern the glob to be expanded. Eg "*.[Ch]".
 *  \param flags flags to be passed to the system function. 
 *  See 'man glob'.
 *  \returns a vector of the files found to match \c pattern.
 */
std::vector<std::string> const 
glob(std::string const & pattern, int flags = 0);

/// Expand csh-style brace expresions "*.{bcd,efg}" to "*.bcd *.efg"
std::string const expand_brace_glob(std::string const & glob);


and a little trial program demonstrating their use:
$ g++ -Ilyx/devel/boost -o trial2 glob_support.C trial2.C 
-Llyx/devel/build/boost/libs/regex/src/.libs -lboostregex
$ ./trial2 '*.{C,pdf} .*'


Why:
Qt does not support the useful csh-style "*.{abc,def}", so use
        expand_brace_glob("*.{abc,def}");
to give "*.abc *.def". This function requires no system-dependent 
files, so should work on unix, mac & windows.

xforms has no support for globs at all, so 'glob' wraps the posix 
function of the same name. Will work only on unix systems, but should 
be independent of unix flavour.

I started off with the intention of writing a parser capable of 
handling the Qt-style syntax:
        "All files (*);;Raster Images (*.{jpg,png});;XFig images (*.fig)"
but I don't really see the need for the descriptive string here, nor 
the need to store multiple file types in a single External Template. 
Given that writing a real glob parser myself would be a PITA, I'm 
going to stick with what I have.

Ie, these two helper functions will enable both frontends to 
understand filter definitions in external_templates of the form:
        FileFilter "*.{gif,png,jpg,bmp,p[bgp]m,tga,tif,x[bp]m}"
        FileFilter "*.fig"
        FileFilter "*.fen"
        FileFilter "*"

-- 
Angus
/**
 * \file glob_support.C
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Angus Leeming
 *
 * Full author contact details are available in file CREDITS.
 */

#include "glob_support.h"

#include <boost/regex.hpp>

#include "glob.h"

using std::string;
using std::vector;


vector<string> const glob(string const & pattern, int flags)
{
	typedef vector<string>::size_type size_type;

	vector<string> matches;
	if (pattern.empty())
		return matches;

	glob_t glob_buffer;
	glob_buffer.gl_offs = 0;
	int const result = glob(pattern.c_str(), flags, 0, &glob_buffer);
	if (result == 0) {
		matches.resize(glob_buffer.gl_pathc);
		size_type i = 0;
		for (size_type i = 0; i != glob_buffer.gl_pathc; ++i)
			matches[i] = glob_buffer.gl_pathv[i];
		
	}
	
	globfree(&glob_buffer);

	return matches;
}


string const expand_brace_glob(string const & glob)
{
	static boost::regex const glob_re(" *([^ {]*)\\{([^ }]+)\\}");
	static boost::regex const block_re("([^,}]+),?");

	string pattern;

	string::const_iterator it = glob.begin();
	string::const_iterator const end = glob.end();
	while (true) {
		boost::match_results<string::const_iterator> what;
		if (!boost::regex_search(it, end, what, glob_re)) {
			pattern += string(it, end);
			break;
		}

		// Everything from the start of the input to 
		// the start of the match.
		pattern += string(what[-1].first, what[-1].second);

		// Given " *.{abc,def}", head == "*." and tail == "abc,def".
		string const head = string(what[1].first, what[1].second);
		string const tail = string(what[2].first, what[2].second);

		// Split the ','-separated chunks of tail so that
		// $head{$chunk1,$chunk2} becomes "$head$chunk1 $head$chunk2".
		string const fmt = " " + head + "$1";
		pattern += boost::regex_merge(tail, block_re, fmt);

		// Increment the iterator to the end of the match.
		it += std::distance(it, what[0].second);
	}
	
	return pattern;
}
// -*- C++ -*-
/**
 * \file glob_support.h
 * This file is part of LyX, the document processor.
 * Licence details can be found in the file COPYING.
 *
 * \author Angus Leeming
 *
 * Full author contact details are available in file CREDITS.
 */

#ifndef GLOB_SUPPORT_H
#define GLOB_SUPPORT_H

#include <string>
#include <vector>

/** A wrapper for the Posix function 'glob'.
 *  \param pattern the glob to be expanded. Eg "*.[Ch]".
 *  \param flags flags to be passed to the system function. man glob.
 *  \returns a vector of the files found to match \c pattern.
 */
std::vector<std::string> const glob(std::string const & pattern, int flags = 0);

/// Expand csh-style brace expresions "*.{bcd,efg}" to "*.bcd *.efg"
std::string const expand_brace_glob(std::string const & glob);

#endif // NOT GLOB_SUPPORT_H
#include "glob_support.h"

#include <boost/tokenizer.hpp>

#include <iostream>

using std::string;
using std::vector;

namespace {

vector<string> const match(char const * const arg)
{
	typedef boost::tokenizer<boost::char_separator<char> > Tokenizer;
	boost::char_separator<char> const separator(" ");

	string const expanded_glob = expand_brace_glob(arg);

	vector<string> matches;
	Tokenizer const tokens(expanded_glob, separator);
	Tokenizer::const_iterator it = tokens.begin();
	Tokenizer::const_iterator const end = tokens.end();
	for (; it != end; ++it) {
		vector<string> const tmp = glob(*it);
		matches.insert(matches.end(), tmp.begin(), tmp.end());
	}
	return matches;
}

} // namespace anon


int main(int const argc, char const * const argv[])
{
	for (int i = 1; i != argc; ++i) {
		vector<string> const matches = match(argv[i]);

		std::cout << "Matching '" << argv[i] << "':\n";
		if (matches.empty())
			continue;
		std::cout << '\t';
		vector<string>::const_iterator it = matches.begin();
		vector<string>::const_iterator const end = matches.end();
		for (; it != end; ++it) {
			std::cout << *it << (it+1 == end ? '\n' : ' ');
		}
	}
	std::flush(std::cout);
	return 0;
}

Reply via email to