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