]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/PropSet.cxx
1 // SciTE - Scintilla based Text Editor
3 ** A Java style properties file module.
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
8 // Maintain a dictionary of properties
19 using namespace Scintilla
;
22 // The comparison and case changing functions here assume ASCII
23 // or extended ASCII such as the normal Windows code page.
25 static inline char MakeUpperCase(char ch
) {
26 if (ch
< 'a' || ch
> 'z')
29 return static_cast<char>(ch
- 'a' + 'A');
32 static inline bool IsLetter(char ch
) {
33 return ((ch
>= 'a' && ch
<= 'z') || (ch
>= 'A' && ch
<= 'Z'));
36 inline bool IsASpace(unsigned int ch
) {
37 return (ch
== ' ') || ((ch
>= 0x09) && (ch
<= 0x0d));
40 int CompareCaseInsensitive(const char *a
, const char *b
) {
43 char upperA
= MakeUpperCase(*a
);
44 char upperB
= MakeUpperCase(*b
);
46 return upperA
- upperB
;
51 // Either *a or *b is nul
55 int CompareNCaseInsensitive(const char *a
, const char *b
, size_t len
) {
56 while (*a
&& *b
&& len
) {
58 char upperA
= MakeUpperCase(*a
);
59 char upperB
= MakeUpperCase(*b
);
61 return upperA
- upperB
;
70 // Either *a or *b is nul
74 bool EqualCaseInsensitive(const char *a
, const char *b
) {
75 return 0 == CompareCaseInsensitive(a
, b
);
78 // Since the CaseInsensitive functions declared in SString
79 // are implemented here, I will for now put the non-inline
80 // implementations of the SString members here as well, so
81 // that I can quickly see what effect this has.
83 SString::SString(int i
) : sizeGrowth(sizeGrowthDefault
) {
85 sprintf(number
, "%0d", i
);
86 s
= StringAllocate(number
);
87 sSize
= sLen
= (s
) ? strlen(s
) : 0;
90 SString::SString(double d
, int precision
) : sizeGrowth(sizeGrowthDefault
) {
92 sprintf(number
, "%.*f", precision
, d
);
93 s
= StringAllocate(number
);
94 sSize
= sLen
= (s
) ? strlen(s
) : 0;
97 bool SString::grow(lenpos_t lenNew
) {
98 while (sizeGrowth
* 6 < lenNew
) {
101 char *sNew
= new char[lenNew
+ sizeGrowth
+ 1];
104 memcpy(sNew
, s
, sLen
);
109 sSize
= lenNew
+ sizeGrowth
;
114 SString
&SString::assign(const char *sOther
, lenpos_t sSize_
) {
117 } else if (sSize_
== measure_length
) {
118 sSize_
= strlen(sOther
);
120 if (sSize
> 0 && sSize_
<= sSize
) { // Does not allocate new buffer if the current is big enough
122 memcpy(s
, sOther
, sSize_
);
128 s
= StringAllocate(sOther
, sSize_
);
130 sSize
= sSize_
; // Allow buffer bigger than real string, thus providing space to grow
139 bool SString::operator==(const SString
&sOther
) const {
140 if ((s
== 0) && (sOther
.s
== 0))
142 if ((s
== 0) || (sOther
.s
== 0))
144 return strcmp(s
, sOther
.s
) == 0;
147 bool SString::operator==(const char *sOther
) const {
148 if ((s
== 0) && (sOther
== 0))
150 if ((s
== 0) || (sOther
== 0))
152 return strcmp(s
, sOther
) == 0;
155 SString
SString::substr(lenpos_t subPos
, lenpos_t subLen
) const {
156 if (subPos
>= sLen
) {
157 return SString(); // return a null string if start index is out of bounds
159 if ((subLen
== measure_length
) || (subPos
+ subLen
> sLen
)) {
160 subLen
= sLen
- subPos
; // can't substr past end of source string
162 return SString(s
, subPos
, subPos
+ subLen
);
165 SString
&SString::lowercase(lenpos_t subPos
, lenpos_t subLen
) {
166 if ((subLen
== measure_length
) || (subPos
+ subLen
> sLen
)) {
167 subLen
= sLen
- subPos
; // don't apply past end of string
169 for (lenpos_t i
= subPos
; i
< subPos
+ subLen
; i
++) {
170 if (s
[i
] < 'A' || s
[i
] > 'Z')
173 s
[i
] = static_cast<char>(s
[i
] - 'A' + 'a');
178 SString
&SString::uppercase(lenpos_t subPos
, lenpos_t subLen
) {
179 if ((subLen
== measure_length
) || (subPos
+ subLen
> sLen
)) {
180 subLen
= sLen
- subPos
; // don't apply past end of string
182 for (lenpos_t i
= subPos
; i
< subPos
+ subLen
; i
++) {
183 if (s
[i
] < 'a' || s
[i
] > 'z')
186 s
[i
] = static_cast<char>(s
[i
] - 'a' + 'A');
191 SString
&SString::append(const char *sOther
, lenpos_t sLenOther
, char sep
) {
195 if (sLenOther
== measure_length
) {
196 sLenOther
= strlen(sOther
);
199 if (sLen
&& sep
) { // Only add a separator if not empty
202 lenpos_t lenNew
= sLen
+ sLenOther
+ lenSep
;
203 // Conservative about growing the buffer: don't do it, unless really needed
204 if ((lenNew
< sSize
) || (grow(lenNew
))) {
209 memcpy(&s
[sLen
], sOther
, sLenOther
);
216 SString
&SString::insert(lenpos_t pos
, const char *sOther
, lenpos_t sLenOther
) {
217 if (!sOther
|| pos
> sLen
) {
220 if (sLenOther
== measure_length
) {
221 sLenOther
= strlen(sOther
);
223 lenpos_t lenNew
= sLen
+ sLenOther
;
224 // Conservative about growing the buffer: don't do it, unless really needed
225 if ((lenNew
< sSize
) || grow(lenNew
)) {
226 lenpos_t moveChars
= sLen
- pos
+ 1;
227 for (lenpos_t i
= moveChars
; i
> 0; i
--) {
228 s
[pos
+ sLenOther
+ i
- 1] = s
[pos
+ i
- 1];
230 memcpy(s
+ pos
, sOther
, sLenOther
);
237 * Remove @a len characters from the @a pos position, included.
238 * Characters at pos + len and beyond replace characters at pos.
239 * If @a len is 0, or greater than the length of the string
240 * starting at @a pos, the string is just truncated at @a pos.
242 void SString::remove(lenpos_t pos
, lenpos_t len
) {
246 if (len
< 1 || pos
+ len
>= sLen
) {
250 for (lenpos_t i
= pos
; i
< sLen
- len
+ 1; i
++) {
257 bool SString::startswith(const char *prefix
) {
258 lenpos_t lenPrefix
= strlen(prefix
);
259 if (lenPrefix
> sLen
) {
262 return strncmp(s
, prefix
, lenPrefix
) == 0;
265 bool SString::endswith(const char *suffix
) {
266 lenpos_t lenSuffix
= strlen(suffix
);
267 if (lenSuffix
> sLen
) {
270 return strncmp(s
+ sLen
- lenSuffix
, suffix
, lenSuffix
) == 0;
273 int SString::search(const char *sFind
, lenpos_t start
) const {
275 const char *sFound
= strstr(s
+ start
, sFind
);
283 int SString::substitute(char chFind
, char chReplace
) {
287 t
= strchr(t
, chFind
);
297 int SString::substitute(const char *sFind
, const char *sReplace
) {
299 lenpos_t lenFind
= strlen(sFind
);
300 lenpos_t lenReplace
= strlen(sReplace
);
301 int posFound
= search(sFind
);
302 while (posFound
>= 0) {
303 remove(posFound
, lenFind
);
304 insert(posFound
, sReplace
, lenReplace
);
305 posFound
= search(sFind
, posFound
+ lenReplace
);
311 char *SContainer::StringAllocate(lenpos_t len
) {
312 if (len
!= measure_length
) {
313 return new char[len
+ 1];
319 char *SContainer::StringAllocate(const char *s
, lenpos_t len
) {
323 if (len
== measure_length
) {
326 char *sNew
= new char[len
+ 1];
328 memcpy(sNew
, s
, len
);
334 // End SString functions
338 for (int root
= 0; root
< hashRoots
; root
++)
342 PropSet::~PropSet() {
347 void PropSet::Set(const char *key
, const char *val
, int lenKey
, int lenVal
) {
348 if (!*key
) // Empty keys are not supported
351 lenKey
= static_cast<int>(strlen(key
));
353 lenVal
= static_cast<int>(strlen(val
));
354 unsigned int hash
= HashString(key
, lenKey
);
355 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
356 if ((hash
== p
->hash
) &&
357 ((strlen(p
->key
) == static_cast<unsigned int>(lenKey
)) &&
358 (0 == strncmp(p
->key
, key
, lenKey
)))) {
359 // Replace current value
361 p
->val
= StringDup(val
, lenVal
);
366 Property
*pNew
= new Property
;
369 pNew
->key
= StringDup(key
, lenKey
);
370 pNew
->val
= StringDup(val
, lenVal
);
371 pNew
->next
= props
[hash
% hashRoots
];
372 props
[hash
% hashRoots
] = pNew
;
376 void PropSet::Set(const char *keyVal
) {
377 while (IsASpace(*keyVal
))
379 const char *endVal
= keyVal
;
380 while (*endVal
&& (*endVal
!= '\n'))
382 const char *eqAt
= strchr(keyVal
, '=');
384 Set(keyVal
, eqAt
+ 1, eqAt
-keyVal
, endVal
- eqAt
- 1);
385 } else if (*keyVal
) { // No '=' so assume '=1'
386 Set(keyVal
, "1", endVal
-keyVal
, 1);
390 void PropSet::Unset(const char *key
, int lenKey
) {
391 if (!*key
) // Empty keys are not supported
394 lenKey
= static_cast<int>(strlen(key
));
395 unsigned int hash
= HashString(key
, lenKey
);
396 Property
*pPrev
= NULL
;
397 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
398 if ((hash
== p
->hash
) &&
399 ((strlen(p
->key
) == static_cast<unsigned int>(lenKey
)) &&
400 (0 == strncmp(p
->key
, key
, lenKey
)))) {
402 pPrev
->next
= p
->next
;
404 props
[hash
% hashRoots
] = p
->next
;
406 enumnext
= p
->next
; // Not that anyone should mix enum and Set / Unset.
417 void PropSet::SetMultiple(const char *s
) {
418 const char *eol
= strchr(s
, '\n');
422 eol
= strchr(s
, '\n');
427 SString
PropSet::Get(const char *key
) const {
428 unsigned int hash
= HashString(key
, strlen(key
));
429 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
430 if ((hash
== p
->hash
) && (0 == strcmp(p
->key
, key
))) {
435 // Failed here, so try in base property set
436 return superPS
->Get(key
);
442 // There is some inconsistency between GetExpanded("foo") and Expand("$(foo)").
443 // A solution is to keep a stack of variables that have been expanded, so that
444 // recursive expansions can be skipped. For now I'll just use the C++ stack
445 // for that, through a recursive function and a simple chain of pointers.
448 VarChain(const char*var_
=NULL
, const VarChain
*link_
=NULL
): var(var_
), link(link_
) {}
450 bool contains(const char *testVar
) const {
451 return (var
&& (0 == strcmp(var
, testVar
)))
452 || (link
&& link
->contains(testVar
));
456 const VarChain
*link
;
459 static int ExpandAllInPlace(const PropSet
&props
, SString
&withVars
, int maxExpands
, const VarChain
&blankVars
= VarChain()) {
460 int varStart
= withVars
.search("$(");
461 while ((varStart
>= 0) && (maxExpands
> 0)) {
462 int varEnd
= withVars
.search(")", varStart
+2);
467 // For consistency, when we see '$(ab$(cde))', expand the inner variable first,
468 // regardless whether there is actually a degenerate variable named 'ab$(cde'.
469 int innerVarStart
= withVars
.search("$(", varStart
+2);
470 while ((innerVarStart
> varStart
) && (innerVarStart
< varEnd
)) {
471 varStart
= innerVarStart
;
472 innerVarStart
= withVars
.search("$(", varStart
+2);
475 SString
var(withVars
.c_str(), varStart
+ 2, varEnd
);
476 SString val
= props
.Get(var
.c_str());
478 if (blankVars
.contains(var
.c_str())) {
479 val
.clear(); // treat blankVar as an empty string (e.g. to block self-reference)
482 if (--maxExpands
>= 0) {
483 maxExpands
= ExpandAllInPlace(props
, val
, maxExpands
, VarChain(var
.c_str(), &blankVars
));
486 withVars
.remove(varStart
, varEnd
-varStart
+1);
487 withVars
.insert(varStart
, val
.c_str(), val
.length());
489 varStart
= withVars
.search("$(");
495 SString
PropSet::GetExpanded(const char *key
) const {
496 SString val
= Get(key
);
497 ExpandAllInPlace(*this, val
, 100, VarChain(key
));
501 SString
PropSet::Expand(const char *withVars
, int maxExpands
) const {
502 SString val
= withVars
;
503 ExpandAllInPlace(*this, val
, maxExpands
);
507 int PropSet::GetInt(const char *key
, int defaultValue
) const {
508 SString val
= GetExpanded(key
);
514 bool isprefix(const char *target
, const char *prefix
) {
515 while (*target
&& *prefix
) {
516 if (*target
!= *prefix
)
527 void PropSet::Clear() {
528 for (int root
= 0; root
< hashRoots
; root
++) {
529 Property
*p
= props
[root
];
531 Property
*pNext
= p
->next
;
544 char *PropSet::ToString() const {
546 for (int r
= 0; r
< hashRoots
; r
++) {
547 for (Property
*p
= props
[r
]; p
; p
= p
->next
) {
548 len
+= strlen(p
->key
) + 1;
549 len
+= strlen(p
->val
) + 1;
553 len
= 1; // Return as empty string
554 char *ret
= new char [len
];
557 for (int root
= 0; root
< hashRoots
; root
++) {
558 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
573 * Creates an array that points into each word in the string and puts \0 terminators
576 static char **ArrayFromWordList(char *wordlist
, int *len
, bool onlyLineEnds
= false) {
579 // For rapid determination of whether a character is a separator, build
581 bool wordSeparator
[256];
582 for (int i
=0;i
<256; i
++) {
583 wordSeparator
[i
] = false;
585 wordSeparator
['\r'] = true;
586 wordSeparator
['\n'] = true;
588 wordSeparator
[' '] = true;
589 wordSeparator
['\t'] = true;
591 for (int j
= 0; wordlist
[j
]; j
++) {
592 int curr
= static_cast<unsigned char>(wordlist
[j
]);
593 if (!wordSeparator
[curr
] && wordSeparator
[prev
])
597 char **keywords
= new char *[words
+ 1];
601 size_t slen
= strlen(wordlist
);
602 for (size_t k
= 0; k
< slen
; k
++) {
603 if (!wordSeparator
[static_cast<unsigned char>(wordlist
[k
])]) {
605 keywords
[words
] = &wordlist
[k
];
613 keywords
[words
] = &wordlist
[slen
];
621 void WordList::Clear() {
632 void WordList::Set(const char *s
) {
635 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
638 extern "C" int cmpString(const void *a1
, const void *a2
) {
639 // Can't work out the correct incantation to use modern casts here
640 return strcmp(*(char**)(a1
), *(char**)(a2
));
643 static void SortWordList(char **words
, unsigned int len
) {
644 qsort(reinterpret_cast<void*>(words
), len
, sizeof(*words
),
648 bool WordList::InList(const char *s
) {
653 SortWordList(words
, len
);
654 for (unsigned int k
= 0; k
< (sizeof(starts
) / sizeof(starts
[0])); k
++)
656 for (int l
= len
- 1; l
>= 0; l
--) {
657 unsigned char indexChar
= words
[l
][0];
658 starts
[indexChar
] = l
;
661 unsigned char firstChar
= s
[0];
662 int j
= starts
[firstChar
];
664 while ((unsigned char)words
[j
][0] == firstChar
) {
665 if (s
[1] == words
[j
][1]) {
666 const char *a
= words
[j
] + 1;
667 const char *b
= s
+ 1;
668 while (*a
&& *a
== *b
) {
680 while (words
[j
][0] == '^') {
681 const char *a
= words
[j
] + 1;
683 while (*a
&& *a
== *b
) {
695 /** similar to InList, but word s can be a substring of keyword.
696 * eg. the keyword define is defined as def~ine. This means the word must start
697 * with def to be a keyword, but also defi, defin and define are valid.
698 * The marker is ~ in this case.
700 bool WordList::InListAbbreviated(const char *s
, const char marker
) {
705 SortWordList(words
, len
);
706 for (unsigned int k
= 0; k
< (sizeof(starts
) / sizeof(starts
[0])); k
++)
708 for (int l
= len
- 1; l
>= 0; l
--) {
709 unsigned char indexChar
= words
[l
][0];
710 starts
[indexChar
] = l
;
713 unsigned char firstChar
= s
[0];
714 int j
= starts
[firstChar
];
716 while (words
[j
][0] == firstChar
) {
717 bool isSubword
= false;
719 if (words
[j
][1] == marker
) {
723 if (s
[1] == words
[j
][start
]) {
724 const char *a
= words
[j
] + start
;
725 const char *b
= s
+ 1;
726 while (*a
&& *a
== *b
) {
734 if ((!*a
|| isSubword
) && !*b
)
742 while (words
[j
][0] == '^') {
743 const char *a
= words
[j
] + 1;
745 while (*a
&& *a
== *b
) {