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) {