OctaBuild/globs.cc

183 lines
5.1 KiB
C++
Raw Normal View History

2015-09-10 19:55:49 +02:00
#include <ostd/types.hh>
#include <ostd/string.hh>
#include <ostd/vector.hh>
#include <ostd/filesystem.hh>
2016-02-28 23:21:47 +01:00
#include <ostd/io.hh>
2015-09-10 19:55:49 +02:00
2016-02-28 23:21:47 +01:00
#include <cubescript.hh>
2015-09-10 19:55:49 +02:00
using ostd::ConstCharRange;
using ostd::Vector;
using ostd::String;
using ostd::slice_until;
2016-08-01 01:34:26 +02:00
using cscript::CsState;
using cscript::TvalRange;
2016-08-01 01:09:29 +02:00
static void ob_get_path_parts(
Vector<ConstCharRange> &parts, ConstCharRange elem
) {
2015-09-10 19:55:49 +02:00
ConstCharRange star = ostd::find(elem, '*');
while (!star.empty()) {
ConstCharRange ep = slice_until(elem, star);
2016-08-01 01:09:29 +02:00
if (!ep.empty()) {
2015-09-10 19:55:49 +02:00
parts.push(ep);
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
parts.push("*");
elem = star;
2015-12-15 22:22:29 +01:00
++elem;
2015-09-10 19:55:49 +02:00
star = ostd::find(elem, '*');
}
2016-08-01 01:09:29 +02:00
if (!elem.empty()) {
2015-09-10 19:55:49 +02:00
parts.push(elem);
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
}
2016-08-01 01:09:29 +02:00
static bool ob_path_matches(
ConstCharRange fn, Vector<ConstCharRange> const &parts
) {
2015-12-17 02:53:14 +01:00
for (auto it = parts.iter(); !it.empty(); ++it) {
2015-09-10 19:55:49 +02:00
ConstCharRange elem = it.front();
if (elem == "*") {
2015-12-15 22:22:29 +01:00
++it;
2015-09-10 19:55:49 +02:00
/* skip multiple stars if present */
2016-08-01 01:09:29 +02:00
while (!it.empty() && ((elem = it.front()) == "*")) {
2015-12-15 22:22:29 +01:00
++it;
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
/* trailing stars, we match */
2016-08-01 01:09:29 +02:00
if (it.empty()) {
2015-09-10 19:55:49 +02:00
return true;
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
/* skip about as much as we can until the elem part matches */
while (fn.size() > elem.size()) {
2016-08-01 01:09:29 +02:00
if (fn.slice(0, elem.size()) == elem) {
2015-09-10 19:55:49 +02:00
break;
2016-08-01 01:09:29 +02:00
}
2015-12-15 22:22:29 +01:00
++fn;
2015-09-10 19:55:49 +02:00
}
}
/* non-star here */
2016-08-01 01:09:29 +02:00
if (fn.size() < elem.size()) {
2015-09-10 19:55:49 +02:00
return false;
2016-08-01 01:09:29 +02:00
}
if (fn.slice(0, elem.size()) != elem) {
2015-09-10 19:55:49 +02:00
return false;
2016-08-01 01:09:29 +02:00
}
2015-12-15 22:22:29 +01:00
fn += elem.size();
2015-09-10 19:55:49 +02:00
}
/* if there are no chars in the fname remaining, we fully matched */
return fn.empty();
}
static bool ob_expand_glob(String &ret, ConstCharRange src, bool ne = false);
2016-08-01 01:09:29 +02:00
static bool ob_expand_dir(
String &ret, ConstCharRange dir, Vector<ConstCharRange> const &parts,
ConstCharRange slash
) {
2015-09-10 19:55:49 +02:00
ostd::DirectoryStream d(dir);
bool appended = false;
2016-08-01 01:09:29 +02:00
if (!d.is_open()) {
2015-09-10 19:55:49 +02:00
return false;
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
for (auto fi: d.iter()) {
ConstCharRange fn = fi.filename();
/* check if filename matches */
2016-08-01 01:09:29 +02:00
if (!ob_path_matches(fn, parts)) {
2015-09-10 19:55:49 +02:00
continue;
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
String afn((dir == ".") ? "" : "./");
afn.append(fn);
/* if we reach this, we match; try recursively matching */
if (!slash.empty()) {
afn.append(slash);
2015-12-15 22:22:29 +01:00
ConstCharRange psl = slash + 1;
2015-09-10 19:55:49 +02:00
if (!ostd::find(psl, '*').empty()) {
2016-08-01 01:09:29 +02:00
if (!appended) {
2015-09-10 19:55:49 +02:00
appended = ob_expand_glob(ret, afn.iter());
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
continue;
}
/* no further star, just do file test */
2016-08-01 01:09:29 +02:00
if (!ostd::FileStream(afn, ostd::StreamMode::read).is_open()) {
2015-09-10 19:55:49 +02:00
continue;
2016-08-01 01:09:29 +02:00
}
if (!ret.empty()) {
2015-09-10 19:55:49 +02:00
ret.push(' ');
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
ret.append(afn);
appended = true;
continue;
}
2016-08-01 01:09:29 +02:00
if (!ret.empty()) {
2015-09-10 19:55:49 +02:00
ret.push(' ');
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
ret.append(afn);
appended = true;
}
return appended;
}
static bool ob_expand_glob(String &ret, ConstCharRange src, bool ne) {
ConstCharRange star = ostd::find(src, '*');
/* no star use as-is */
if (star.empty()) {
if (ne) return false;
2016-08-01 01:09:29 +02:00
if (!ret.empty()) {
2015-09-10 19:55:49 +02:00
ret.push(' ');
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
ret.append(src);
return false;
}
/* part before star */
ConstCharRange prestar = slice_until(src, star);
/* try finding slash before star */
ConstCharRange slash = ostd::find_last(prestar, '/');
/* directory to scan */
ConstCharRange dir = ".";
/* part of name before star */
ConstCharRange fnpre = prestar;
if (!slash.empty()) {
/* there was slash, adjust directory + prefix accordingly */
dir = slice_until(src, slash);
2015-12-15 22:22:29 +01:00
fnpre = slash + 1;
2015-09-10 19:55:49 +02:00
}
/* part after star */
2015-12-15 22:22:29 +01:00
ConstCharRange fnpost = star + 1;
2015-09-10 19:55:49 +02:00
/* if a slash follows, adjust */
ConstCharRange nslash = ostd::find(fnpost, '/');
2016-08-01 01:09:29 +02:00
if (!nslash.empty()) {
2015-09-10 19:55:49 +02:00
fnpost = slice_until(fnpost, nslash);
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
/* retrieve the single element with whatever stars in it, chop it up */
Vector<ConstCharRange> parts;
ob_get_path_parts(parts, ConstCharRange(&fnpre[0], &fnpost[fnpost.size()]));
/* do a directory scan and match */
if (!ob_expand_dir(ret, dir, parts, nslash)) {
2016-08-01 01:09:29 +02:00
if (ne) {
return false;
}
if (!ret.empty()) {
2015-09-10 19:55:49 +02:00
ret.push(' ');
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
ret.append(src);
return false;
}
return true;
}
2016-08-01 01:09:29 +02:00
static String ob_expand_globs(Vector<String> const &src) {
2015-09-10 19:55:49 +02:00
String ret;
2016-08-01 01:09:29 +02:00
for (auto &s: src.iter()) {
2015-09-10 19:55:49 +02:00
ob_expand_glob(ret, s.iter());
2016-08-01 01:09:29 +02:00
}
2015-09-10 19:55:49 +02:00
return ret;
}
2016-08-01 20:34:04 +02:00
void cs_register_globs(CsState &cs) {
cs.add_command("glob", "C", [&cs](TvalRange args) {
2016-08-01 01:34:26 +02:00
auto fnames = cscript::util::list_explode(args[0].get_strr());
2015-09-10 19:55:49 +02:00
cs.result->set_str(ob_expand_globs(fnames).disown());
});
2016-02-07 22:20:28 +01:00
}