PropSetSimple.cpp 4.4 KB

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