1*8af74909SZhong Yang // Scintilla source code edit control
2*8af74909SZhong Yang /** @file PropSetSimple.cxx
3*8af74909SZhong Yang ** A basic string to string map.
4*8af74909SZhong Yang **/
5*8af74909SZhong Yang // Copyright 1998-2010 by Neil Hodgson <[email protected]>
6*8af74909SZhong Yang // The License.txt file describes the conditions under which this software may be distributed.
7*8af74909SZhong Yang
8*8af74909SZhong Yang // Maintain a dictionary of properties
9*8af74909SZhong Yang
10*8af74909SZhong Yang #include <cstdlib>
11*8af74909SZhong Yang #include <cstring>
12*8af74909SZhong Yang
13*8af74909SZhong Yang #include <string>
14*8af74909SZhong Yang #include <map>
15*8af74909SZhong Yang
16*8af74909SZhong Yang #include "PropSetSimple.h"
17*8af74909SZhong Yang
18*8af74909SZhong Yang using namespace Scintilla;
19*8af74909SZhong Yang
20*8af74909SZhong Yang namespace {
21*8af74909SZhong Yang
22*8af74909SZhong Yang typedef std::map<std::string, std::string> mapss;
23*8af74909SZhong Yang
PropsFromPointer(void * impl)24*8af74909SZhong Yang mapss *PropsFromPointer(void *impl) noexcept {
25*8af74909SZhong Yang return static_cast<mapss *>(impl);
26*8af74909SZhong Yang }
27*8af74909SZhong Yang
IsASpaceCharacter(int ch)28*8af74909SZhong Yang constexpr bool IsASpaceCharacter(int ch) noexcept {
29*8af74909SZhong Yang return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
30*8af74909SZhong Yang }
31*8af74909SZhong Yang
32*8af74909SZhong Yang }
33*8af74909SZhong Yang
PropSetSimple()34*8af74909SZhong Yang PropSetSimple::PropSetSimple() {
35*8af74909SZhong Yang mapss *props = new mapss;
36*8af74909SZhong Yang impl = static_cast<void *>(props);
37*8af74909SZhong Yang }
38*8af74909SZhong Yang
~PropSetSimple()39*8af74909SZhong Yang PropSetSimple::~PropSetSimple() {
40*8af74909SZhong Yang mapss *props = PropsFromPointer(impl);
41*8af74909SZhong Yang delete props;
42*8af74909SZhong Yang impl = nullptr;
43*8af74909SZhong Yang }
44*8af74909SZhong Yang
Set(const char * key,const char * val,size_t lenKey,size_t lenVal)45*8af74909SZhong Yang void PropSetSimple::Set(const char *key, const char *val, size_t lenKey, size_t lenVal) {
46*8af74909SZhong Yang mapss *props = PropsFromPointer(impl);
47*8af74909SZhong Yang if (!*key) // Empty keys are not supported
48*8af74909SZhong Yang return;
49*8af74909SZhong Yang (*props)[std::string(key, lenKey)] = std::string(val, lenVal);
50*8af74909SZhong Yang }
51*8af74909SZhong Yang
Set(const char * keyVal)52*8af74909SZhong Yang void PropSetSimple::Set(const char *keyVal) {
53*8af74909SZhong Yang while (IsASpaceCharacter(*keyVal))
54*8af74909SZhong Yang keyVal++;
55*8af74909SZhong Yang const char *endVal = keyVal;
56*8af74909SZhong Yang while (*endVal && (*endVal != '\n'))
57*8af74909SZhong Yang endVal++;
58*8af74909SZhong Yang const char *eqAt = strchr(keyVal, '=');
59*8af74909SZhong Yang if (eqAt) {
60*8af74909SZhong Yang Set(keyVal, eqAt + 1, eqAt-keyVal,
61*8af74909SZhong Yang endVal - eqAt - 1);
62*8af74909SZhong Yang } else if (*keyVal) { // No '=' so assume '=1'
63*8af74909SZhong Yang Set(keyVal, "1", endVal-keyVal, 1);
64*8af74909SZhong Yang }
65*8af74909SZhong Yang }
66*8af74909SZhong Yang
SetMultiple(const char * s)67*8af74909SZhong Yang void PropSetSimple::SetMultiple(const char *s) {
68*8af74909SZhong Yang const char *eol = strchr(s, '\n');
69*8af74909SZhong Yang while (eol) {
70*8af74909SZhong Yang Set(s);
71*8af74909SZhong Yang s = eol + 1;
72*8af74909SZhong Yang eol = strchr(s, '\n');
73*8af74909SZhong Yang }
74*8af74909SZhong Yang Set(s);
75*8af74909SZhong Yang }
76*8af74909SZhong Yang
Get(const char * key) const77*8af74909SZhong Yang const char *PropSetSimple::Get(const char *key) const {
78*8af74909SZhong Yang mapss *props = PropsFromPointer(impl);
79*8af74909SZhong Yang mapss::const_iterator keyPos = props->find(std::string(key));
80*8af74909SZhong Yang if (keyPos != props->end()) {
81*8af74909SZhong Yang return keyPos->second.c_str();
82*8af74909SZhong Yang } else {
83*8af74909SZhong Yang return "";
84*8af74909SZhong Yang }
85*8af74909SZhong Yang }
86*8af74909SZhong Yang
87*8af74909SZhong Yang // There is some inconsistency between GetExpanded("foo") and Expand("$(foo)").
88*8af74909SZhong Yang // A solution is to keep a stack of variables that have been expanded, so that
89*8af74909SZhong Yang // recursive expansions can be skipped. For now I'll just use the C++ stack
90*8af74909SZhong Yang // for that, through a recursive function and a simple chain of pointers.
91*8af74909SZhong Yang
92*8af74909SZhong Yang struct VarChain {
VarChainVarChain93*8af74909SZhong Yang VarChain(const char *var_=nullptr, const VarChain *link_= nullptr) noexcept : var(var_), link(link_) {}
94*8af74909SZhong Yang
containsVarChain95*8af74909SZhong Yang bool contains(const char *testVar) const {
96*8af74909SZhong Yang return (var && (0 == strcmp(var, testVar)))
97*8af74909SZhong Yang || (link && link->contains(testVar));
98*8af74909SZhong Yang }
99*8af74909SZhong Yang
100*8af74909SZhong Yang const char *var;
101*8af74909SZhong Yang const VarChain *link;
102*8af74909SZhong Yang };
103*8af74909SZhong Yang
ExpandAllInPlace(const PropSetSimple & props,std::string & withVars,int maxExpands,const VarChain & blankVars)104*8af74909SZhong Yang static int ExpandAllInPlace(const PropSetSimple &props, std::string &withVars, int maxExpands, const VarChain &blankVars) {
105*8af74909SZhong Yang size_t varStart = withVars.find("$(");
106*8af74909SZhong Yang while ((varStart != std::string::npos) && (maxExpands > 0)) {
107*8af74909SZhong Yang const size_t varEnd = withVars.find(')', varStart+2);
108*8af74909SZhong Yang if (varEnd == std::string::npos) {
109*8af74909SZhong Yang break;
110*8af74909SZhong Yang }
111*8af74909SZhong Yang
112*8af74909SZhong Yang // For consistency, when we see '$(ab$(cde))', expand the inner variable first,
113*8af74909SZhong Yang // regardless whether there is actually a degenerate variable named 'ab$(cde'.
114*8af74909SZhong Yang size_t innerVarStart = withVars.find("$(", varStart+2);
115*8af74909SZhong Yang while ((innerVarStart != std::string::npos) && (innerVarStart > varStart) && (innerVarStart < varEnd)) {
116*8af74909SZhong Yang varStart = innerVarStart;
117*8af74909SZhong Yang innerVarStart = withVars.find("$(", varStart+2);
118*8af74909SZhong Yang }
119*8af74909SZhong Yang
120*8af74909SZhong Yang std::string var(withVars, varStart + 2, varEnd - varStart - 2);
121*8af74909SZhong Yang std::string val = props.Get(var.c_str());
122*8af74909SZhong Yang
123*8af74909SZhong Yang if (blankVars.contains(var.c_str())) {
124*8af74909SZhong Yang val = ""; // treat blankVar as an empty string (e.g. to block self-reference)
125*8af74909SZhong Yang }
126*8af74909SZhong Yang
127*8af74909SZhong Yang if (--maxExpands >= 0) {
128*8af74909SZhong Yang maxExpands = ExpandAllInPlace(props, val, maxExpands, VarChain(var.c_str(), &blankVars));
129*8af74909SZhong Yang }
130*8af74909SZhong Yang
131*8af74909SZhong Yang withVars.erase(varStart, varEnd-varStart+1);
132*8af74909SZhong Yang withVars.insert(varStart, val.c_str(), val.length());
133*8af74909SZhong Yang
134*8af74909SZhong Yang varStart = withVars.find("$(");
135*8af74909SZhong Yang }
136*8af74909SZhong Yang
137*8af74909SZhong Yang return maxExpands;
138*8af74909SZhong Yang }
139*8af74909SZhong Yang
GetExpanded(const char * key,char * result) const140*8af74909SZhong Yang size_t PropSetSimple::GetExpanded(const char *key, char *result) const {
141*8af74909SZhong Yang std::string val = Get(key);
142*8af74909SZhong Yang ExpandAllInPlace(*this, val, 100, VarChain(key));
143*8af74909SZhong Yang const size_t n = val.size();
144*8af74909SZhong Yang if (result) {
145*8af74909SZhong Yang memcpy(result, val.c_str(), n+1);
146*8af74909SZhong Yang }
147*8af74909SZhong Yang return n; // Not including NUL
148*8af74909SZhong Yang }
149*8af74909SZhong Yang
GetInt(const char * key,int defaultValue) const150*8af74909SZhong Yang int PropSetSimple::GetInt(const char *key, int defaultValue) const {
151*8af74909SZhong Yang std::string val = Get(key);
152*8af74909SZhong Yang ExpandAllInPlace(*this, val, 100, VarChain(key));
153*8af74909SZhong Yang if (!val.empty()) {
154*8af74909SZhong Yang return atoi(val.c_str());
155*8af74909SZhong Yang }
156*8af74909SZhong Yang return defaultValue;
157*8af74909SZhong Yang }
158