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