]>
git.saurik.com Git - wxWidgets.git/blob - contrib/src/stc/scintilla/src/PropSet.cxx
4e875087964e940afbdb55e6305e0d0ac8efa25b
1 // SciTE - Scintilla based Text Editor
3 ** A Java style properties file module.
5 // Copyright 1998-2001 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'));
34 int CompareCaseInsensitive(const char *a
, const char *b
) {
37 if (IsLetter(*a
) && IsLetter(*b
)) {
38 char upperA
= MakeUpperCase(*a
);
39 char upperB
= MakeUpperCase(*b
);
41 return upperA
- upperB
;
50 // Either *a or *b is nul
54 int CompareNCaseInsensitive(const char *a
, const char *b
, int len
) {
55 while (*a
&& *b
&& len
) {
57 if (IsLetter(*a
) && IsLetter(*b
)) {
58 char upperA
= MakeUpperCase(*a
);
59 char upperB
= MakeUpperCase(*b
);
61 return upperA
- upperB
;
74 // Either *a or *b is nul
78 bool EqualCaseInsensitive(const char *a
, const char *b
) {
79 return 0 == CompareCaseInsensitive(a
, b
);
82 inline unsigned int HashString(const char *s
, int len
) {
94 for (int root
= 0; root
< hashRoots
; root
++)
103 void PropSet::Set(const char *key
, const char *val
, int lenKey
, int lenVal
) {
104 if (!*key
) // Empty keys are not supported
107 lenKey
= strlen(key
);
109 lenVal
= strlen(val
);
110 unsigned int hash
= HashString(key
, lenKey
);
111 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
112 if ((hash
== p
->hash
) &&
113 ((strlen(p
->key
) == static_cast<unsigned int>(lenKey
)) &&
114 (0 == strncmp(p
->key
, key
, lenKey
)))) {
115 // Replace current value
117 p
->val
= StringDup(val
, lenVal
);
122 Property
*pNew
= new Property
;
125 pNew
->key
= StringDup(key
, lenKey
);
126 pNew
->val
= StringDup(val
, lenVal
);
127 pNew
->next
= props
[hash
% hashRoots
];
128 props
[hash
% hashRoots
] = pNew
;
132 void PropSet::Set(const char *keyVal
) {
133 while (isspace(*keyVal
))
135 const char *endVal
= keyVal
;
136 while (*endVal
&& (*endVal
!= '\n'))
138 const char *eqAt
= strchr(keyVal
, '=');
140 Set(keyVal
, eqAt
+ 1, eqAt
-keyVal
, endVal
- eqAt
- 1);
141 } else if (*keyVal
) { // No '=' so assume '=1'
142 Set(keyVal
, "1", endVal
-keyVal
, 1);
146 void PropSet::SetMultiple(const char *s
) {
147 const char *eol
= strchr(s
, '\n');
151 eol
= strchr(s
, '\n');
156 SString
PropSet::Get(const char *key
) {
157 unsigned int hash
= HashString(key
, strlen(key
));
158 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
159 if ((hash
== p
->hash
) && (0 == strcmp(p
->key
, key
))) {
164 // Failed here, so try in base property set
165 return superPS
->Get(key
);
171 static bool IncludesVar(const char *value
, const char *key
) {
172 const char *var
= strstr(value
, "$(");
174 if (isprefix(var
+ 2, key
) && (var
[2 + strlen(key
)] == ')')) {
175 // Found $(key) which would lead to an infinite loop so exit
178 var
= strstr(var
+ 2, ")");
180 var
= strstr(var
+ 1, "$(");
185 SString
PropSet::GetExpanded(const char *key
) {
186 SString val
= Get(key
);
187 if (IncludesVar(val
.c_str(), key
))
189 return Expand(val
.c_str());
192 SString
PropSet::Expand(const char *withVars
) {
193 char *base
= StringDup(withVars
);
194 char *cpvar
= strstr(base
, "$(");
196 char *cpendvar
= strchr(cpvar
, ')');
198 int lenvar
= cpendvar
- cpvar
- 2; // Subtract the $()
199 char *var
= StringDup(cpvar
+ 2, lenvar
);
200 SString val
= GetExpanded(var
);
201 int newlenbase
= strlen(base
) + val
.length() - lenvar
;
202 char *newbase
= new char[newlenbase
];
203 strncpy(newbase
, base
, cpvar
- base
);
204 strcpy(newbase
+ (cpvar
- base
), val
.c_str());
205 strcpy(newbase
+ (cpvar
- base
) + val
.length(), cpendvar
+ 1);
210 cpvar
= strstr(base
, "$(");
217 int PropSet::GetInt(const char *key
, int defaultValue
) {
218 SString val
= GetExpanded(key
);
224 bool isprefix(const char *target
, const char *prefix
) {
225 while (*target
&& *prefix
) {
226 if (*target
!= *prefix
)
237 static bool IsSuffixCaseInsensitive(const char *target
, const char *suffix
) {
238 int lentarget
= strlen(target
);
239 int lensuffix
= strlen(suffix
);
240 if (lensuffix
> lentarget
)
242 for (int i
= lensuffix
- 1; i
>= 0; i
--) {
243 if (MakeUpperCase(target
[i
+ lentarget
- lensuffix
]) !=
244 MakeUpperCase(suffix
[i
]))
250 SString
PropSet::GetWild(const char *keybase
, const char *filename
) {
251 for (int root
= 0; root
< hashRoots
; root
++) {
252 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
253 if (isprefix(p
->key
, keybase
)) {
254 char * orgkeyfile
= p
->key
+ strlen(keybase
);
255 char *keyfile
= NULL
;
257 if (strstr(orgkeyfile
, "$(") == orgkeyfile
) {
258 char *cpendvar
= strchr(orgkeyfile
, ')');
261 SString s
= GetExpanded(orgkeyfile
+ 2);
263 keyfile
= StringDup(s
.c_str());
266 char *keyptr
= keyfile
;
269 keyfile
= orgkeyfile
;
272 char *del
= strchr(keyfile
, ';');
274 del
= keyfile
+ strlen(keyfile
);
277 if (*keyfile
== '*') {
278 if (IsSuffixCaseInsensitive(filename
, keyfile
+ 1)) {
283 } else if (0 == strcmp(keyfile
, filename
)) {
295 if (0 == strcmp(p
->key
, keybase
)) {
302 // Failed here, so try in base property set
303 return superPS
->GetWild(keybase
, filename
);
309 // GetNewExpand does not use Expand as it has to use GetWild with the filename for each
310 // variable reference found.
311 SString
PropSet::GetNewExpand(const char *keybase
, const char *filename
) {
312 char *base
= StringDup(GetWild(keybase
, filename
).c_str());
313 char *cpvar
= strstr(base
, "$(");
315 char *cpendvar
= strchr(cpvar
, ')');
317 int lenvar
= cpendvar
- cpvar
- 2; // Subtract the $()
318 char *var
= StringDup(cpvar
+ 2, lenvar
);
319 SString val
= GetWild(var
, filename
);
320 int newlenbase
= strlen(base
) + val
.length() - lenvar
;
321 char *newbase
= new char[newlenbase
];
322 strncpy(newbase
, base
, cpvar
- base
);
323 strcpy(newbase
+ (cpvar
- base
), val
.c_str());
324 strcpy(newbase
+ (cpvar
- base
) + val
.length(), cpendvar
+ 1);
329 cpvar
= strstr(base
, "$(");
336 void PropSet::Clear() {
337 for (int root
= 0; root
< hashRoots
; root
++) {
338 Property
*p
= props
[root
];
340 Property
*pNext
= p
->next
;
353 char *PropSet::ToString() {
355 for (int r
= 0; r
< hashRoots
; r
++) {
356 for (Property
*p
= props
[r
]; p
; p
= p
->next
) {
357 len
+= strlen(p
->key
) + 1;
358 len
+= strlen(p
->val
) + 1;
362 len
= 1; // Return as empty string
363 char *ret
= new char [len
];
366 for (int root
= 0; root
< hashRoots
; root
++) {
367 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
382 * Initiate enumeration.
384 bool PropSet::GetFirst(char **key
, char **val
) {
385 for (int i
= 0; i
< hashRoots
; i
++) {
386 for (Property
*p
= props
[i
]; p
; p
= p
->next
) {
390 enumnext
= p
->next
; // GetNext will begin here ...
391 enumhash
= i
; // ... in this block
400 * Continue enumeration.
402 bool PropSet::GetNext(char ** key
, char ** val
) {
403 bool firstloop
= true;
405 // search begins where we left it : in enumhash block
406 for (int i
= enumhash
; i
< hashRoots
; i
++) {
408 enumnext
= props
[i
]; // Begin with first property in block
409 // else : begin where we left
412 for (Property
*p
= enumnext
; p
; p
= p
->next
) {
416 enumnext
= p
->next
; // for GetNext
425 static bool iswordsep(char ch
, bool onlyLineEnds
) {
430 return ch
== '\r' || ch
== '\n';
434 * Creates an array that points into each word in the string and puts \0 terminators
437 static char **ArrayFromWordList(char *wordlist
, int *len
, bool onlyLineEnds
= false) {
440 for (int j
= 0; wordlist
[j
]; j
++) {
441 if (!iswordsep(wordlist
[j
], onlyLineEnds
) && iswordsep(prev
, onlyLineEnds
))
445 char **keywords
= new char * [words
+ 1];
449 int slen
= strlen(wordlist
);
450 for (int k
= 0; k
< slen
; k
++) {
451 if (!iswordsep(wordlist
[k
], onlyLineEnds
)) {
453 keywords
[words
] = &wordlist
[k
];
461 keywords
[words
] = &wordlist
[slen
];
469 void WordList::Clear() {
473 delete []wordsNoCase
;
482 void WordList::Set(const char *s
) {
485 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
486 wordsNoCase
= new char * [len
+ 1];
487 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
490 char *WordList::Allocate(int size
) {
491 list
= new char[size
+ 1];
496 void WordList::SetFromAllocated() {
498 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
499 wordsNoCase
= new char * [len
+ 1];
500 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
503 int cmpString(const void *a1
, const void *a2
) {
504 // Can't work out the correct incantation to use modern casts here
505 return strcmp(*(char**)(a1
), *(char**)(a2
));
508 int cmpStringNoCase(const void *a1
, const void *a2
) {
509 // Can't work out the correct incantation to use modern casts here
510 return CompareCaseInsensitive(*(char**)(a1
), *(char**)(a2
));
513 static void SortWordList(char **words
, char **wordsNoCase
, unsigned int len
) {
514 qsort(reinterpret_cast<void*>(words
), len
, sizeof(*words
),
516 qsort(reinterpret_cast<void*>(wordsNoCase
), len
, sizeof(*wordsNoCase
),
520 bool WordList::InList(const char *s
) {
525 SortWordList(words
, wordsNoCase
, len
);
526 for (unsigned int k
= 0; k
< (sizeof(starts
) / sizeof(starts
[0])); k
++)
528 for (int l
= len
- 1; l
>= 0; l
--) {
529 unsigned char indexChar
= words
[l
][0];
530 starts
[indexChar
] = l
;
533 unsigned char firstChar
= s
[0];
534 int j
= starts
[firstChar
];
536 while (words
[j
][0] == firstChar
) {
537 if (s
[1] == words
[j
][1]) {
538 const char *a
= words
[j
] + 1;
539 const char *b
= s
+ 1;
540 while (*a
&& *a
== *b
) {
552 while (words
[j
][0] == '^') {
553 const char *a
= words
[j
] + 1;
555 while (*a
&& *a
== *b
) {
568 * Returns an element (complete) of the wordlist array which has
569 * the same beginning as the passed string.
570 * The length of the word to compare is passed too.
571 * Letter case can be ignored or preserved (default).
573 const char *WordList::GetNearestWord(const char *wordStart
, int searchLen
/*= -1*/, bool ignoreCase
/*= false*/) {
574 int start
= 0; // lower bound of the api array block to search
575 int end
= len
- 1; // upper bound of the api array block to search
576 int pivot
; // index of api array element just being compared
577 int cond
; // comparison result (in the sense of strcmp() result)
578 const char *word
; // api array element just being compared
584 SortWordList(words
, wordsNoCase
, len
);
587 while (start
<= end
) { // binary searching loop
588 pivot
= (start
+ end
) >> 1;
589 word
= wordsNoCase
[pivot
];
590 cond
= CompareNCaseInsensitive(wordStart
, word
, searchLen
);
591 if (!cond
&& nonFuncChar(word
[searchLen
])) // maybe there should be a "non-word character" test here?
592 return word
; // result must not be freed with free()
598 } else { // preserve the letter case
599 while (start
<= end
) { // binary searching loop
600 pivot
= (start
+ end
) >> 1;
602 cond
= strncmp(wordStart
, word
, searchLen
);
603 if (!cond
&& nonFuncChar(word
[searchLen
])) // maybe there should be a "non-word character" test here?
604 return word
; // result must not be freed with free()
615 * Find the length of a 'word' which is actually an identifier in a string
616 * which looks like "identifier(..." or "identifier:" or "identifier" and where
617 * there may be extra spaces after the identifier that should not be
618 * counted in the length.
620 static unsigned int LengthWord(const char *word
, char otherSeparator
) {
621 // Find a '(', or ':'. If that fails go to the end of the string.
622 const char *endWord
= strchr(word
, '(');
624 endWord
= strchr(word
, ':');
625 if (!endWord
&& otherSeparator
)
626 endWord
= strchr(word
, otherSeparator
);
628 endWord
= word
+ strlen(word
);
629 // Last case always succeeds so endWord != 0
631 // Drop any space characters.
632 if (endWord
> word
) {
633 endWord
--; // Back from the '(', ':', or '\0'
634 // Move backwards over any spaces
635 while ((endWord
> word
) && (isspace(*endWord
))) {
639 return endWord
- word
;
643 * Returns elements (first words of them) of the wordlist array which have
644 * the same beginning as the passed string.
645 * The length of the word to compare is passed too.
646 * Letter case can be ignored or preserved (default).
647 * If there are more words meeting the condition they are returned all of
648 * them in the ascending order separated with spaces.
650 * NOTE: returned buffer has to be freed with delete[].
652 char *WordList::GetNearestWords(
653 const char *wordStart
,
654 int searchLen
/*= -1*/,
655 bool ignoreCase
/*= false*/,
656 char otherSeparator
/*= '\0'*/) {
657 int wordlen
; // length of the word part (before the '(' brace) of the api array element
659 wordsNear
.setsizegrowth(1000);
660 int start
= 0; // lower bound of the api array block to search
661 int end
= len
- 1; // upper bound of the api array block to search
662 int pivot
; // index of api array element just being compared
663 int cond
; // comparison result (in the sense of strcmp() result)
669 SortWordList(words
, wordsNoCase
, len
);
672 while (start
<= end
) { // Binary searching loop
673 pivot
= (start
+ end
) / 2;
674 cond
= CompareNCaseInsensitive(wordStart
, wordsNoCase
[pivot
], searchLen
);
677 while ((pivot
> start
) &&
678 (0 == CompareNCaseInsensitive(wordStart
,
679 wordsNoCase
[pivot
-1], searchLen
))) {
683 while ((pivot
<= end
) &&
684 (0 == CompareNCaseInsensitive(wordStart
,
685 wordsNoCase
[pivot
], searchLen
))) {
686 wordlen
= LengthWord(wordsNoCase
[pivot
], otherSeparator
) + 1;
687 wordsNear
.append(wordsNoCase
[pivot
], wordlen
, ' ');
690 return wordsNear
.detach();
691 } else if (cond
< 0) {
693 } else if (cond
> 0) {
697 } else { // Preserve the letter case
698 while (start
<= end
) { // Binary searching loop
699 pivot
= (start
+ end
) / 2;
700 cond
= strncmp(wordStart
, words
[pivot
], searchLen
);
703 while ((pivot
> start
) &&
704 (0 == strncmp(wordStart
,
705 words
[pivot
-1], searchLen
))) {
709 while ((pivot
<= end
) &&
710 (0 == strncmp(wordStart
,
711 words
[pivot
], searchLen
))) {
712 wordlen
= LengthWord(words
[pivot
], otherSeparator
) + 1;
713 wordsNear
.append(words
[pivot
], wordlen
, ' ');
716 return wordsNear
.detach();
717 } else if (cond
< 0) {
719 } else if (cond
> 0) {