mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
ae65e281f3
to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
170 lines
4.6 KiB
C++
170 lines
4.6 KiB
C++
//===-- GlobPattern.cpp - Glob pattern matcher implementation -------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements a glob pattern matcher.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/GlobPattern.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/Optional.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Errc.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static bool hasWildcard(StringRef S) {
|
|
return S.find_first_of("?*[") != StringRef::npos;
|
|
}
|
|
|
|
// Expands character ranges and returns a bitmap.
|
|
// For example, "a-cf-hz" is expanded to "abcfghz".
|
|
static Expected<BitVector> expand(StringRef S, StringRef Original) {
|
|
BitVector BV(256, false);
|
|
|
|
// Expand X-Y.
|
|
for (;;) {
|
|
if (S.size() < 3)
|
|
break;
|
|
|
|
uint8_t Start = S[0];
|
|
uint8_t End = S[2];
|
|
|
|
// If it doesn't start with something like X-Y,
|
|
// consume the first character and proceed.
|
|
if (S[1] != '-') {
|
|
BV[Start] = true;
|
|
S = S.substr(1);
|
|
continue;
|
|
}
|
|
|
|
// It must be in the form of X-Y.
|
|
// Validate it and then interpret the range.
|
|
if (Start > End)
|
|
return make_error<StringError>("invalid glob pattern: " + Original,
|
|
errc::invalid_argument);
|
|
|
|
for (int C = Start; C <= End; ++C)
|
|
BV[(uint8_t)C] = true;
|
|
S = S.substr(3);
|
|
}
|
|
|
|
for (char C : S)
|
|
BV[(uint8_t)C] = true;
|
|
return BV;
|
|
}
|
|
|
|
// This is a scanner for the glob pattern.
|
|
// A glob pattern token is one of "*", "?", "[<chars>]", "[^<chars>]"
|
|
// (which is a negative form of "[<chars>]"), or a non-meta character.
|
|
// This function returns the first token in S.
|
|
static Expected<BitVector> scan(StringRef &S, StringRef Original) {
|
|
switch (S[0]) {
|
|
case '*':
|
|
S = S.substr(1);
|
|
// '*' is represented by an empty bitvector.
|
|
// All other bitvectors are 256-bit long.
|
|
return BitVector();
|
|
case '?':
|
|
S = S.substr(1);
|
|
return BitVector(256, true);
|
|
case '[': {
|
|
size_t End = S.find(']', 1);
|
|
if (End == StringRef::npos)
|
|
return make_error<StringError>("invalid glob pattern: " + Original,
|
|
errc::invalid_argument);
|
|
|
|
StringRef Chars = S.substr(1, End - 1);
|
|
S = S.substr(End + 1);
|
|
if (Chars.startswith("^")) {
|
|
Expected<BitVector> BV = expand(Chars.substr(1), Original);
|
|
if (!BV)
|
|
return BV.takeError();
|
|
return BV->flip();
|
|
}
|
|
return expand(Chars, Original);
|
|
}
|
|
default:
|
|
BitVector BV(256, false);
|
|
BV[(uint8_t)S[0]] = true;
|
|
S = S.substr(1);
|
|
return BV;
|
|
}
|
|
}
|
|
|
|
Expected<GlobPattern> GlobPattern::create(StringRef S) {
|
|
GlobPattern Pat;
|
|
|
|
// S doesn't contain any metacharacter,
|
|
// so the regular string comparison should work.
|
|
if (!hasWildcard(S)) {
|
|
Pat.Exact = S;
|
|
return Pat;
|
|
}
|
|
|
|
// S is something like "foo*". We can use startswith().
|
|
if (S.endswith("*") && !hasWildcard(S.drop_back())) {
|
|
Pat.Prefix = S.drop_back();
|
|
return Pat;
|
|
}
|
|
|
|
// S is something like "*foo". We can use endswith().
|
|
if (S.startswith("*") && !hasWildcard(S.drop_front())) {
|
|
Pat.Suffix = S.drop_front();
|
|
return Pat;
|
|
}
|
|
|
|
// Otherwise, we need to do real glob pattern matching.
|
|
// Parse the pattern now.
|
|
StringRef Original = S;
|
|
while (!S.empty()) {
|
|
Expected<BitVector> BV = scan(S, Original);
|
|
if (!BV)
|
|
return BV.takeError();
|
|
Pat.Tokens.push_back(*BV);
|
|
}
|
|
return Pat;
|
|
}
|
|
|
|
bool GlobPattern::match(StringRef S) const {
|
|
if (Exact)
|
|
return S == *Exact;
|
|
if (Prefix)
|
|
return S.startswith(*Prefix);
|
|
if (Suffix)
|
|
return S.endswith(*Suffix);
|
|
return matchOne(Tokens, S);
|
|
}
|
|
|
|
// Runs glob pattern Pats against string S.
|
|
bool GlobPattern::matchOne(ArrayRef<BitVector> Pats, StringRef S) const {
|
|
for (;;) {
|
|
if (Pats.empty())
|
|
return S.empty();
|
|
|
|
// If Pats[0] is '*', try to match Pats[1..] against all possible
|
|
// tail strings of S to see at least one pattern succeeds.
|
|
if (Pats[0].size() == 0) {
|
|
Pats = Pats.slice(1);
|
|
if (Pats.empty())
|
|
// Fast path. If a pattern is '*', it matches anything.
|
|
return true;
|
|
for (size_t I = 0, E = S.size(); I < E; ++I)
|
|
if (matchOne(Pats, S.substr(I)))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// If Pats[0] is not '*', it must consume one character.
|
|
if (S.empty() || !Pats[0][(uint8_t)S[0]])
|
|
return false;
|
|
Pats = Pats.slice(1);
|
|
S = S.substr(1);
|
|
}
|
|
}
|