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 24 mapss *PropsFromPointer(void *impl) noexcept { 25 return static_cast<mapss *>(impl); 26 } 27 28 constexpr bool IsASpaceCharacter(int ch) noexcept { 29 return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d)); 30 } 31 32 } 33 34 PropSetSimple::PropSetSimple() { 35 mapss *props = new mapss; 36 impl = static_cast<void *>(props); 37 } 38 39 PropSetSimple::~PropSetSimple() { 40 mapss *props = PropsFromPointer(impl); 41 delete props; 42 impl = nullptr; 43 } 44 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 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 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 77 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 { 93 VarChain(const char *var_=nullptr, const VarChain *link_= nullptr) noexcept : var(var_), link(link_) {} 94 95 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 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 140 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 150 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