]>
git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/PropSet.cxx
021a6572739172bb6868a3c8518be713356bf297
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
18 // The comparison and case changing functions here assume ASCII
19 // or extended ASCII such as the normal Windows code page.
21 static inline char MakeUpperCase(char ch
) {
22 if (ch
< 'a' || ch
> 'z')
25 return static_cast<char>(ch
- 'a' + 'A');
28 static inline bool IsLetter(char ch
) {
29 return ((ch
>= 'a' && ch
<= 'z') || (ch
>= 'A' && ch
<= 'Z'));
32 inline bool IsASpace(unsigned int ch
) {
33 return (ch
== ' ') || ((ch
>= 0x09) && (ch
<= 0x0d));
36 int CompareCaseInsensitive(const char *a
, const char *b
) {
39 char upperA
= MakeUpperCase(*a
);
40 char upperB
= MakeUpperCase(*b
);
42 return upperA
- upperB
;
47 // Either *a or *b is nul
51 int CompareNCaseInsensitive(const char *a
, const char *b
, size_t len
) {
52 while (*a
&& *b
&& len
) {
54 char upperA
= MakeUpperCase(*a
);
55 char upperB
= MakeUpperCase(*b
);
57 return upperA
- upperB
;
66 // Either *a or *b is nul
70 bool EqualCaseInsensitive(const char *a
, const char *b
) {
71 return 0 == CompareCaseInsensitive(a
, b
);
76 for (int root
= 0; root
< hashRoots
; root
++)
85 void PropSet::Set(const char *key
, const char *val
, int lenKey
, int lenVal
) {
86 if (!*key
) // Empty keys are not supported
89 lenKey
= static_cast<int>(strlen(key
));
91 lenVal
= static_cast<int>(strlen(val
));
92 unsigned int hash
= HashString(key
, lenKey
);
93 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
94 if ((hash
== p
->hash
) &&
95 ((strlen(p
->key
) == static_cast<unsigned int>(lenKey
)) &&
96 (0 == strncmp(p
->key
, key
, lenKey
)))) {
97 // Replace current value
99 p
->val
= StringDup(val
, lenVal
);
104 Property
*pNew
= new Property
;
107 pNew
->key
= StringDup(key
, lenKey
);
108 pNew
->val
= StringDup(val
, lenVal
);
109 pNew
->next
= props
[hash
% hashRoots
];
110 props
[hash
% hashRoots
] = pNew
;
114 void PropSet::Set(const char *keyVal
) {
115 while (IsASpace(*keyVal
))
117 const char *endVal
= keyVal
;
118 while (*endVal
&& (*endVal
!= '\n'))
120 const char *eqAt
= strchr(keyVal
, '=');
122 Set(keyVal
, eqAt
+ 1, eqAt
-keyVal
, endVal
- eqAt
- 1);
123 } else if (*keyVal
) { // No '=' so assume '=1'
124 Set(keyVal
, "1", endVal
-keyVal
, 1);
128 void PropSet::SetMultiple(const char *s
) {
129 const char *eol
= strchr(s
, '\n');
133 eol
= strchr(s
, '\n');
138 SString
PropSet::Get(const char *key
) {
139 unsigned int hash
= HashString(key
, strlen(key
));
140 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
141 if ((hash
== p
->hash
) && (0 == strcmp(p
->key
, key
))) {
146 // Failed here, so try in base property set
147 return superPS
->Get(key
);
153 bool PropSet::IncludesVar(const char *value
, const char *key
) {
154 const char *var
= strstr(value
, "$(");
156 if (isprefix(var
+ 2, key
) && (var
[2 + strlen(key
)] == ')')) {
157 // Found $(key) which would lead to an infinite loop so exit
160 var
= strstr(var
+ 2, ")");
162 var
= strstr(var
+ 1, "$(");
167 SString
PropSet::GetExpanded(const char *key
) {
168 SString val
= Get(key
);
169 if (IncludesVar(val
.c_str(), key
))
171 return Expand(val
.c_str());
174 SString
PropSet::Expand(const char *withVars
, int maxExpands
) {
175 char *base
= StringDup(withVars
);
176 char *cpvar
= strstr(base
, "$(");
177 while (cpvar
&& (maxExpands
> 0)) {
178 char *cpendvar
= strchr(cpvar
, ')');
181 int lenvar
= cpendvar
- cpvar
- 2; // Subtract the $()
182 char *var
= StringDup(cpvar
+ 2, lenvar
);
183 SString val
= Get(var
);
184 if (IncludesVar(val
.c_str(), var
))
186 size_t newlenbase
= strlen(base
) + val
.length() - lenvar
;
187 char *newbase
= new char[newlenbase
];
188 strncpy(newbase
, base
, cpvar
- base
);
189 strcpy(newbase
+ (cpvar
- base
), val
.c_str());
190 strcpy(newbase
+ (cpvar
- base
) + val
.length(), cpendvar
+ 1);
194 cpvar
= strstr(base
, "$(");
202 int PropSet::GetInt(const char *key
, int defaultValue
) {
203 SString val
= GetExpanded(key
);
209 bool isprefix(const char *target
, const char *prefix
) {
210 while (*target
&& *prefix
) {
211 if (*target
!= *prefix
)
222 static bool IsSuffixCaseInsensitive(const char *target
, const char *suffix
) {
223 size_t lentarget
= strlen(target
);
224 size_t lensuffix
= strlen(suffix
);
225 if (lensuffix
> lentarget
)
227 for (int i
= static_cast<int>(lensuffix
) - 1; i
>= 0; i
--) {
228 if (MakeUpperCase(target
[i
+ lentarget
- lensuffix
]) !=
229 MakeUpperCase(suffix
[i
]))
235 SString
PropSet::GetWild(const char *keybase
, const char *filename
) {
236 for (int root
= 0; root
< hashRoots
; root
++) {
237 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
238 if (isprefix(p
->key
, keybase
)) {
239 char * orgkeyfile
= p
->key
+ strlen(keybase
);
240 char *keyfile
= NULL
;
242 if (strstr(orgkeyfile
, "$(") == orgkeyfile
) {
243 char *cpendvar
= strchr(orgkeyfile
, ')');
246 SString s
= GetExpanded(orgkeyfile
+ 2);
248 keyfile
= StringDup(s
.c_str());
251 char *keyptr
= keyfile
;
254 keyfile
= orgkeyfile
;
257 char *del
= strchr(keyfile
, ';');
259 del
= keyfile
+ strlen(keyfile
);
262 if (*keyfile
== '*') {
263 if (IsSuffixCaseInsensitive(filename
, keyfile
+ 1)) {
268 } else if (0 == strcmp(keyfile
, filename
)) {
280 if (0 == strcmp(p
->key
, keybase
)) {
287 // Failed here, so try in base property set
288 return superPS
->GetWild(keybase
, filename
);
294 // GetNewExpand does not use Expand as it has to use GetWild with the filename for each
295 // variable reference found.
296 SString
PropSet::GetNewExpand(const char *keybase
, const char *filename
) {
297 char *base
= StringDup(GetWild(keybase
, filename
).c_str());
298 char *cpvar
= strstr(base
, "$(");
299 int maxExpands
= 1000; // Avoid infinite expansion of recursive definitions
300 while (cpvar
&& (maxExpands
> 0)) {
301 char *cpendvar
= strchr(cpvar
, ')');
303 int lenvar
= cpendvar
- cpvar
- 2; // Subtract the $()
304 char *var
= StringDup(cpvar
+ 2, lenvar
);
305 SString val
= GetWild(var
, filename
);
306 size_t newlenbase
= strlen(base
) + val
.length() - lenvar
;
307 char *newbase
= new char[newlenbase
];
308 strncpy(newbase
, base
, cpvar
- base
);
309 strcpy(newbase
+ (cpvar
- base
), val
.c_str());
310 strcpy(newbase
+ (cpvar
- base
) + val
.length(), cpendvar
+ 1);
315 cpvar
= strstr(base
, "$(");
323 void PropSet::Clear() {
324 for (int root
= 0; root
< hashRoots
; root
++) {
325 Property
*p
= props
[root
];
327 Property
*pNext
= p
->next
;
340 char *PropSet::ToString() {
342 for (int r
= 0; r
< hashRoots
; r
++) {
343 for (Property
*p
= props
[r
]; p
; p
= p
->next
) {
344 len
+= strlen(p
->key
) + 1;
345 len
+= strlen(p
->val
) + 1;
349 len
= 1; // Return as empty string
350 char *ret
= new char [len
];
353 for (int root
= 0; root
< hashRoots
; root
++) {
354 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
369 * Initiate enumeration.
371 bool PropSet::GetFirst(char **key
, char **val
) {
372 for (int i
= 0; i
< hashRoots
; i
++) {
373 for (Property
*p
= props
[i
]; p
; p
= p
->next
) {
377 enumnext
= p
->next
; // GetNext will begin here ...
378 enumhash
= i
; // ... in this block
387 * Continue enumeration.
389 bool PropSet::GetNext(char ** key
, char ** val
) {
390 bool firstloop
= true;
392 // search begins where we left it : in enumhash block
393 for (int i
= enumhash
; i
< hashRoots
; i
++) {
395 enumnext
= props
[i
]; // Begin with first property in block
396 // else : begin where we left
399 for (Property
*p
= enumnext
; p
; p
= p
->next
) {
403 enumnext
= p
->next
; // for GetNext
413 * Creates an array that points into each word in the string and puts \0 terminators
416 static char **ArrayFromWordList(char *wordlist
, int *len
, bool onlyLineEnds
= false) {
419 // For rapid determination of whether a character is a separator, build
421 bool wordSeparator
[256];
422 for (int i
=0;i
<256; i
++) {
423 wordSeparator
[i
] = false;
425 wordSeparator
['\r'] = true;
426 wordSeparator
['\n'] = true;
428 wordSeparator
[' '] = true;
429 wordSeparator
['\t'] = true;
431 for (int j
= 0; wordlist
[j
]; j
++) {
432 int curr
= static_cast<unsigned char>(wordlist
[j
]);
433 if (!wordSeparator
[curr
] && wordSeparator
[prev
])
437 char **keywords
= new char *[words
+ 1];
441 size_t slen
= strlen(wordlist
);
442 for (size_t k
= 0; k
< slen
; k
++) {
443 if (!wordSeparator
[static_cast<unsigned char>(wordlist
[k
])]) {
445 keywords
[words
] = &wordlist
[k
];
453 keywords
[words
] = &wordlist
[slen
];
461 void WordList::Clear() {
465 delete []wordsNoCase
;
474 void WordList::Set(const char *s
) {
477 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
478 wordsNoCase
= new char * [len
+ 1];
479 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
482 char *WordList::Allocate(int size
) {
483 list
= new char[size
+ 1];
488 void WordList::SetFromAllocated() {
490 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
491 wordsNoCase
= new char * [len
+ 1];
492 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
495 int cmpString(const void *a1
, const void *a2
) {
496 // Can't work out the correct incantation to use modern casts here
497 return strcmp(*(char**)(a1
), *(char**)(a2
));
500 int cmpStringNoCase(const void *a1
, const void *a2
) {
501 // Can't work out the correct incantation to use modern casts here
502 return CompareCaseInsensitive(*(char**)(a1
), *(char**)(a2
));
505 static void SortWordList(char **words
, char **wordsNoCase
, unsigned int len
) {
506 qsort(reinterpret_cast<void*>(words
), len
, sizeof(*words
),
508 qsort(reinterpret_cast<void*>(wordsNoCase
), len
, sizeof(*wordsNoCase
),
512 bool WordList::InList(const char *s
) {
517 SortWordList(words
, wordsNoCase
, len
);
518 for (unsigned int k
= 0; k
< (sizeof(starts
) / sizeof(starts
[0])); k
++)
520 for (int l
= len
- 1; l
>= 0; l
--) {
521 unsigned char indexChar
= words
[l
][0];
522 starts
[indexChar
] = l
;
525 unsigned char firstChar
= s
[0];
526 int j
= starts
[firstChar
];
528 while (words
[j
][0] == firstChar
) {
529 if (s
[1] == words
[j
][1]) {
530 const char *a
= words
[j
] + 1;
531 const char *b
= s
+ 1;
532 while (*a
&& *a
== *b
) {
544 while (words
[j
][0] == '^') {
545 const char *a
= words
[j
] + 1;
547 while (*a
&& *a
== *b
) {
560 * Returns an element (complete) of the wordlist array which has
561 * the same beginning as the passed string.
562 * The length of the word to compare is passed too.
563 * Letter case can be ignored or preserved (default).
565 const char *WordList::GetNearestWord(const char *wordStart
, int searchLen
/*= -1*/, bool ignoreCase
/*= false*/, SString wordCharacters
/*='/0' */) {
566 int start
= 0; // lower bound of the api array block to search
567 int end
= len
- 1; // upper bound of the api array block to search
568 int pivot
; // index of api array element just being compared
569 int cond
; // comparison result (in the sense of strcmp() result)
570 const char *word
; // api array element just being compared
576 SortWordList(words
, wordsNoCase
, len
);
579 while (start
<= end
) { // binary searching loop
580 pivot
= (start
+ end
) >> 1;
581 word
= wordsNoCase
[pivot
];
582 cond
= CompareNCaseInsensitive(wordStart
, word
, searchLen
);
583 if (!cond
&& (!wordCharacters
.contains(word
[searchLen
])))
584 return word
; // result must not be freed with free()
590 } else { // preserve the letter case
591 while (start
<= end
) { // binary searching loop
592 pivot
= (start
+ end
) >> 1;
594 cond
= strncmp(wordStart
, word
, searchLen
);
595 if (!cond
&& (!wordCharacters
.contains(word
[searchLen
])))
596 return word
; // result must not be freed with free()
607 * Find the length of a 'word' which is actually an identifier in a string
608 * which looks like "identifier(..." or "identifier:" or "identifier" and where
609 * there may be extra spaces after the identifier that should not be
610 * counted in the length.
612 static unsigned int LengthWord(const char *word
, char otherSeparator
) {
613 // Find a '(', or ':'. If that fails go to the end of the string.
614 const char *endWord
= strchr(word
, '(');
616 endWord
= strchr(word
, ':');
617 if (!endWord
&& otherSeparator
)
618 endWord
= strchr(word
, otherSeparator
);
620 endWord
= word
+ strlen(word
);
621 // Last case always succeeds so endWord != 0
623 // Drop any space characters.
624 if (endWord
> word
) {
625 endWord
--; // Back from the '(', ':', or '\0'
626 // Move backwards over any spaces
627 while ((endWord
> word
) && (IsASpace(*endWord
))) {
631 return endWord
- word
;
635 * Returns elements (first words of them) of the wordlist array which have
636 * the same beginning as the passed string.
637 * The length of the word to compare is passed too.
638 * Letter case can be ignored or preserved (default).
639 * If there are more words meeting the condition they are returned all of
640 * them in the ascending order separated with spaces.
642 * NOTE: returned buffer has to be freed with delete[].
644 char *WordList::GetNearestWords(
645 const char *wordStart
,
646 int searchLen
/*= -1*/,
647 bool ignoreCase
/*= false*/,
648 char otherSeparator
/*= '\0'*/) {
649 int wordlen
; // length of the word part (before the '(' brace) of the api array element
651 wordsNear
.setsizegrowth(1000);
652 int start
= 0; // lower bound of the api array block to search
653 int end
= len
- 1; // upper bound of the api array block to search
654 int pivot
; // index of api array element just being compared
655 int cond
; // comparison result (in the sense of strcmp() result)
661 SortWordList(words
, wordsNoCase
, len
);
664 while (start
<= end
) { // Binary searching loop
665 pivot
= (start
+ end
) / 2;
666 cond
= CompareNCaseInsensitive(wordStart
, wordsNoCase
[pivot
], searchLen
);
669 while ((pivot
> start
) &&
670 (0 == CompareNCaseInsensitive(wordStart
,
671 wordsNoCase
[pivot
-1], searchLen
))) {
675 while ((pivot
<= end
) &&
676 (0 == CompareNCaseInsensitive(wordStart
,
677 wordsNoCase
[pivot
], searchLen
))) {
678 wordlen
= LengthWord(wordsNoCase
[pivot
], otherSeparator
) + 1;
679 wordsNear
.append(wordsNoCase
[pivot
], wordlen
, ' ');
682 return wordsNear
.detach();
683 } else if (cond
< 0) {
685 } else if (cond
> 0) {
689 } else { // Preserve the letter case
690 while (start
<= end
) { // Binary searching loop
691 pivot
= (start
+ end
) / 2;
692 cond
= strncmp(wordStart
, words
[pivot
], searchLen
);
695 while ((pivot
> start
) &&
696 (0 == strncmp(wordStart
,
697 words
[pivot
-1], searchLen
))) {
701 while ((pivot
<= end
) &&
702 (0 == strncmp(wordStart
,
703 words
[pivot
], searchLen
))) {
704 wordlen
= LengthWord(words
[pivot
], otherSeparator
) + 1;
705 wordsNear
.append(words
[pivot
], wordlen
, ' ');
708 return wordsNear
.detach();
709 } else if (cond
< 0) {
711 } else if (cond
> 0) {