]>
git.saurik.com Git - wxWidgets.git/blob - contrib/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
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
);
74 // Since the CaseInsensitive functions declared in SString
75 // are implemented here, I will for now put the non-inline
76 // implementations of the SString members here as well, so
77 // that I can quickly see what effect this has.
79 SString::SString(int i
) : sizeGrowth(sizeGrowthDefault
) {
81 sprintf(number
, "%0d", i
);
82 s
= StringAllocate(number
);
83 sSize
= sLen
= (s
) ? strlen(s
) : 0;
86 SString::SString(double d
, int precision
) : sizeGrowth(sizeGrowthDefault
) {
88 sprintf(number
, "%.*f", precision
, d
);
89 s
= StringAllocate(number
);
90 sSize
= sLen
= (s
) ? strlen(s
) : 0;
93 bool SString::grow(lenpos_t lenNew
) {
94 while (sizeGrowth
* 6 < lenNew
) {
97 char *sNew
= new char[lenNew
+ sizeGrowth
+ 1];
100 memcpy(sNew
, s
, sLen
);
105 sSize
= lenNew
+ sizeGrowth
;
110 SString
&SString::assign(const char *sOther
, lenpos_t sSize_
) {
113 } else if (sSize_
== measure_length
) {
114 sSize_
= strlen(sOther
);
116 if (sSize
> 0 && sSize_
<= sSize
) { // Does not allocate new buffer if the current is big enough
118 memcpy(s
, sOther
, sSize_
);
124 s
= StringAllocate(sOther
, sSize_
);
126 sSize
= sSize_
; // Allow buffer bigger than real string, thus providing space to grow
135 bool SString::operator==(const SString
&sOther
) const {
136 if ((s
== 0) && (sOther
.s
== 0))
138 if ((s
== 0) || (sOther
.s
== 0))
140 return strcmp(s
, sOther
.s
) == 0;
143 bool SString::operator==(const char *sOther
) const {
144 if ((s
== 0) && (sOther
== 0))
146 if ((s
== 0) || (sOther
== 0))
148 return strcmp(s
, sOther
) == 0;
151 SString
SString::substr(lenpos_t subPos
, lenpos_t subLen
) const {
152 if (subPos
>= sLen
) {
153 return SString(); // return a null string if start index is out of bounds
155 if ((subLen
== measure_length
) || (subPos
+ subLen
> sLen
)) {
156 subLen
= sLen
- subPos
; // can't substr past end of source string
158 return SString(s
, subPos
, subPos
+ subLen
);
161 SString
&SString::lowercase(lenpos_t subPos
, lenpos_t subLen
) {
162 if ((subLen
== measure_length
) || (subPos
+ subLen
> sLen
)) {
163 subLen
= sLen
- subPos
; // don't apply past end of string
165 for (lenpos_t i
= subPos
; i
< subPos
+ subLen
; i
++) {
166 if (s
[i
] < 'A' || s
[i
] > 'Z')
169 s
[i
] = static_cast<char>(s
[i
] - 'A' + 'a');
174 SString
&SString::uppercase(lenpos_t subPos
, lenpos_t subLen
) {
175 if ((subLen
== measure_length
) || (subPos
+ subLen
> sLen
)) {
176 subLen
= sLen
- subPos
; // don't apply past end of string
178 for (lenpos_t i
= subPos
; i
< subPos
+ subLen
; i
++) {
179 if (s
[i
] < 'a' || s
[i
] > 'z')
182 s
[i
] = static_cast<char>(s
[i
] - 'a' + 'A');
187 SString
&SString::append(const char *sOther
, lenpos_t sLenOther
, char sep
) {
191 if (sLenOther
== measure_length
) {
192 sLenOther
= strlen(sOther
);
195 if (sLen
&& sep
) { // Only add a separator if not empty
198 lenpos_t lenNew
= sLen
+ sLenOther
+ lenSep
;
199 // Conservative about growing the buffer: don't do it, unless really needed
200 if ((lenNew
< sSize
) || (grow(lenNew
))) {
205 memcpy(&s
[sLen
], sOther
, sLenOther
);
212 SString
&SString::insert(lenpos_t pos
, const char *sOther
, lenpos_t sLenOther
) {
213 if (!sOther
|| pos
> sLen
) {
216 if (sLenOther
== measure_length
) {
217 sLenOther
= strlen(sOther
);
219 lenpos_t lenNew
= sLen
+ sLenOther
;
220 // Conservative about growing the buffer: don't do it, unless really needed
221 if ((lenNew
< sSize
) || grow(lenNew
)) {
222 lenpos_t moveChars
= sLen
- pos
+ 1;
223 for (lenpos_t i
= moveChars
; i
> 0; i
--) {
224 s
[pos
+ sLenOther
+ i
- 1] = s
[pos
+ i
- 1];
226 memcpy(s
+ pos
, sOther
, sLenOther
);
233 * Remove @a len characters from the @a pos position, included.
234 * Characters at pos + len and beyond replace characters at pos.
235 * If @a len is 0, or greater than the length of the string
236 * starting at @a pos, the string is just truncated at @a pos.
238 void SString::remove(lenpos_t pos
, lenpos_t len
) {
242 if (len
< 1 || pos
+ len
>= sLen
) {
246 for (lenpos_t i
= pos
; i
< sLen
- len
+ 1; i
++) {
253 bool SString::startswith(const char *prefix
) {
254 lenpos_t lenPrefix
= strlen(prefix
);
255 if (lenPrefix
> sLen
) {
258 return strncmp(s
, prefix
, lenPrefix
) == 0;
261 bool SString::endswith(const char *suffix
) {
262 lenpos_t lenSuffix
= strlen(suffix
);
263 if (lenSuffix
> sLen
) {
266 return strncmp(s
+ sLen
- lenSuffix
, suffix
, lenSuffix
) == 0;
269 int SString::search(const char *sFind
, lenpos_t start
) const {
271 const char *sFound
= strstr(s
+ start
, sFind
);
279 int SString::substitute(char chFind
, char chReplace
) {
283 t
= strchr(t
, chFind
);
293 int SString::substitute(const char *sFind
, const char *sReplace
) {
295 lenpos_t lenFind
= strlen(sFind
);
296 lenpos_t lenReplace
= strlen(sReplace
);
297 int posFound
= search(sFind
);
298 while (posFound
>= 0) {
299 remove(posFound
, lenFind
);
300 insert(posFound
, sReplace
, lenReplace
);
301 posFound
= search(sFind
, posFound
+ lenReplace
);
307 char *SContainer::StringAllocate(lenpos_t len
) {
308 if (len
!= measure_length
) {
309 return new char[len
+ 1];
315 char *SContainer::StringAllocate(const char *s
, lenpos_t len
) {
319 if (len
== measure_length
) {
322 char *sNew
= new char[len
+ 1];
324 memcpy(sNew
, s
, len
);
330 // End SString functions
332 bool PropSet::caseSensitiveFilenames
= false;
336 for (int root
= 0; root
< hashRoots
; root
++)
340 PropSet::~PropSet() {
345 void PropSet::Set(const char *key
, const char *val
, int lenKey
, int lenVal
) {
346 if (!*key
) // Empty keys are not supported
349 lenKey
= static_cast<int>(strlen(key
));
351 lenVal
= static_cast<int>(strlen(val
));
352 unsigned int hash
= HashString(key
, lenKey
);
353 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
354 if ((hash
== p
->hash
) &&
355 ((strlen(p
->key
) == static_cast<unsigned int>(lenKey
)) &&
356 (0 == strncmp(p
->key
, key
, lenKey
)))) {
357 // Replace current value
359 p
->val
= StringDup(val
, lenVal
);
364 Property
*pNew
= new Property
;
367 pNew
->key
= StringDup(key
, lenKey
);
368 pNew
->val
= StringDup(val
, lenVal
);
369 pNew
->next
= props
[hash
% hashRoots
];
370 props
[hash
% hashRoots
] = pNew
;
374 void PropSet::Set(const char *keyVal
) {
375 while (IsASpace(*keyVal
))
377 const char *endVal
= keyVal
;
378 while (*endVal
&& (*endVal
!= '\n'))
380 const char *eqAt
= strchr(keyVal
, '=');
382 Set(keyVal
, eqAt
+ 1, eqAt
-keyVal
, endVal
- eqAt
- 1);
383 } else if (*keyVal
) { // No '=' so assume '=1'
384 Set(keyVal
, "1", endVal
-keyVal
, 1);
388 void PropSet::Unset(const char *key
, int lenKey
) {
389 if (!*key
) // Empty keys are not supported
392 lenKey
= static_cast<int>(strlen(key
));
393 unsigned int hash
= HashString(key
, lenKey
);
394 Property
*pPrev
= NULL
;
395 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
396 if ((hash
== p
->hash
) &&
397 ((strlen(p
->key
) == static_cast<unsigned int>(lenKey
)) &&
398 (0 == strncmp(p
->key
, key
, lenKey
)))) {
400 pPrev
->next
= p
->next
;
402 props
[hash
% hashRoots
] = p
->next
;
404 enumnext
= p
->next
; // Not that anyone should mix enum and Set / Unset.
415 void PropSet::SetMultiple(const char *s
) {
416 const char *eol
= strchr(s
, '\n');
420 eol
= strchr(s
, '\n');
425 SString
PropSet::Get(const char *key
) {
426 unsigned int hash
= HashString(key
, strlen(key
));
427 for (Property
*p
= props
[hash
% hashRoots
]; p
; p
= p
->next
) {
428 if ((hash
== p
->hash
) && (0 == strcmp(p
->key
, key
))) {
433 // Failed here, so try in base property set
434 return superPS
->Get(key
);
440 bool PropSet::IncludesVar(const char *value
, const char *key
) {
441 const char *var
= strstr(value
, "$(");
443 if (isprefix(var
+ 2, key
) && (var
[2 + strlen(key
)] == ')')) {
444 // Found $(key) which would lead to an infinite loop so exit
447 var
= strstr(var
+ 2, ")");
449 var
= strstr(var
+ 1, "$(");
455 // There is some inconsistency between GetExpanded("foo") and Expand("$(foo)").
456 // A solution is to keep a stack of variables that have been expanded, so that
457 // recursive expansions can be skipped. For now I'll just use the C++ stack
458 // for that, through a recursive function and a simple chain of pointers.
461 VarChain(const char*var_
=NULL
, const VarChain
*link_
=NULL
): var(var_
), link(link_
) {}
463 bool contains(const char *testVar
) const {
464 return (var
&& (0 == strcmp(var
, testVar
)))
465 || (link
&& link
->contains(testVar
));
469 const VarChain
*link
;
472 static int ExpandAllInPlace(PropSet
&props
, SString
&withVars
, int maxExpands
, const VarChain
&blankVars
= VarChain()) {
473 int varStart
= withVars
.search("$(");
474 while ((varStart
>= 0) && (maxExpands
> 0)) {
475 int varEnd
= withVars
.search(")", varStart
+2);
480 // For consistency, when we see '$(ab$(cde))', expand the inner variable first,
481 // regardless whether there is actually a degenerate variable named 'ab$(cde'.
482 int innerVarStart
= withVars
.search("$(", varStart
+2);
483 while ((innerVarStart
> varStart
) && (innerVarStart
< varEnd
)) {
484 varStart
= innerVarStart
;
485 innerVarStart
= withVars
.search("$(", varStart
+2);
488 SString
var(withVars
.c_str(), varStart
+ 2, varEnd
);
489 SString val
= props
.Get(var
.c_str());
491 if (blankVars
.contains(var
.c_str())) {
492 val
.clear(); // treat blankVar as an empty string (e.g. to block self-reference)
495 if (--maxExpands
>= 0) {
496 maxExpands
= ExpandAllInPlace(props
, val
, maxExpands
, VarChain(var
.c_str(), &blankVars
));
499 withVars
.remove(varStart
, varEnd
-varStart
+1);
500 withVars
.insert(varStart
, val
.c_str(), val
.length());
502 varStart
= withVars
.search("$(");
509 SString
PropSet::GetExpanded(const char *key
) {
510 SString val
= Get(key
);
511 ExpandAllInPlace(*this, val
, 100, VarChain(key
));
515 SString
PropSet::Expand(const char *withVars
, int maxExpands
) {
516 SString val
= withVars
;
517 ExpandAllInPlace(*this, val
, maxExpands
);
521 int PropSet::GetInt(const char *key
, int defaultValue
) {
522 SString val
= GetExpanded(key
);
528 bool isprefix(const char *target
, const char *prefix
) {
529 while (*target
&& *prefix
) {
530 if (*target
!= *prefix
)
541 static bool IsSuffix(const char *target
, const char *suffix
, bool caseSensitive
) {
542 size_t lentarget
= strlen(target
);
543 size_t lensuffix
= strlen(suffix
);
544 if (lensuffix
> lentarget
)
547 for (int i
= static_cast<int>(lensuffix
) - 1; i
>= 0; i
--) {
548 if (target
[i
+ lentarget
- lensuffix
] != suffix
[i
])
552 for (int i
= static_cast<int>(lensuffix
) - 1; i
>= 0; i
--) {
553 if (MakeUpperCase(target
[i
+ lentarget
- lensuffix
]) !=
554 MakeUpperCase(suffix
[i
]))
561 SString
PropSet::GetWild(const char *keybase
, const char *filename
) {
562 for (int root
= 0; root
< hashRoots
; root
++) {
563 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
564 if (isprefix(p
->key
, keybase
)) {
565 char * orgkeyfile
= p
->key
+ strlen(keybase
);
566 char *keyfile
= NULL
;
568 if (strstr(orgkeyfile
, "$(") == orgkeyfile
) {
569 char *cpendvar
= strchr(orgkeyfile
, ')');
572 SString s
= GetExpanded(orgkeyfile
+ 2);
574 keyfile
= StringDup(s
.c_str());
577 char *keyptr
= keyfile
;
580 keyfile
= orgkeyfile
;
583 char *del
= strchr(keyfile
, ';');
585 del
= keyfile
+ strlen(keyfile
);
588 if (*keyfile
== '*') {
589 if (IsSuffix(filename
, keyfile
+ 1, caseSensitiveFilenames
)) {
594 } else if (0 == strcmp(keyfile
, filename
)) {
606 if (0 == strcmp(p
->key
, keybase
)) {
613 // Failed here, so try in base property set
614 return superPS
->GetWild(keybase
, filename
);
622 // GetNewExpand does not use Expand as it has to use GetWild with the filename for each
623 // variable reference found.
624 SString
PropSet::GetNewExpand(const char *keybase
, const char *filename
) {
625 char *base
= StringDup(GetWild(keybase
, filename
).c_str());
626 char *cpvar
= strstr(base
, "$(");
627 int maxExpands
= 1000; // Avoid infinite expansion of recursive definitions
628 while (cpvar
&& (maxExpands
> 0)) {
629 char *cpendvar
= strchr(cpvar
, ')');
631 int lenvar
= cpendvar
- cpvar
- 2; // Subtract the $()
632 char *var
= StringDup(cpvar
+ 2, lenvar
);
633 SString val
= GetWild(var
, filename
);
634 if (0 == strcmp(var
, keybase
))
635 val
.clear(); // Self-references evaluate to empty string
636 size_t newlenbase
= strlen(base
) + val
.length() - lenvar
;
637 char *newbase
= new char[newlenbase
];
638 strncpy(newbase
, base
, cpvar
- base
);
639 strcpy(newbase
+ (cpvar
- base
), val
.c_str());
640 strcpy(newbase
+ (cpvar
- base
) + val
.length(), cpendvar
+ 1);
645 cpvar
= strstr(base
, "$(");
653 void PropSet::Clear() {
654 for (int root
= 0; root
< hashRoots
; root
++) {
655 Property
*p
= props
[root
];
657 Property
*pNext
= p
->next
;
670 char *PropSet::ToString() {
672 for (int r
= 0; r
< hashRoots
; r
++) {
673 for (Property
*p
= props
[r
]; p
; p
= p
->next
) {
674 len
+= strlen(p
->key
) + 1;
675 len
+= strlen(p
->val
) + 1;
679 len
= 1; // Return as empty string
680 char *ret
= new char [len
];
683 for (int root
= 0; root
< hashRoots
; root
++) {
684 for (Property
*p
= props
[root
]; p
; p
= p
->next
) {
699 * Initiate enumeration.
701 bool PropSet::GetFirst(char **key
, char **val
) {
702 for (int i
= 0; i
< hashRoots
; i
++) {
703 for (Property
*p
= props
[i
]; p
; p
= p
->next
) {
707 enumnext
= p
->next
; // GetNext will begin here ...
708 enumhash
= i
; // ... in this block
717 * Continue enumeration.
719 bool PropSet::GetNext(char ** key
, char ** val
) {
720 bool firstloop
= true;
722 // search begins where we left it : in enumhash block
723 for (int i
= enumhash
; i
< hashRoots
; i
++) {
725 enumnext
= props
[i
]; // Begin with first property in block
726 // else : begin where we left
729 for (Property
*p
= enumnext
; p
; p
= p
->next
) {
733 enumnext
= p
->next
; // for GetNext
743 * Creates an array that points into each word in the string and puts \0 terminators
746 static char **ArrayFromWordList(char *wordlist
, int *len
, bool onlyLineEnds
= false) {
749 // For rapid determination of whether a character is a separator, build
751 bool wordSeparator
[256];
752 for (int i
=0;i
<256; i
++) {
753 wordSeparator
[i
] = false;
755 wordSeparator
['\r'] = true;
756 wordSeparator
['\n'] = true;
758 wordSeparator
[' '] = true;
759 wordSeparator
['\t'] = true;
761 for (int j
= 0; wordlist
[j
]; j
++) {
762 int curr
= static_cast<unsigned char>(wordlist
[j
]);
763 if (!wordSeparator
[curr
] && wordSeparator
[prev
])
767 char **keywords
= new char *[words
+ 1];
771 size_t slen
= strlen(wordlist
);
772 for (size_t k
= 0; k
< slen
; k
++) {
773 if (!wordSeparator
[static_cast<unsigned char>(wordlist
[k
])]) {
775 keywords
[words
] = &wordlist
[k
];
783 keywords
[words
] = &wordlist
[slen
];
791 void WordList::Clear() {
795 delete []wordsNoCase
;
802 sortedNoCase
= false;
805 void WordList::Set(const char *s
) {
808 sortedNoCase
= false;
809 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
810 wordsNoCase
= new char * [len
+ 1];
811 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
814 char *WordList::Allocate(int size
) {
815 list
= new char[size
+ 1];
820 void WordList::SetFromAllocated() {
822 sortedNoCase
= false;
823 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
824 wordsNoCase
= new char * [len
+ 1];
825 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
828 int cmpString(const void *a1
, const void *a2
) {
829 // Can't work out the correct incantation to use modern casts here
830 return strcmp(*(char**)(a1
), *(char**)(a2
));
833 int cmpStringNoCase(const void *a1
, const void *a2
) {
834 // Can't work out the correct incantation to use modern casts here
835 return CompareCaseInsensitive(*(char**)(a1
), *(char**)(a2
));
838 static void SortWordList(char **words
, unsigned int len
) {
839 qsort(reinterpret_cast<void*>(words
), len
, sizeof(*words
),
843 static void SortWordListNoCase(char **wordsNoCase
, unsigned int len
) {
844 qsort(reinterpret_cast<void*>(wordsNoCase
), len
, sizeof(*wordsNoCase
),
848 bool WordList::InList(const char *s
) {
853 SortWordList(words
, len
);
854 for (unsigned int k
= 0; k
< (sizeof(starts
) / sizeof(starts
[0])); k
++)
856 for (int l
= len
- 1; l
>= 0; l
--) {
857 unsigned char indexChar
= words
[l
][0];
858 starts
[indexChar
] = l
;
861 unsigned char firstChar
= s
[0];
862 int j
= starts
[firstChar
];
864 while (words
[j
][0] == firstChar
) {
865 if (s
[1] == words
[j
][1]) {
866 const char *a
= words
[j
] + 1;
867 const char *b
= s
+ 1;
868 while (*a
&& *a
== *b
) {
880 while (words
[j
][0] == '^') {
881 const char *a
= words
[j
] + 1;
883 while (*a
&& *a
== *b
) {
895 /** similar to InList, but word s can be a substring of keyword.
896 * eg. the keyword define is defined as def~ine. This means the word must start
897 * with def to be a keyword, but also defi, defin and define are valid.
898 * The marker is ~ in this case.
900 bool WordList::InListAbbreviated(const char *s
, const char marker
) {
905 SortWordList(words
, len
);
906 for (unsigned int k
= 0; k
< (sizeof(starts
) / sizeof(starts
[0])); k
++)
908 for (int l
= len
- 1; l
>= 0; l
--) {
909 unsigned char indexChar
= words
[l
][0];
910 starts
[indexChar
] = l
;
913 unsigned char firstChar
= s
[0];
914 int j
= starts
[firstChar
];
916 while (words
[j
][0] == firstChar
) {
917 bool isSubword
= false;
919 if (words
[j
][1] == marker
) {
923 if (s
[1] == words
[j
][start
]) {
924 const char *a
= words
[j
] + start
;
925 const char *b
= s
+ 1;
926 while (*a
&& *a
== *b
) {
934 if ((!*a
|| isSubword
) && !*b
)
942 while (words
[j
][0] == '^') {
943 const char *a
= words
[j
] + 1;
945 while (*a
&& *a
== *b
) {
958 * Returns an element (complete) of the wordlist array which has
959 * the same beginning as the passed string.
960 * The length of the word to compare is passed too.
961 * Letter case can be ignored or preserved (default).
963 const char *WordList::GetNearestWord(const char *wordStart
, int searchLen
, bool ignoreCase
/*= false*/, SString wordCharacters
/*='/0' */, int wordIndex
/*= -1 */) {
964 int start
= 0; // lower bound of the api array block to search
965 int end
= len
- 1; // upper bound of the api array block to search
966 int pivot
; // index of api array element just being compared
967 int cond
; // comparison result (in the sense of strcmp() result)
968 const char *word
; // api array element just being compared
975 SortWordListNoCase(wordsNoCase
, len
);
977 while (start
<= end
) { // binary searching loop
978 pivot
= (start
+ end
) >> 1;
979 word
= wordsNoCase
[pivot
];
980 cond
= CompareNCaseInsensitive(wordStart
, word
, searchLen
);
984 while (start
> 0 && !CompareNCaseInsensitive(wordStart
, wordsNoCase
[start
-1], searchLen
)) {
989 while (end
< len
-1 && !CompareNCaseInsensitive(wordStart
, wordsNoCase
[end
+1], searchLen
)) {
993 // Finds first word in a series of equal words
994 for (pivot
= start
; pivot
<= end
; pivot
++) {
995 word
= wordsNoCase
[pivot
];
996 if (!wordCharacters
.contains(word
[searchLen
])) {
997 if (wordIndex
<= 0) // Checks if a specific index was requested
998 return word
; // result must not be freed with free()
1009 } else { // preserve the letter case
1012 SortWordList(words
, len
);
1014 while (start
<= end
) { // binary searching loop
1015 pivot
= (start
+ end
) >> 1;
1016 word
= words
[pivot
];
1017 cond
= strncmp(wordStart
, word
, searchLen
);
1021 while (start
> 0 && !strncmp(wordStart
, words
[start
-1], searchLen
)) {
1026 while (end
< len
-1 && !strncmp(wordStart
, words
[end
+1], searchLen
)) {
1030 // Finds first word in a series of equal words
1032 while (pivot
<= end
) {
1033 word
= words
[pivot
];
1034 if (!wordCharacters
.contains(word
[searchLen
])) {
1035 if (wordIndex
<= 0) // Checks if a specific index was requested
1036 return word
; // result must not be freed with free()
1053 * Find the length of a 'word' which is actually an identifier in a string
1054 * which looks like "identifier(..." or "identifier" and where
1055 * there may be extra spaces after the identifier that should not be
1056 * counted in the length.
1058 static unsigned int LengthWord(const char *word
, char otherSeparator
) {
1059 // Find a '('. If that fails go to the end of the string.
1060 const char *endWord
= strchr(word
, '(');
1061 if (!endWord
&& otherSeparator
)
1062 endWord
= strchr(word
, otherSeparator
);
1064 endWord
= word
+ strlen(word
);
1065 // Last case always succeeds so endWord != 0
1067 // Drop any space characters.
1068 if (endWord
> word
) {
1069 endWord
--; // Back from the '(', otherSeparator, or '\0'
1070 // Move backwards over any spaces
1071 while ((endWord
> word
) && (IsASpace(*endWord
))) {
1075 return endWord
- word
;
1079 * Returns elements (first words of them) of the wordlist array which have
1080 * the same beginning as the passed string.
1081 * The length of the word to compare is passed too.
1082 * Letter case can be ignored or preserved (default).
1083 * If there are more words meeting the condition they are returned all of
1084 * them in the ascending order separated with spaces.
1086 * NOTE: returned buffer has to be freed with delete[].
1088 char *WordList::GetNearestWords(
1089 const char *wordStart
,
1091 bool ignoreCase
/*= false*/,
1092 char otherSeparator
/*= '\0'*/,
1093 bool exactLen
/*=false*/) {
1094 unsigned int wordlen
; // length of the word part (before the '(' brace) of the api array element
1096 wordsNear
.setsizegrowth(1000);
1097 int start
= 0; // lower bound of the api array block to search
1098 int end
= len
- 1; // upper bound of the api array block to search
1099 int pivot
; // index of api array element just being compared
1100 int cond
; // comparison result (in the sense of strcmp() result)
1105 if (!sortedNoCase
) {
1106 sortedNoCase
= true;
1107 SortWordListNoCase(wordsNoCase
, len
);
1109 while (start
<= end
) { // Binary searching loop
1110 pivot
= (start
+ end
) / 2;
1111 cond
= CompareNCaseInsensitive(wordStart
, wordsNoCase
[pivot
], searchLen
);
1114 while ((pivot
> start
) &&
1115 (0 == CompareNCaseInsensitive(wordStart
,
1116 wordsNoCase
[pivot
-1], searchLen
))) {
1120 while ((pivot
<= end
) &&
1121 (0 == CompareNCaseInsensitive(wordStart
,
1122 wordsNoCase
[pivot
], searchLen
))) {
1123 wordlen
= LengthWord(wordsNoCase
[pivot
], otherSeparator
) + 1;
1125 if (exactLen
&& wordlen
!= LengthWord(wordStart
, otherSeparator
) + 1)
1127 wordsNear
.append(wordsNoCase
[pivot
-1], wordlen
, ' ');
1129 return wordsNear
.detach();
1130 } else if (cond
< 0) {
1132 } else if (cond
> 0) {
1136 } else { // Preserve the letter case
1139 SortWordList(words
, len
);
1141 while (start
<= end
) { // Binary searching loop
1142 pivot
= (start
+ end
) / 2;
1143 cond
= strncmp(wordStart
, words
[pivot
], searchLen
);
1146 while ((pivot
> start
) &&
1147 (0 == strncmp(wordStart
,
1148 words
[pivot
-1], searchLen
))) {
1152 while ((pivot
<= end
) &&
1153 (0 == strncmp(wordStart
,
1154 words
[pivot
], searchLen
))) {
1155 wordlen
= LengthWord(words
[pivot
], otherSeparator
) + 1;
1157 if (exactLen
&& wordlen
!= LengthWord(wordStart
, otherSeparator
) + 1)
1159 wordsNear
.append(words
[pivot
-1], wordlen
, ' ');
1161 return wordsNear
.detach();
1162 } else if (cond
< 0) {
1164 } else if (cond
> 0) {