OSDN Git Service

Compiler warnings removal Numerous tweaks to remove compiler warnings where solution...
[tortoisegit/TortoiseGitJp.git] / ext / scintilla / src / PropSet.cxx
1 // SciTE - Scintilla based Text Editor\r
2 /** @file PropSet.cxx\r
3  ** A Java style properties file module.\r
4  **/\r
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>\r
6 // The License.txt file describes the conditions under which this software may be distributed.\r
7 \r
8 // Maintain a dictionary of properties\r
9 \r
10 #include <stdlib.h>\r
11 #include <string.h>\r
12 #include <stdio.h>\r
13 \r
14 #include "Platform.h"\r
15 \r
16 #include "PropSet.h"\r
17 \r
18 #ifdef SCI_NAMESPACE\r
19 using namespace Scintilla;\r
20 #endif\r
21 \r
22 // The comparison and case changing functions here assume ASCII\r
23 // or extended ASCII such as the normal Windows code page.\r
24 \r
25 static inline char MakeUpperCase(char ch) {\r
26         if (ch < 'a' || ch > 'z')\r
27                 return ch;\r
28         else\r
29                 return static_cast<char>(ch - 'a' + 'A');\r
30 }\r
31 \r
32 static inline bool IsLetter(char ch) {\r
33         return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'));\r
34 }\r
35 \r
36 inline bool IsASpace(unsigned int ch) {\r
37     return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));\r
38 }\r
39 \r
40 int CompareCaseInsensitive(const char *a, const char *b) {\r
41         while (*a && *b) {\r
42                 if (*a != *b) {\r
43                         char upperA = MakeUpperCase(*a);\r
44                         char upperB = MakeUpperCase(*b);\r
45                         if (upperA != upperB)\r
46                                 return upperA - upperB;\r
47                 }\r
48                 a++;\r
49                 b++;\r
50         }\r
51         // Either *a or *b is nul\r
52         return *a - *b;\r
53 }\r
54 \r
55 int CompareNCaseInsensitive(const char *a, const char *b, size_t len) {\r
56         while (*a && *b && len) {\r
57                 if (*a != *b) {\r
58                         char upperA = MakeUpperCase(*a);\r
59                         char upperB = MakeUpperCase(*b);\r
60                         if (upperA != upperB)\r
61                                 return upperA - upperB;\r
62                 }\r
63                 a++;\r
64                 b++;\r
65                 len--;\r
66         }\r
67         if (len == 0)\r
68                 return 0;\r
69         else\r
70                 // Either *a or *b is nul\r
71                 return *a - *b;\r
72 }\r
73 \r
74 bool EqualCaseInsensitive(const char *a, const char *b) {\r
75         return 0 == CompareCaseInsensitive(a, b);\r
76 }\r
77 \r
78 // Since the CaseInsensitive functions declared in SString\r
79 // are implemented here, I will for now put the non-inline\r
80 // implementations of the SString members here as well, so\r
81 // that I can quickly see what effect this has.\r
82 \r
83 SString::SString(int i) : sizeGrowth(sizeGrowthDefault) {\r
84         char number[32];\r
85         sprintf(number, "%0d", i);\r
86         s = StringAllocate(number);\r
87         sSize = sLen = (s) ? strlen(s) : 0;\r
88 }\r
89 \r
90 SString::SString(double d, int precision) : sizeGrowth(sizeGrowthDefault) {\r
91         char number[32];\r
92         sprintf(number, "%.*f", precision, d);\r
93         s = StringAllocate(number);\r
94         sSize = sLen = (s) ? strlen(s) : 0;\r
95 }\r
96 \r
97 bool SString::grow(lenpos_t lenNew) {\r
98         while (sizeGrowth * 6 < lenNew) {\r
99                 sizeGrowth *= 2;\r
100         }\r
101         char *sNew = new char[lenNew + sizeGrowth + 1];\r
102         if (sNew) {\r
103                 if (s) {\r
104                         memcpy(sNew, s, sLen);\r
105                         delete []s;\r
106                 }\r
107                 s = sNew;\r
108                 s[sLen] = '\0';\r
109                 sSize = lenNew + sizeGrowth;\r
110         }\r
111         return sNew != 0;\r
112 }\r
113 \r
114 SString &SString::assign(const char *sOther, lenpos_t sSize_) {\r
115         if (!sOther) {\r
116                 sSize_ = 0;\r
117         } else if (sSize_ == measure_length) {\r
118                 sSize_ = strlen(sOther);\r
119         }\r
120         if (sSize > 0 && sSize_ <= sSize) {     // Does not allocate new buffer if the current is big enough\r
121                 if (s && sSize_) {\r
122                         memcpy(s, sOther, sSize_);\r
123                 }\r
124                 s[sSize_] = '\0';\r
125                 sLen = sSize_;\r
126         } else {\r
127                 delete []s;\r
128                 s = StringAllocate(sOther, sSize_);\r
129                 if (s) {\r
130                         sSize = sSize_; // Allow buffer bigger than real string, thus providing space to grow\r
131                         sLen = sSize_;\r
132                 } else {\r
133                         sSize = sLen = 0;\r
134                 }\r
135         }\r
136         return *this;\r
137 }\r
138 \r
139 bool SString::operator==(const SString &sOther) const {\r
140         if ((s == 0) && (sOther.s == 0))\r
141                 return true;\r
142         if ((s == 0) || (sOther.s == 0))\r
143                 return false;\r
144         return strcmp(s, sOther.s) == 0;\r
145 }\r
146 \r
147 bool SString::operator==(const char *sOther) const {\r
148         if ((s == 0) && (sOther == 0))\r
149                 return true;\r
150         if ((s == 0) || (sOther == 0))\r
151                 return false;\r
152         return strcmp(s, sOther) == 0;\r
153 }\r
154 \r
155 SString SString::substr(lenpos_t subPos, lenpos_t subLen) const {\r
156         if (subPos >= sLen) {\r
157                 return SString();                                       // return a null string if start index is out of bounds\r
158         }\r
159         if ((subLen == measure_length) || (subPos + subLen > sLen)) {\r
160                 subLen = sLen - subPos;         // can't substr past end of source string\r
161         }\r
162         return SString(s, subPos, subPos + subLen);\r
163 }\r
164 \r
165 SString &SString::lowercase(lenpos_t subPos, lenpos_t subLen) {\r
166         if ((subLen == measure_length) || (subPos + subLen > sLen)) {\r
167                 subLen = sLen - subPos;         // don't apply past end of string\r
168         }\r
169         for (lenpos_t i = subPos; i < subPos + subLen; i++) {\r
170                 if (s[i] < 'A' || s[i] > 'Z')\r
171                         continue;\r
172                 else\r
173                         s[i] = static_cast<char>(s[i] - 'A' + 'a');\r
174         }\r
175         return *this;\r
176 }\r
177 \r
178 SString &SString::uppercase(lenpos_t subPos, lenpos_t subLen) {\r
179         if ((subLen == measure_length) || (subPos + subLen > sLen)) {\r
180                 subLen = sLen - subPos;         // don't apply past end of string\r
181         }\r
182         for (lenpos_t i = subPos; i < subPos + subLen; i++) {\r
183                 if (s[i] < 'a' || s[i] > 'z')\r
184                         continue;\r
185                 else\r
186                         s[i] = static_cast<char>(s[i] - 'a' + 'A');\r
187         }\r
188         return *this;\r
189 }\r
190 \r
191 SString &SString::append(const char *sOther, lenpos_t sLenOther, char sep) {\r
192         if (!sOther) {\r
193                 return *this;\r
194         }\r
195         if (sLenOther == measure_length) {\r
196                 sLenOther = strlen(sOther);\r
197         }\r
198         int lenSep = 0;\r
199         if (sLen && sep) {      // Only add a separator if not empty\r
200                 lenSep = 1;\r
201         }\r
202         lenpos_t lenNew = sLen + sLenOther + lenSep;\r
203         // Conservative about growing the buffer: don't do it, unless really needed\r
204         if ((lenNew < sSize) || (grow(lenNew))) {\r
205                 if (lenSep) {\r
206                         s[sLen] = sep;\r
207                         sLen++;\r
208                 }\r
209                 memcpy(&s[sLen], sOther, sLenOther);\r
210                 sLen += sLenOther;\r
211                 s[sLen] = '\0';\r
212         }\r
213         return *this;\r
214 }\r
215 \r
216 SString &SString::insert(lenpos_t pos, const char *sOther, lenpos_t sLenOther) {\r
217         if (!sOther || pos > sLen) {\r
218                 return *this;\r
219         }\r
220         if (sLenOther == measure_length) {\r
221                 sLenOther = strlen(sOther);\r
222         }\r
223         lenpos_t lenNew = sLen + sLenOther;\r
224         // Conservative about growing the buffer: don't do it, unless really needed\r
225         if ((lenNew < sSize) || grow(lenNew)) {\r
226                 lenpos_t moveChars = sLen - pos + 1;\r
227                 for (lenpos_t i = moveChars; i > 0; i--) {\r
228                         s[pos + sLenOther + i - 1] = s[pos + i - 1];\r
229                 }\r
230                 memcpy(s + pos, sOther, sLenOther);\r
231                 sLen = lenNew;\r
232         }\r
233         return *this;\r
234 }\r
235 \r
236 /**\r
237  * Remove @a len characters from the @a pos position, included.\r
238  * Characters at pos + len and beyond replace characters at pos.\r
239  * If @a len is 0, or greater than the length of the string\r
240  * starting at @a pos, the string is just truncated at @a pos.\r
241  */\r
242 void SString::remove(lenpos_t pos, lenpos_t len) {\r
243         if (pos >= sLen) {\r
244                 return;\r
245         }\r
246         if (len < 1 || pos + len >= sLen) {\r
247                 s[pos] = '\0';\r
248                 sLen = pos;\r
249         } else {\r
250                 for (lenpos_t i = pos; i < sLen - len + 1; i++) {\r
251                         s[i] = s[i+len];\r
252                 }\r
253                 sLen -= len;\r
254         }\r
255 }\r
256 \r
257 bool SString::startswith(const char *prefix) {\r
258         lenpos_t lenPrefix = strlen(prefix);\r
259         if (lenPrefix > sLen) {\r
260                 return false;\r
261         }\r
262         return strncmp(s, prefix, lenPrefix) == 0;\r
263 }\r
264 \r
265 bool SString::endswith(const char *suffix) {\r
266         lenpos_t lenSuffix = strlen(suffix);\r
267         if (lenSuffix > sLen) {\r
268                 return false;\r
269         }\r
270         return strncmp(s + sLen - lenSuffix, suffix, lenSuffix) == 0;\r
271 }\r
272 \r
273 int SString::search(const char *sFind, lenpos_t start) const {\r
274         if (start < sLen) {\r
275                 const char *sFound = strstr(s + start, sFind);\r
276                 if (sFound) {\r
277                         return sFound - s;\r
278                 }\r
279         }\r
280         return -1;\r
281 }\r
282 \r
283 int SString::substitute(char chFind, char chReplace) {\r
284         int c = 0;\r
285         char *t = s;\r
286         while (t) {\r
287                 t = strchr(t, chFind);\r
288                 if (t) {\r
289                         *t = chReplace;\r
290                         t++;\r
291                         c++;\r
292                 }\r
293         }\r
294         return c;\r
295 }\r
296 \r
297 int SString::substitute(const char *sFind, const char *sReplace) {\r
298         int c = 0;\r
299         lenpos_t lenFind = strlen(sFind);\r
300         lenpos_t lenReplace = strlen(sReplace);\r
301         int posFound = search(sFind);\r
302         while (posFound >= 0) {\r
303                 remove(posFound, lenFind);\r
304                 insert(posFound, sReplace, lenReplace);\r
305                 posFound = search(sFind, posFound + lenReplace);\r
306                 c++;\r
307         }\r
308         return c;\r
309 }\r
310 \r
311 char *SContainer::StringAllocate(lenpos_t len) {\r
312         if (len != measure_length) {\r
313                 return new char[len + 1];\r
314         } else {\r
315                 return 0;\r
316         }\r
317 }\r
318 \r
319 char *SContainer::StringAllocate(const char *s, lenpos_t len) {\r
320         if (s == 0) {\r
321                 return 0;\r
322         }\r
323         if (len == measure_length) {\r
324                 len = strlen(s);\r
325         }\r
326         char *sNew = new char[len + 1];\r
327         if (sNew) {\r
328                 memcpy(sNew, s, len);\r
329                 sNew[len] = '\0';\r
330         }\r
331         return sNew;\r
332 }\r
333 \r
334 // End SString functions\r
335 \r
336 PropSet::PropSet() {\r
337         superPS = 0;\r
338         for (int root = 0; root < hashRoots; root++)\r
339                 props[root] = 0;\r
340 }\r
341 \r
342 PropSet::~PropSet() {\r
343         superPS = 0;\r
344         Clear();\r
345 }\r
346 \r
347 void PropSet::Set(const char *key, const char *val, int lenKey, int lenVal) {\r
348         if (!*key)      // Empty keys are not supported\r
349                 return;\r
350         if (lenKey == -1)\r
351                 lenKey = static_cast<int>(strlen(key));\r
352         if (lenVal == -1)\r
353                 lenVal = static_cast<int>(strlen(val));\r
354         unsigned int hash = HashString(key, lenKey);\r
355         for (Property *p = props[hash % hashRoots]; p; p = p->next) {\r
356                 if ((hash == p->hash) &&\r
357                         ((strlen(p->key) == static_cast<unsigned int>(lenKey)) &&\r
358                                 (0 == strncmp(p->key, key, lenKey)))) {\r
359                         // Replace current value\r
360                         delete [](p->val);\r
361                         p->val = StringDup(val, lenVal);\r
362                         return;\r
363                 }\r
364         }\r
365         // Not found\r
366         Property *pNew = new Property;\r
367         if (pNew) {\r
368                 pNew->hash = hash;\r
369                 pNew->key = StringDup(key, lenKey);\r
370                 pNew->val = StringDup(val, lenVal);\r
371                 pNew->next = props[hash % hashRoots];\r
372                 props[hash % hashRoots] = pNew;\r
373         }\r
374 }\r
375 \r
376 void PropSet::Set(const char *keyVal) {\r
377         while (IsASpace(*keyVal))\r
378                 keyVal++;\r
379         const char *endVal = keyVal;\r
380         while (*endVal && (*endVal != '\n'))\r
381                 endVal++;\r
382         const char *eqAt = strchr(keyVal, '=');\r
383         if (eqAt) {\r
384                 Set(keyVal, eqAt + 1, eqAt-keyVal, endVal - eqAt - 1);\r
385         } else if (*keyVal) {   // No '=' so assume '=1'\r
386                 Set(keyVal, "1", endVal-keyVal, 1);\r
387         }\r
388 }\r
389 \r
390 void PropSet::Unset(const char *key, int lenKey) {\r
391         if (!*key)      // Empty keys are not supported\r
392                 return;\r
393         if (lenKey == -1)\r
394                 lenKey = static_cast<int>(strlen(key));\r
395         unsigned int hash = HashString(key, lenKey);\r
396         Property *pPrev = NULL;\r
397         for (Property *p = props[hash % hashRoots]; p; p = p->next) {\r
398                 if ((hash == p->hash) &&\r
399                         ((strlen(p->key) == static_cast<unsigned int>(lenKey)) &&\r
400                                 (0 == strncmp(p->key, key, lenKey)))) {\r
401                         if (pPrev)\r
402                                 pPrev->next = p->next;\r
403                         else\r
404                                 props[hash % hashRoots] = p->next;\r
405                         if (p == enumnext)\r
406                                 enumnext = p->next; // Not that anyone should mix enum and Set / Unset.\r
407                         delete [](p->key);\r
408                         delete [](p->val);\r
409                         delete p;\r
410                         return;\r
411                 } else {\r
412                         pPrev = p;\r
413                 }\r
414         }\r
415 }\r
416 \r
417 void PropSet::SetMultiple(const char *s) {\r
418         const char *eol = strchr(s, '\n');\r
419         while (eol) {\r
420                 Set(s);\r
421                 s = eol + 1;\r
422                 eol = strchr(s, '\n');\r
423         }\r
424         Set(s);\r
425 }\r
426 \r
427 SString PropSet::Get(const char *key) const {\r
428         unsigned int hash = HashString(key, strlen(key));\r
429         for (Property *p = props[hash % hashRoots]; p; p = p->next) {\r
430                 if ((hash == p->hash) && (0 == strcmp(p->key, key))) {\r
431                         return p->val;\r
432                 }\r
433         }\r
434         if (superPS) {\r
435                 // Failed here, so try in base property set\r
436                 return superPS->Get(key);\r
437         } else {\r
438                 return "";\r
439         }\r
440 }\r
441 \r
442 // There is some inconsistency between GetExpanded("foo") and Expand("$(foo)").\r
443 // A solution is to keep a stack of variables that have been expanded, so that\r
444 // recursive expansions can be skipped.  For now I'll just use the C++ stack\r
445 // for that, through a recursive function and a simple chain of pointers.\r
446 \r
447 struct VarChain {\r
448         VarChain(const char*var_=NULL, const VarChain *link_=NULL): var(var_), link(link_) {}\r
449 \r
450         bool contains(const char *testVar) const {\r
451                 return (var && (0 == strcmp(var, testVar)))\r
452                         || (link && link->contains(testVar));\r
453         }\r
454 \r
455         const char *var;\r
456         const VarChain *link;\r
457 };\r
458 \r
459 static int ExpandAllInPlace(const PropSet &props, SString &withVars, int maxExpands, const VarChain &blankVars = VarChain()) {\r
460         int varStart = withVars.search("$(");\r
461         while ((varStart >= 0) && (maxExpands > 0)) {\r
462                 int varEnd = withVars.search(")", varStart+2);\r
463                 if (varEnd < 0) {\r
464                         break;\r
465                 }\r
466 \r
467                 // For consistency, when we see '$(ab$(cde))', expand the inner variable first,\r
468                 // regardless whether there is actually a degenerate variable named 'ab$(cde'.\r
469                 int innerVarStart = withVars.search("$(", varStart+2);\r
470                 while ((innerVarStart > varStart) && (innerVarStart < varEnd)) {\r
471                         varStart = innerVarStart;\r
472                         innerVarStart = withVars.search("$(", varStart+2);\r
473                 }\r
474 \r
475                 SString var(withVars.c_str(), varStart + 2, varEnd);\r
476                 SString val = props.Get(var.c_str());\r
477 \r
478                 if (blankVars.contains(var.c_str())) {\r
479                         val.clear(); // treat blankVar as an empty string (e.g. to block self-reference)\r
480                 }\r
481 \r
482                 if (--maxExpands >= 0) {\r
483                         maxExpands = ExpandAllInPlace(props, val, maxExpands, VarChain(var.c_str(), &blankVars));\r
484                 }\r
485 \r
486                 withVars.remove(varStart, varEnd-varStart+1);\r
487                 withVars.insert(varStart, val.c_str(), val.length());\r
488 \r
489                 varStart = withVars.search("$(");\r
490         }\r
491 \r
492         return maxExpands;\r
493 }\r
494 \r
495 SString PropSet::GetExpanded(const char *key) const {\r
496         SString val = Get(key);\r
497         ExpandAllInPlace(*this, val, 100, VarChain(key));\r
498         return val;\r
499 }\r
500 \r
501 SString PropSet::Expand(const char *withVars, int maxExpands) const {\r
502         SString val = withVars;\r
503         ExpandAllInPlace(*this, val, maxExpands);\r
504         return val;\r
505 }\r
506 \r
507 int PropSet::GetInt(const char *key, int defaultValue) const {\r
508         SString val = GetExpanded(key);\r
509         if (val.length())\r
510                 return val.value();\r
511         return defaultValue;\r
512 }\r
513 \r
514 bool isprefix(const char *target, const char *prefix) {\r
515         while (*target && *prefix) {\r
516                 if (*target != *prefix)\r
517                         return false;\r
518                 target++;\r
519                 prefix++;\r
520         }\r
521         if (*prefix)\r
522                 return false;\r
523         else\r
524                 return true;\r
525 }\r
526 \r
527 void PropSet::Clear() {\r
528         for (int root = 0; root < hashRoots; root++) {\r
529                 Property *p = props[root];\r
530                 while (p) {\r
531                         Property *pNext = p->next;\r
532                         p->hash = 0;\r
533                         delete []p->key;\r
534                         p->key = 0;\r
535                         delete []p->val;\r
536                         p->val = 0;\r
537                         delete p;\r
538                         p = pNext;\r
539                 }\r
540                 props[root] = 0;\r
541         }\r
542 }\r
543 \r
544 char *PropSet::ToString() const {\r
545         size_t len=0;\r
546         for (int r = 0; r < hashRoots; r++) {\r
547                 for (Property *p = props[r]; p; p = p->next) {\r
548                         len += strlen(p->key) + 1;\r
549                         len += strlen(p->val) + 1;\r
550                 }\r
551         }\r
552         if (len == 0)\r
553                 len = 1;        // Return as empty string\r
554         char *ret = new char [len];\r
555         if (ret) {\r
556                 char *w = ret;\r
557                 for (int root = 0; root < hashRoots; root++) {\r
558                         for (Property *p = props[root]; p; p = p->next) {\r
559                                 strcpy(w, p->key);\r
560                                 w += strlen(p->key);\r
561                                 *w++ = '=';\r
562                                 strcpy(w, p->val);\r
563                                 w += strlen(p->val);\r
564                                 *w++ = '\n';\r
565                         }\r
566                 }\r
567                 ret[len-1] = '\0';\r
568         }\r
569         return ret;\r
570 }\r
571 \r
572 /**\r
573  * Creates an array that points into each word in the string and puts \0 terminators\r
574  * after each word.\r
575  */\r
576 static char **ArrayFromWordList(char *wordlist, int *len, bool onlyLineEnds = false) {\r
577         int prev = '\n';\r
578         int words = 0;\r
579         // For rapid determination of whether a character is a separator, build\r
580         // a look up table.\r
581         bool wordSeparator[256];\r
582         for (int i=0;i<256; i++) {\r
583                 wordSeparator[i] = false;\r
584         }\r
585         wordSeparator['\r'] = true;\r
586         wordSeparator['\n'] = true;\r
587         if (!onlyLineEnds) {\r
588                 wordSeparator[' '] = true;\r
589                 wordSeparator['\t'] = true;\r
590         }\r
591         for (int j = 0; wordlist[j]; j++) {\r
592                 int curr = static_cast<unsigned char>(wordlist[j]);\r
593                 if (!wordSeparator[curr] && wordSeparator[prev])\r
594                         words++;\r
595                 prev = curr;\r
596         }\r
597         char **keywords = new char *[words + 1];\r
598         if (keywords) {\r
599                 words = 0;\r
600                 prev = '\0';\r
601                 size_t slen = strlen(wordlist);\r
602                 for (size_t k = 0; k < slen; k++) {\r
603                         if (!wordSeparator[static_cast<unsigned char>(wordlist[k])]) {\r
604                                 if (!prev) {\r
605                                         keywords[words] = &wordlist[k];\r
606                                         words++;\r
607                                 }\r
608                         } else {\r
609                                 wordlist[k] = '\0';\r
610                         }\r
611                         prev = wordlist[k];\r
612                 }\r
613                 keywords[words] = &wordlist[slen];\r
614                 *len = words;\r
615         } else {\r
616                 *len = 0;\r
617         }\r
618         return keywords;\r
619 }\r
620 \r
621 void WordList::Clear() {\r
622         if (words) {\r
623                 delete []list;\r
624                 delete []words;\r
625         }\r
626         words = 0;\r
627         list = 0;\r
628         len = 0;\r
629         sorted = false;\r
630 }\r
631 \r
632 void WordList::Set(const char *s) {\r
633         list = StringDup(s);\r
634         sorted = false;\r
635         words = ArrayFromWordList(list, &len, onlyLineEnds);\r
636 }\r
637 \r
638 extern "C" int cmpString(const void *a1, const void *a2) {\r
639         // Can't work out the correct incantation to use modern casts here\r
640         return strcmp(*(char**)(a1), *(char**)(a2));\r
641 }\r
642 \r
643 static void SortWordList(char **words, unsigned int len) {\r
644         qsort(reinterpret_cast<void*>(words), len, sizeof(*words),\r
645               cmpString);\r
646 }\r
647 \r
648 bool WordList::InList(const char *s) {\r
649         if (0 == words)\r
650                 return false;\r
651         if (!sorted) {\r
652                 sorted = true;\r
653                 SortWordList(words, len);\r
654                 for (unsigned int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++)\r
655                         starts[k] = -1;\r
656                 for (int l = len - 1; l >= 0; l--) {\r
657                         unsigned char indexChar = words[l][0];\r
658                         starts[indexChar] = l;\r
659                 }\r
660         }\r
661         unsigned char firstChar = s[0];\r
662         int j = starts[firstChar];\r
663         if (j >= 0) {\r
664                 while ((unsigned char)words[j][0] == firstChar) {\r
665                         if (s[1] == words[j][1]) {\r
666                                 const char *a = words[j] + 1;\r
667                                 const char *b = s + 1;\r
668                                 while (*a && *a == *b) {\r
669                                         a++;\r
670                                         b++;\r
671                                 }\r
672                                 if (!*a && !*b)\r
673                                         return true;\r
674                         }\r
675                         j++;\r
676                 }\r
677         }\r
678         j = starts['^'];\r
679         if (j >= 0) {\r
680                 while (words[j][0] == '^') {\r
681                         const char *a = words[j] + 1;\r
682                         const char *b = s;\r
683                         while (*a && *a == *b) {\r
684                                 a++;\r
685                                 b++;\r
686                         }\r
687                         if (!*a)\r
688                                 return true;\r
689                         j++;\r
690                 }\r
691         }\r
692         return false;\r
693 }\r
694 \r
695 /** similar to InList, but word s can be a substring of keyword.\r
696  * eg. the keyword define is defined as def~ine. This means the word must start\r
697  * with def to be a keyword, but also defi, defin and define are valid.\r
698  * The marker is ~ in this case.\r
699  */\r
700 bool WordList::InListAbbreviated(const char *s, const char marker) {\r
701         if (0 == words)\r
702                 return false;\r
703         if (!sorted) {\r
704                 sorted = true;\r
705                 SortWordList(words, len);\r
706                 for (unsigned int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++)\r
707                         starts[k] = -1;\r
708                 for (int l = len - 1; l >= 0; l--) {\r
709                         unsigned char indexChar = words[l][0];\r
710                         starts[indexChar] = l;\r
711                 }\r
712         }\r
713         unsigned char firstChar = s[0];\r
714         int j = starts[firstChar];\r
715         if (j >= 0) {\r
716                 while (words[j][0] == firstChar) {\r
717                         bool isSubword = false;\r
718                         int start = 1;\r
719                         if (words[j][1] == marker) {\r
720                                 isSubword = true;\r
721                                 start++;\r
722                         }\r
723                         if (s[1] == words[j][start]) {\r
724                                 const char *a = words[j] + start;\r
725                                 const char *b = s + 1;\r
726                                 while (*a && *a == *b) {\r
727                                         a++;\r
728                                         if (*a == marker) {\r
729                                                 isSubword = true;\r
730                                                 a++;\r
731                                         }\r
732                                         b++;\r
733                                 }\r
734                                 if ((!*a || isSubword) && !*b)\r
735                                         return true;\r
736                         }\r
737                         j++;\r
738                 }\r
739         }\r
740         j = starts['^'];\r
741         if (j >= 0) {\r
742                 while (words[j][0] == '^') {\r
743                         const char *a = words[j] + 1;\r
744                         const char *b = s;\r
745                         while (*a && *a == *b) {\r
746                                 a++;\r
747                                 b++;\r
748                         }\r
749                         if (!*a)\r
750                                 return true;\r
751                         j++;\r
752                 }\r
753         }\r
754         return false;\r
755 }\r