📄 propset.cxx
字号:
// SciTE - Scintilla based Text Editor/** @file PropSet.cxx ** A Java style properties file module. **/// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>// The License.txt file describes the conditions under which this software may be distributed.// Maintain a dictionary of properties#include <stdlib.h>#include <string.h>#include <stdio.h>#include "Platform.h"#include "PropSet.h"// The comparison and case changing functions here assume ASCII// or extended ASCII such as the normal Windows code page.static inline char MakeUpperCase(char ch) { if (ch < 'a' || ch > 'z') return ch; else return static_cast<char>(ch - 'a' + 'A');}static inline bool IsLetter(char ch) { return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'));}inline bool IsASpace(unsigned int ch) { return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));}int CompareCaseInsensitive(const char *a, const char *b) { while (*a && *b) { if (*a != *b) { char upperA = MakeUpperCase(*a); char upperB = MakeUpperCase(*b); if (upperA != upperB) return upperA - upperB; } a++; b++; } // Either *a or *b is nul return *a - *b;}int CompareNCaseInsensitive(const char *a, const char *b, size_t len) { while (*a && *b && len) { if (*a != *b) { char upperA = MakeUpperCase(*a); char upperB = MakeUpperCase(*b); if (upperA != upperB) return upperA - upperB; } a++; b++; len--; } if (len == 0) return 0; else // Either *a or *b is nul return *a - *b;}bool EqualCaseInsensitive(const char *a, const char *b) { return 0 == CompareCaseInsensitive(a, b);}// Since the CaseInsensitive functions declared in SString// are implemented here, I will for now put the non-inline// implementations of the SString members here as well, so// that I can quickly see what effect this has.SString::SString(int i) : sizeGrowth(sizeGrowthDefault) { char number[32]; sprintf(number, "%0d", i); s = StringAllocate(number); sSize = sLen = (s) ? strlen(s) : 0;}SString::SString(double d, int precision) : sizeGrowth(sizeGrowthDefault) { char number[32]; sprintf(number, "%.*f", precision, d); s = StringAllocate(number); sSize = sLen = (s) ? strlen(s) : 0;}bool SString::grow(lenpos_t lenNew) { while (sizeGrowth * 6 < lenNew) { sizeGrowth *= 2; } char *sNew = new char[lenNew + sizeGrowth + 1]; if (sNew) { if (s) { memcpy(sNew, s, sLen); delete []s; } s = sNew; s[sLen] = '\0'; sSize = lenNew + sizeGrowth; } return sNew != 0;}SString &SString::assign(const char *sOther, lenpos_t sSize_) { if (!sOther) { sSize_ = 0; } else if (sSize_ == measure_length) { sSize_ = strlen(sOther); } if (sSize > 0 && sSize_ <= sSize) { // Does not allocate new buffer if the current is big enough if (s && sSize_) { memcpy(s, sOther, sSize_); } s[sSize_] = '\0'; sLen = sSize_; } else { delete []s; s = StringAllocate(sOther, sSize_); if (s) { sSize = sSize_; // Allow buffer bigger than real string, thus providing space to grow sLen = sSize_; } else { sSize = sLen = 0; } } return *this;}bool SString::operator==(const SString &sOther) const { if ((s == 0) && (sOther.s == 0)) return true; if ((s == 0) || (sOther.s == 0)) return false; return strcmp(s, sOther.s) == 0;}bool SString::operator==(const char *sOther) const { if ((s == 0) && (sOther == 0)) return true; if ((s == 0) || (sOther == 0)) return false; return strcmp(s, sOther) == 0;}SString SString::substr(lenpos_t subPos, lenpos_t subLen) const { if (subPos >= sLen) { return SString(); // return a null string if start index is out of bounds } if ((subLen == measure_length) || (subPos + subLen > sLen)) { subLen = sLen - subPos; // can't substr past end of source string } return SString(s, subPos, subPos + subLen);}SString &SString::lowercase(lenpos_t subPos, lenpos_t subLen) { if ((subLen == measure_length) || (subPos + subLen > sLen)) { subLen = sLen - subPos; // don't apply past end of string } for (lenpos_t i = subPos; i < subPos + subLen; i++) { if (s[i] < 'A' || s[i] > 'Z') continue; else s[i] = static_cast<char>(s[i] - 'A' + 'a'); } return *this;}SString &SString::uppercase(lenpos_t subPos, lenpos_t subLen) { if ((subLen == measure_length) || (subPos + subLen > sLen)) { subLen = sLen - subPos; // don't apply past end of string } for (lenpos_t i = subPos; i < subPos + subLen; i++) { if (s[i] < 'a' || s[i] > 'z') continue; else s[i] = static_cast<char>(s[i] - 'a' + 'A'); } return *this;}SString &SString::append(const char *sOther, lenpos_t sLenOther, char sep) { if (!sOther) { return *this; } if (sLenOther == measure_length) { sLenOther = strlen(sOther); } int lenSep = 0; if (sLen && sep) { // Only add a separator if not empty lenSep = 1; } lenpos_t lenNew = sLen + sLenOther + lenSep; // Conservative about growing the buffer: don't do it, unless really needed if ((lenNew < sSize) || (grow(lenNew))) { if (lenSep) { s[sLen] = sep; sLen++; } memcpy(&s[sLen], sOther, sLenOther); sLen += sLenOther; s[sLen] = '\0'; } return *this;}SString &SString::insert(lenpos_t pos, const char *sOther, lenpos_t sLenOther) { if (!sOther || pos > sLen) { return *this; } if (sLenOther == measure_length) { sLenOther = strlen(sOther); } lenpos_t lenNew = sLen + sLenOther; // Conservative about growing the buffer: don't do it, unless really needed if ((lenNew < sSize) || grow(lenNew)) { lenpos_t moveChars = sLen - pos + 1; for (lenpos_t i = moveChars; i > 0; i--) { s[pos + sLenOther + i - 1] = s[pos + i - 1]; } memcpy(s + pos, sOther, sLenOther); sLen = lenNew; } return *this;}/** * Remove @a len characters from the @a pos position, included. * Characters at pos + len and beyond replace characters at pos. * If @a len is 0, or greater than the length of the string * starting at @a pos, the string is just truncated at @a pos. */void SString::remove(lenpos_t pos, lenpos_t len) { if (pos >= sLen) { return; } if (len < 1 || pos + len >= sLen) { s[pos] = '\0'; sLen = pos; } else { for (lenpos_t i = pos; i < sLen - len + 1; i++) { s[i] = s[i+len]; } sLen -= len; }}bool SString::startswith(const char *prefix) { lenpos_t lenPrefix = strlen(prefix); if (lenPrefix > sLen) { return false; } return strncmp(s, prefix, lenPrefix) == 0;}bool SString::endswith(const char *suffix) { lenpos_t lenSuffix = strlen(suffix); if (lenSuffix > sLen) { return false; } return strncmp(s + sLen - lenSuffix, suffix, lenSuffix) == 0;}int SString::search(const char *sFind, lenpos_t start) const { if (start < sLen) { const char *sFound = strstr(s + start, sFind); if (sFound) { return sFound - s; } } return -1;}int SString::substitute(char chFind, char chReplace) { int c = 0; char *t = s; while (t) { t = strchr(t, chFind); if (t) { *t = chReplace; t++; c++; } } return c;}int SString::substitute(const char *sFind, const char *sReplace) { int c = 0; lenpos_t lenFind = strlen(sFind); lenpos_t lenReplace = strlen(sReplace); int posFound = search(sFind); while (posFound >= 0) { remove(posFound, lenFind); insert(posFound, sReplace, lenReplace); posFound = search(sFind, posFound + lenReplace); c++; } return c;}char *SContainer::StringAllocate(lenpos_t len) { if (len != measure_length) { return new char[len + 1]; } else { return 0; }}char *SContainer::StringAllocate(const char *s, lenpos_t len) { if (s == 0) { return 0; } if (len == measure_length) { len = strlen(s); } char *sNew = new char[len + 1]; if (sNew) { memcpy(sNew, s, len); sNew[len] = '\0'; } return sNew;}// End SString functionsbool PropSet::caseSensitiveFilenames = false;PropSet::PropSet() { superPS = 0; for (int root = 0; root < hashRoots; root++) props[root] = 0;}PropSet::~PropSet() { superPS = 0; Clear();}void PropSet::Set(const char *key, const char *val, int lenKey, int lenVal) { if (!*key) // Empty keys are not supported return; if (lenKey == -1) lenKey = static_cast<int>(strlen(key)); if (lenVal == -1) lenVal = static_cast<int>(strlen(val)); unsigned int hash = HashString(key, lenKey); for (Property *p = props[hash % hashRoots]; p; p = p->next) { if ((hash == p->hash) && ((strlen(p->key) == static_cast<unsigned int>(lenKey)) && (0 == strncmp(p->key, key, lenKey)))) { // Replace current value delete [](p->val); p->val = StringDup(val, lenVal); return; } } // Not found Property *pNew = new Property; if (pNew) { pNew->hash = hash; pNew->key = StringDup(key, lenKey); pNew->val = StringDup(val, lenVal); pNew->next = props[hash % hashRoots]; props[hash % hashRoots] = pNew; }}void PropSet::Set(const char *keyVal) { while (IsASpace(*keyVal)) keyVal++; const char *endVal = keyVal; while (*endVal && (*endVal != '\n')) endVal++; const char *eqAt = strchr(keyVal, '='); if (eqAt) { Set(keyVal, eqAt + 1, eqAt-keyVal, endVal - eqAt - 1); } else if (*keyVal) { // No '=' so assume '=1' Set(keyVal, "1", endVal-keyVal, 1); }}void PropSet::Unset(const char *key, int lenKey) { if (!*key) // Empty keys are not supported return; if (lenKey == -1) lenKey = static_cast<int>(strlen(key)); unsigned int hash = HashString(key, lenKey); Property *pPrev = NULL; for (Property *p = props[hash % hashRoots]; p; p = p->next) { if ((hash == p->hash) && ((strlen(p->key) == static_cast<unsigned int>(lenKey)) && (0 == strncmp(p->key, key, lenKey)))) { if (pPrev) pPrev->next = p->next; else props[hash % hashRoots] = p->next; if (p == enumnext) enumnext = p->next; // Not that anyone should mix enum and Set / Unset. delete [](p->key); delete [](p->val); delete p; return; } else { pPrev = p; } }}void PropSet::SetMultiple(const char *s) { const char *eol = strchr(s, '\n'); while (eol) { Set(s); s = eol + 1; eol = strchr(s, '\n'); } Set(s);}SString PropSet::Get(const char *key) { unsigned int hash = HashString(key, strlen(key)); for (Property *p = props[hash % hashRoots]; p; p = p->next) { if ((hash == p->hash) && (0 == strcmp(p->key, key))) { return p->val; } } if (superPS) { // Failed here, so try in base property set return superPS->Get(key); } else { return ""; }}bool PropSet::IncludesVar(const char *value, const char *key) { const char *var = strstr(value, "$("); while (var) { if (isprefix(var + 2, key) && (var[2 + strlen(key)] == ')')) { // Found $(key) which would lead to an infinite loop so exit return true; } var = strstr(var + 2, ")"); if (var) var = strstr(var + 1, "$("); } return false;}// There is some inconsistency between GetExpanded("foo") and Expand("$(foo)").// A solution is to keep a stack of variables that have been expanded, so that// recursive expansions can be skipped. For now I'll just use the C++ stack// for that, through a recursive function and a simple chain of pointers.struct VarChain { VarChain(const char*var_=NULL, const VarChain *link_=NULL): var(var_), link(link_) {} bool contains(const char *testVar) const { return (var && (0 == strcmp(var, testVar))) || (link && link->contains(testVar)); } const char *var; const VarChain *link;};static int ExpandAllInPlace(PropSet &props, SString &withVars, int maxExpands, const VarChain &blankVars = VarChain()) { int varStart = withVars.search("$("); while ((varStart >= 0) && (maxExpands > 0)) { int varEnd = withVars.search(")", varStart+2); if (varEnd < 0) { break; } // For consistency, when we see '$(ab$(cde))', expand the inner variable first, // regardless whether there is actually a degenerate variable named 'ab$(cde'. int innerVarStart = withVars.search("$(", varStart+2); while ((innerVarStart > varStart) && (innerVarStart < varEnd)) { varStart = innerVarStart; innerVarStart = withVars.search("$(", varStart+2); } SString var(withVars.c_str(), varStart + 2, varEnd); SString val = props.Get(var.c_str()); if (blankVars.contains(var.c_str())) { val.clear(); // treat blankVar as an empty string (e.g. to block self-reference) } if (--maxExpands >= 0) { maxExpands = ExpandAllInPlace(props, val, maxExpands, VarChain(var.c_str(), &blankVars)); } withVars.remove(varStart, varEnd-varStart+1); withVars.insert(varStart, val.c_str(), val.length()); varStart = withVars.search("$("); } return maxExpands;}SString PropSet::GetExpanded(const char *key) { SString val = Get(key); ExpandAllInPlace(*this, val, 100, VarChain(key)); return val;}SString PropSet::Expand(const char *withVars, int maxExpands) { SString val = withVars; ExpandAllInPlace(*this, val, maxExpands); return val;}int PropSet::GetInt(const char *key, int defaultValue) { SString val = GetExpanded(key); if (val.length()) return val.value(); return defaultValue;}bool isprefix(const char *target, const char *prefix) { while (*target && *prefix) { if (*target != *prefix) return false; target++; prefix++; } if (*prefix) return false; else return true;}static bool IsSuffix(const char *target, const char *suffix, bool caseSensitive) { size_t lentarget = strlen(target); size_t lensuffix = strlen(suffix); if (lensuffix > lentarget) return false; if (caseSensitive) { for (int i = static_cast<int>(lensuffix) - 1; i >= 0; i--) { if (target[i + lentarget - lensuffix] != suffix[i]) return false; } } else { for (int i = static_cast<int>(lensuffix) - 1; i >= 0; i--) { if (MakeUpperCase(target[i + lentarget - lensuffix]) != MakeUpperCase(suffix[i])) return false; } } return true;}SString PropSet::GetWild(const char *keybase, const char *filename) { for (int root = 0; root < hashRoots; root++) { for (Property *p = props[root]; p; p = p->next) { if (isprefix(p->key, keybase)) { char * orgkeyfile = p->key + strlen(keybase); char *keyfile = NULL; if (strstr(orgkeyfile, "$(") == orgkeyfile) { char *cpendvar = strchr(orgkeyfile, ')'); if (cpendvar) { *cpendvar = '\0'; SString s = GetExpanded(orgkeyfile + 2); *cpendvar = ')'; keyfile = StringDup(s.c_str()); } } char *keyptr = keyfile; if (keyfile == NULL) keyfile = orgkeyfile; for (;;) { char *del = strchr(keyfile, ';'); if (del == NULL) del = keyfile + strlen(keyfile); char delchr = *del;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -