You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
4.4 KiB
157 lines
4.4 KiB
// Scintilla source code edit control |
|
/** @file PropSetSimple.cxx |
|
** A basic string to string map. |
|
**/ |
|
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org> |
|
// The License.txt file describes the conditions under which this software may be distributed. |
|
|
|
// Maintain a dictionary of properties |
|
|
|
#include <cstdlib> |
|
#include <cstring> |
|
|
|
#include <string> |
|
#include <map> |
|
|
|
#include "PropSetSimple.h" |
|
|
|
using namespace Scintilla; |
|
|
|
namespace { |
|
|
|
typedef std::map<std::string, std::string> mapss; |
|
|
|
mapss *PropsFromPointer(void *impl) noexcept { |
|
return static_cast<mapss *>(impl); |
|
} |
|
|
|
constexpr bool IsASpaceCharacter(int ch) noexcept { |
|
return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d)); |
|
} |
|
|
|
} |
|
|
|
PropSetSimple::PropSetSimple() { |
|
mapss *props = new mapss; |
|
impl = static_cast<void *>(props); |
|
} |
|
|
|
PropSetSimple::~PropSetSimple() { |
|
mapss *props = PropsFromPointer(impl); |
|
delete props; |
|
impl = nullptr; |
|
} |
|
|
|
void PropSetSimple::Set(const char *key, const char *val, size_t lenKey, size_t lenVal) { |
|
mapss *props = PropsFromPointer(impl); |
|
if (!*key) // Empty keys are not supported |
|
return; |
|
(*props)[std::string(key, lenKey)] = std::string(val, lenVal); |
|
} |
|
|
|
void PropSetSimple::Set(const char *keyVal) { |
|
while (IsASpaceCharacter(*keyVal)) |
|
keyVal++; |
|
const char *endVal = keyVal; |
|
while (*endVal && (*endVal != '\n')) |
|
endVal++; |
|
const char *eqAt = strchr(keyVal, '='); |
|
if (eqAt) { |
|
Set(keyVal, eqAt + 1, eqAt-keyVal, |
|
endVal - eqAt - 1); |
|
} else if (*keyVal) { // No '=' so assume '=1' |
|
Set(keyVal, "1", endVal-keyVal, 1); |
|
} |
|
} |
|
|
|
void PropSetSimple::SetMultiple(const char *s) { |
|
const char *eol = strchr(s, '\n'); |
|
while (eol) { |
|
Set(s); |
|
s = eol + 1; |
|
eol = strchr(s, '\n'); |
|
} |
|
Set(s); |
|
} |
|
|
|
const char *PropSetSimple::Get(const char *key) const { |
|
mapss *props = PropsFromPointer(impl); |
|
mapss::const_iterator keyPos = props->find(std::string(key)); |
|
if (keyPos != props->end()) { |
|
return keyPos->second.c_str(); |
|
} else { |
|
return ""; |
|
} |
|
} |
|
|
|
// There is some inconsistency between GetExpanded("foo") and Expand("$(foo)"). |
|
// A solution is to keep a stack of variables that have been expanded, so that |
|
// recursive expansions can be skipped. For now I'll just use the C++ stack |
|
// for that, through a recursive function and a simple chain of pointers. |
|
|
|
struct VarChain { |
|
VarChain(const char *var_=nullptr, const VarChain *link_= nullptr) noexcept : var(var_), link(link_) {} |
|
|
|
bool contains(const char *testVar) const { |
|
return (var && (0 == strcmp(var, testVar))) |
|
|| (link && link->contains(testVar)); |
|
} |
|
|
|
const char *var; |
|
const VarChain *link; |
|
}; |
|
|
|
static int ExpandAllInPlace(const PropSetSimple &props, std::string &withVars, int maxExpands, const VarChain &blankVars) { |
|
size_t varStart = withVars.find("$("); |
|
while ((varStart != std::string::npos) && (maxExpands > 0)) { |
|
const size_t varEnd = withVars.find(')', varStart+2); |
|
if (varEnd == std::string::npos) { |
|
break; |
|
} |
|
|
|
// For consistency, when we see '$(ab$(cde))', expand the inner variable first, |
|
// regardless whether there is actually a degenerate variable named 'ab$(cde'. |
|
size_t innerVarStart = withVars.find("$(", varStart+2); |
|
while ((innerVarStart != std::string::npos) && (innerVarStart > varStart) && (innerVarStart < varEnd)) { |
|
varStart = innerVarStart; |
|
innerVarStart = withVars.find("$(", varStart+2); |
|
} |
|
|
|
std::string var(withVars, varStart + 2, varEnd - varStart - 2); |
|
std::string val = props.Get(var.c_str()); |
|
|
|
if (blankVars.contains(var.c_str())) { |
|
val = ""; // treat blankVar as an empty string (e.g. to block self-reference) |
|
} |
|
|
|
if (--maxExpands >= 0) { |
|
maxExpands = ExpandAllInPlace(props, val, maxExpands, VarChain(var.c_str(), &blankVars)); |
|
} |
|
|
|
withVars.erase(varStart, varEnd-varStart+1); |
|
withVars.insert(varStart, val.c_str(), val.length()); |
|
|
|
varStart = withVars.find("$("); |
|
} |
|
|
|
return maxExpands; |
|
} |
|
|
|
size_t PropSetSimple::GetExpanded(const char *key, char *result) const { |
|
std::string val = Get(key); |
|
ExpandAllInPlace(*this, val, 100, VarChain(key)); |
|
const size_t n = val.size(); |
|
if (result) { |
|
memcpy(result, val.c_str(), n+1); |
|
} |
|
return n; // Not including NUL |
|
} |
|
|
|
int PropSetSimple::GetInt(const char *key, int defaultValue) const { |
|
std::string val = Get(key); |
|
ExpandAllInPlace(*this, val, 100, VarChain(key)); |
|
if (!val.empty()) { |
|
return atoi(val.c_str()); |
|
} |
|
return defaultValue; |
|
}
|
|
|