]>
git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/PropSet.cxx
18544aef23c87b54b8e09c91b7edffa1944895b6
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 // The comparison and case changing functions here assume ASCII
20 // or extended ASCII such as the normal Windows code page.
22 static inline char MakeUpperCase(char ch
) {
23 if (ch
< 'a' || ch
> 'z')
26 return static_cast<char>(ch
- 'a' + 'A');
29 static inline bool IsLetter(char ch
) {
30 return ((ch
>= 'a' && ch
<= 'z') || (ch
>= 'A' && ch
<= 'Z'));
33 inline bool IsASpace(unsigned int ch
) {
34 return (ch
== ' ') || ((ch
>= 0x09) && (ch
<= 0x0d));
37 int CompareCaseInsensitive(const char *a
, const char *b
) {
40 char upperA
= MakeUpperCase(*a
);
41 char upperB
= MakeUpperCase(*b
);
43 return upperA
- upperB
;
48 // Either *a or *b is nul
52 int CompareNCaseInsensitive(const char *a
, const char *b
, size_t len
) {
53 while (*a
&& *b
&& len
) {
55 char upperA
= MakeUpperCase(*a
);
56 char upperB
= MakeUpperCase(*b
);
58 return upperA
- upperB
;
67 // Either *a or *b is nul
71 bool EqualCaseInsensitive(const char *a
, const char *b
) {
72 return 0 == CompareCaseInsensitive(a
, b
);
75 inline unsigned int HashString(const char *s
, size_t len
) {
87 for (int root
= 0; root
< hashRoots
; root
++)
96 void PropSet::Set(const char *key
, const char *val
, int lenKey
, int lenVal
) {
97 if (!*key
) // Empty keys are not supported
100 lenKey
= static_cast<int>(strlen(key
));
102 lenVal
= static_cast<int>(strlen(val
));
103 unsigned int hash
= HashString(key
, lenKey
);
104 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
105 if ((hash
== p
->hash
) &&
106 ((strlen(p
->key
) == static_cast<unsigned int>(lenKey
)) &&
107 (0 == strncmp(p
->key
, key
, lenKey
)))) {
108 // Replace current value
110 p
->val
= StringDup(val
, lenVal
);
115 Property
*pNew
= new Property
;
118 pNew
->key
= StringDup(key
, lenKey
);
119 pNew
->val
= StringDup(val
, lenVal
);
120 pNew
->next
= props
[hash
% hashRoots
];
121 props
[hash
% hashRoots
] = pNew
;
125 void PropSet::Set(const char *keyVal
) {
126 while (IsASpace(*keyVal
))
128 const char *endVal
= keyVal
;
129 while (*endVal
&& (*endVal
!= '\n'))
131 const char *eqAt
= strchr(keyVal
, '=');
133 Set(keyVal
, eqAt
+ 1, eqAt
-keyVal
, endVal
- eqAt
- 1);
134 } else if (*keyVal
) { // No '=' so assume '=1'
135 Set(keyVal
, "1", endVal
-keyVal
, 1);
139 void PropSet::SetMultiple(const char *s
) {
140 const char *eol
= strchr(s
, '\n');
144 eol
= strchr(s
, '\n');
149 SString
PropSet::Get(const char *key
) {
150 unsigned int hash
= HashString(key
, strlen(key
));
151 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
152 if ((hash
== p
->hash
) && (0 == strcmp(p
->key
, key
))) {
157 // Failed here, so try in base property set
158 return superPS
->Get(key
);
164 static bool IncludesVar(const char *value
, const char *key
) {
165 const char *var
= strstr(value
, "$(");
167 if (isprefix(var
+ 2, key
) && (var
[2 + strlen(key
)] == ')')) {
168 // Found $(key) which would lead to an infinite loop so exit
171 var
= strstr(var
+ 2, ")");
173 var
= strstr(var
+ 1, "$(");
178 SString
PropSet::GetExpanded(const char *key
) {
179 SString val
= Get(key
);
180 if (IncludesVar(val
.c_str(), key
))
182 return Expand(val
.c_str());
185 SString
PropSet::Expand(const char *withVars
) {
186 char *base
= StringDup(withVars
);
187 char *cpvar
= strstr(base
, "$(");
188 int maxExpands
= 1000; // Avoid infinite expansion of recursive definitions
189 while (cpvar
&& (maxExpands
> 0)) {
190 char *cpendvar
= strchr(cpvar
, ')');
192 int lenvar
= cpendvar
- cpvar
- 2; // Subtract the $()
193 char *var
= StringDup(cpvar
+ 2, lenvar
);
194 SString val
= GetExpanded(var
);
195 size_t newlenbase
= strlen(base
) + val
.length() - lenvar
;
196 char *newbase
= new char[newlenbase
];
197 strncpy(newbase
, base
, cpvar
- base
);
198 strcpy(newbase
+ (cpvar
- base
), val
.c_str());
199 strcpy(newbase
+ (cpvar
- base
) + val
.length(), cpendvar
+ 1);
204 cpvar
= strstr(base
, "$(");
212 int PropSet::GetInt(const char *key
, int defaultValue
) {
213 SString val
= GetExpanded(key
);
219 bool isprefix(const char *target
, const char *prefix
) {
220 while (*target
&& *prefix
) {
221 if (*target
!= *prefix
)
232 static bool IsSuffixCaseInsensitive(const char *target
, const char *suffix
) {
233 size_t lentarget
= strlen(target
);
234 size_t lensuffix
= strlen(suffix
);
235 if (lensuffix
> lentarget
)
237 for (int i
= static_cast<int>(lensuffix
) - 1; i
>= 0; i
--) {
238 if (MakeUpperCase(target
[i
+ lentarget
- lensuffix
]) !=
239 MakeUpperCase(suffix
[i
]))
245 SString
PropSet::GetWild(const char *keybase
, const char *filename
) {
246 for (int root
= 0; root
< hashRoots
; root
++) {
247 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
248 if (isprefix(p
->key
, keybase
)) {
249 char * orgkeyfile
= p
->key
+ strlen(keybase
);
250 char *keyfile
= NULL
;
252 if (strstr(orgkeyfile
, "$(") == orgkeyfile
) {
253 char *cpendvar
= strchr(orgkeyfile
, ')');
256 SString s
= GetExpanded(orgkeyfile
+ 2);
258 keyfile
= StringDup(s
.c_str());
261 char *keyptr
= keyfile
;
264 keyfile
= orgkeyfile
;
267 char *del
= strchr(keyfile
, ';');
269 del
= keyfile
+ strlen(keyfile
);
272 if (*keyfile
== '*') {
273 if (IsSuffixCaseInsensitive(filename
, keyfile
+ 1)) {
278 } else if (0 == strcmp(keyfile
, filename
)) {
290 if (0 == strcmp(p
->key
, keybase
)) {
297 // Failed here, so try in base property set
298 return superPS
->GetWild(keybase
, filename
);
304 // GetNewExpand does not use Expand as it has to use GetWild with the filename for each
305 // variable reference found.
306 SString
PropSet::GetNewExpand(const char *keybase
, const char *filename
) {
307 char *base
= StringDup(GetWild(keybase
, filename
).c_str());
308 char *cpvar
= strstr(base
, "$(");
309 int maxExpands
= 1000; // Avoid infinite expansion of recursive definitions
310 while (cpvar
&& (maxExpands
> 0)) {
311 char *cpendvar
= strchr(cpvar
, ')');
313 int lenvar
= cpendvar
- cpvar
- 2; // Subtract the $()
314 char *var
= StringDup(cpvar
+ 2, lenvar
);
315 SString val
= GetWild(var
, filename
);
316 size_t newlenbase
= strlen(base
) + val
.length() - lenvar
;
317 char *newbase
= new char[newlenbase
];
318 strncpy(newbase
, base
, cpvar
- base
);
319 strcpy(newbase
+ (cpvar
- base
), val
.c_str());
320 strcpy(newbase
+ (cpvar
- base
) + val
.length(), cpendvar
+ 1);
325 cpvar
= strstr(base
, "$(");
333 void PropSet::Clear() {
334 for (int root
= 0; root
< hashRoots
; root
++) {
335 Property
*p
= props
[root
];
337 Property
*pNext
= p
->next
;
350 char *PropSet::ToString() {
352 for (int r
= 0; r
< hashRoots
; r
++) {
353 for (Property
*p
= props
[r
]; p
; p
= p
->next
) {
354 len
+= strlen(p
->key
) + 1;
355 len
+= strlen(p
->val
) + 1;
359 len
= 1; // Return as empty string
360 char *ret
= new char [len
];
363 for (int root
= 0; root
< hashRoots
; root
++) {
364 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
379 * Initiate enumeration.
381 bool PropSet::GetFirst(char **key
, char **val
) {
382 for (int i
= 0; i
< hashRoots
; i
++) {
383 for (Property
*p
= props
[i
]; p
; p
= p
->next
) {
387 enumnext
= p
->next
; // GetNext will begin here ...
388 enumhash
= i
; // ... in this block
397 * Continue enumeration.
399 bool PropSet::GetNext(char ** key
, char ** val
) {
400 bool firstloop
= true;
402 // search begins where we left it : in enumhash block
403 for (int i
= enumhash
; i
< hashRoots
; i
++) {
405 enumnext
= props
[i
]; // Begin with first property in block
406 // else : begin where we left
409 for (Property
*p
= enumnext
; p
; p
= p
->next
) {
413 enumnext
= p
->next
; // for GetNext
423 * Creates an array that points into each word in the string and puts \0 terminators
426 static char **ArrayFromWordList(char *wordlist
, int *len
, bool onlyLineEnds
= false) {
429 // For rapid determination of whether a character is a separator, build
431 bool wordSeparator
[256];
432 for (int i
=0;i
<256; i
++) {
433 wordSeparator
[i
] = false;
435 wordSeparator
['\r'] = true;
436 wordSeparator
['\n'] = true;
438 wordSeparator
[' '] = true;
439 wordSeparator
['\t'] = true;
441 for (int j
= 0; wordlist
[j
]; j
++) {
442 int curr
= static_cast<unsigned char>(wordlist
[j
]);
443 if (!wordSeparator
[curr
] && wordSeparator
[prev
])
447 char **keywords
= new char *[words
+ 1];
451 size_t slen
= strlen(wordlist
);
452 for (size_t k
= 0; k
< slen
; k
++) {
453 if (!wordSeparator
[static_cast<unsigned char>(wordlist
[k
])]) {
455 keywords
[words
] = &wordlist
[k
];
463 keywords
[words
] = &wordlist
[slen
];
471 void WordList::Clear() {
475 delete []wordsNoCase
;
484 void WordList::Set(const char *s
) {
487 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
488 wordsNoCase
= new char * [len
+ 1];
489 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
492 char *WordList::Allocate(int size
) {
493 list
= new char[size
+ 1];
498 void WordList::SetFromAllocated() {
500 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
501 wordsNoCase
= new char * [len
+ 1];
502 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
505 int cmpString(const void *a1
, const void *a2
) {
506 // Can't work out the correct incantation to use modern casts here
507 return strcmp(*(char**)(a1
), *(char**)(a2
));
510 int cmpStringNoCase(const void *a1
, const void *a2
) {
511 // Can't work out the correct incantation to use modern casts here
512 return CompareCaseInsensitive(*(char**)(a1
), *(char**)(a2
));
515 static void SortWordList(char **words
, char **wordsNoCase
, unsigned int len
) {
516 qsort(reinterpret_cast<void*>(words
), len
, sizeof(*words
),
518 qsort(reinterpret_cast<void*>(wordsNoCase
), len
, sizeof(*wordsNoCase
),
522 bool WordList::InList(const char *s
) {
527 SortWordList(words
, wordsNoCase
, len
);
528 for (unsigned int k
= 0; k
< (sizeof(starts
) / sizeof(starts
[0])); k
++)
530 for (int l
= len
- 1; l
>= 0; l
--) {
531 unsigned char indexChar
= words
[l
][0];
532 starts
[indexChar
] = l
;
535 unsigned char firstChar
= s
[0];
536 int j
= starts
[firstChar
];
538 while (words
[j
][0] == firstChar
) {
539 if (s
[1] == words
[j
][1]) {
540 const char *a
= words
[j
] + 1;
541 const char *b
= s
+ 1;
542 while (*a
&& *a
== *b
) {
554 while (words
[j
][0] == '^') {
555 const char *a
= words
[j
] + 1;
557 while (*a
&& *a
== *b
) {
570 * Returns an element (complete) of the wordlist array which has
571 * the same beginning as the passed string.
572 * The length of the word to compare is passed too.
573 * Letter case can be ignored or preserved (default).
575 const char *WordList::GetNearestWord(const char *wordStart
, int searchLen
/*= -1*/, bool ignoreCase
/*= false*/, SString wordCharacters
/*='/0' */) {
576 int start
= 0; // lower bound of the api array block to search
577 int end
= len
- 1; // upper bound of the api array block to search
578 int pivot
; // index of api array element just being compared
579 int cond
; // comparison result (in the sense of strcmp() result)
580 const char *word
; // api array element just being compared
586 SortWordList(words
, wordsNoCase
, len
);
589 while (start
<= end
) { // binary searching loop
590 pivot
= (start
+ end
) >> 1;
591 word
= wordsNoCase
[pivot
];
592 cond
= CompareNCaseInsensitive(wordStart
, word
, searchLen
);
593 if (!cond
&& (!wordCharacters
.contains(word
[searchLen
])))
594 return word
; // result must not be freed with free()
600 } else { // preserve the letter case
601 while (start
<= end
) { // binary searching loop
602 pivot
= (start
+ end
) >> 1;
604 cond
= strncmp(wordStart
, word
, searchLen
);
605 if (!cond
&& (!wordCharacters
.contains(word
[searchLen
])))
606 return word
; // result must not be freed with free()
617 * Find the length of a 'word' which is actually an identifier in a string
618 * which looks like "identifier(..." or "identifier:" or "identifier" and where
619 * there may be extra spaces after the identifier that should not be
620 * counted in the length.
622 static unsigned int LengthWord(const char *word
, char otherSeparator
) {
623 // Find a '(', or ':'. If that fails go to the end of the string.
624 const char *endWord
= strchr(word
, '(');
626 endWord
= strchr(word
, ':');
627 if (!endWord
&& otherSeparator
)
628 endWord
= strchr(word
, otherSeparator
);
630 endWord
= word
+ strlen(word
);
631 // Last case always succeeds so endWord != 0
633 // Drop any space characters.
634 if (endWord
> word
) {
635 endWord
--; // Back from the '(', ':', or '\0'
636 // Move backwards over any spaces
637 while ((endWord
> word
) && (IsASpace(*endWord
))) {
641 return endWord
- word
;
645 * Returns elements (first words of them) of the wordlist array which have
646 * the same beginning as the passed string.
647 * The length of the word to compare is passed too.
648 * Letter case can be ignored or preserved (default).
649 * If there are more words meeting the condition they are returned all of
650 * them in the ascending order separated with spaces.
652 * NOTE: returned buffer has to be freed with delete[].
654 char *WordList::GetNearestWords(
655 const char *wordStart
,
656 int searchLen
/*= -1*/,
657 bool ignoreCase
/*= false*/,
658 char otherSeparator
/*= '\0'*/) {
659 int wordlen
; // length of the word part (before the '(' brace) of the api array element
661 wordsNear
.setsizegrowth(1000);
662 int start
= 0; // lower bound of the api array block to search
663 int end
= len
- 1; // upper bound of the api array block to search
664 int pivot
; // index of api array element just being compared
665 int cond
; // comparison result (in the sense of strcmp() result)
671 SortWordList(words
, wordsNoCase
, len
);
674 while (start
<= end
) { // Binary searching loop
675 pivot
= (start
+ end
) / 2;
676 cond
= CompareNCaseInsensitive(wordStart
, wordsNoCase
[pivot
], searchLen
);
679 while ((pivot
> start
) &&
680 (0 == CompareNCaseInsensitive(wordStart
,
681 wordsNoCase
[pivot
-1], searchLen
))) {
685 while ((pivot
<= end
) &&
686 (0 == CompareNCaseInsensitive(wordStart
,
687 wordsNoCase
[pivot
], searchLen
))) {
688 wordlen
= LengthWord(wordsNoCase
[pivot
], otherSeparator
) + 1;
689 wordsNear
.append(wordsNoCase
[pivot
], wordlen
, ' ');
692 return wordsNear
.detach();
693 } else if (cond
< 0) {
695 } else if (cond
> 0) {
699 } else { // Preserve the letter case
700 while (start
<= end
) { // Binary searching loop
701 pivot
= (start
+ end
) / 2;
702 cond
= strncmp(wordStart
, words
[pivot
], searchLen
);
705 while ((pivot
> start
) &&
706 (0 == strncmp(wordStart
,
707 words
[pivot
-1], searchLen
))) {
711 while ((pivot
<= end
) &&
712 (0 == strncmp(wordStart
,
713 words
[pivot
], searchLen
))) {
714 wordlen
= LengthWord(words
[pivot
], otherSeparator
) + 1;
715 wordsNear
.append(words
[pivot
], wordlen
, ' ');
718 return wordsNear
.detach();
719 } else if (cond
< 0) {
721 } else if (cond
> 0) {