]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/PropSet.cxx
renamed scrolwin file - no other way to make wxGTK/Univ compile
[wxWidgets.git] / src / stc / scintilla / src / PropSet.cxx
1 // SciTE - Scintilla based Text Editor
2 // PropSet.cxx - a java style properties file module
3 // Copyright 1998-2000 by Neil Hodgson <neilh@scintilla.org>
4 // The License.txt file describes the conditions under which this software may be distributed.
5
6 // Maintain a dictionary of properties
7
8 #include <stdlib.h>
9 #include <string.h>
10 #include <ctype.h>
11 #include <stdio.h>
12
13 #include "Platform.h"
14
15 #include "PropSet.h"
16
17 bool EqualCaseInsensitive(const char *a, const char *b) {
18 #if PLAT_GTK
19 return 0 == strcasecmp(a, b);
20 #elif PLAT_WIN
21 return 0 == stricmp(a, b);
22 #elif PLAT_WX
23 return 0 == wxStricmp(a, b);
24 #endif
25 }
26
27 SString::size_type SString::npos = -1;
28
29 inline unsigned int HashString(const char *s) {
30 unsigned int ret = 0;
31 while (*s) {
32 ret <<= 4;
33 ret ^= *s;
34 s++;
35 }
36 return ret;
37 }
38
39 // Get a line of input. If end of line escaped with '\\' then continue reading.
40 static bool GetFullLine(const char *&fpc, int &lenData, char *s, int len) {
41 bool continuation = true;
42 s[0] = '\0';
43 while ((len > 1) && lenData > 0) {
44 char ch = *fpc;
45 fpc++;
46 lenData--;
47 if ((ch == '\r') || (ch == '\n')) {
48 if (!continuation) {
49 if ((lenData > 0) && (ch == '\r') && ((*fpc) == '\n')) {
50 // munch the second half of a crlf
51 fpc++;
52 lenData--;
53 }
54 *s = '\0';
55 return true;
56 }
57 } else if ((ch == '\\') && (lenData > 0) && ((*fpc == '\r') || (*fpc == '\n'))) {
58 continuation = true;
59 } else {
60 continuation = false;
61 *s++ = ch;
62 *s = '\0';
63 len--;
64 }
65 }
66 return false;
67 }
68
69 PropSet::PropSet() {
70 superPS = 0;
71 for (int root=0; root < hashRoots; root++)
72 props[root] = 0;
73 }
74
75 PropSet::~PropSet() {
76 superPS = 0;
77 Clear();
78 }
79
80 void PropSet::Set(const char *key, const char *val) {
81 unsigned int hash = HashString(key);
82 for (Property *p=props[hash % hashRoots]; p; p=p->next) {
83 if ((hash == p->hash) && (0 == strcmp(p->key, key))) {
84 // Replace current value
85 delete [](p->val);
86 p->val = StringDup(val);
87 return;
88 }
89 }
90 // Not found
91 Property *pNew = new Property;
92 if (pNew) {
93 pNew->hash = HashString(key);
94 pNew->key = StringDup(key);
95 pNew->val = StringDup(val);
96 pNew->next = props[hash % hashRoots];
97 props[hash % hashRoots] = pNew;
98 }
99 }
100
101 void PropSet::Set(char *keyval) {
102 while (isspace(*keyval))
103 keyval++;
104 char *eqat = strchr(keyval, '=');
105 if (eqat) {
106 *eqat = '\0';
107 Set(keyval, eqat + 1);
108 *eqat = '=';
109 }
110 }
111
112 SString PropSet::Get(const char *key) {
113 unsigned int hash = HashString(key);
114 for (Property *p=props[hash % hashRoots]; p; p=p->next) {
115 if ((hash == p->hash) && (0 == strcmp(p->key, key))) {
116 return p->val;
117 }
118 }
119 if (superPS) {
120 // Failed here, so try in base property set
121 return superPS->Get(key);
122 } else {
123 return "";
124 }
125 }
126
127 SString PropSet::GetExpanded(const char *key) {
128 SString val = Get(key);
129 return Expand(val.c_str());
130 }
131
132 SString PropSet::Expand(const char *withvars) {
133 char *base = StringDup(withvars);
134 char *cpvar = strstr(base, "$(");
135 while (cpvar) {
136 char *cpendvar = strchr(cpvar, ')');
137 if (cpendvar) {
138 int lenvar = cpendvar - cpvar - 2; // Subtract the $()
139 char *var = StringDup(cpvar+2, lenvar);
140 SString val = GetExpanded(var);
141 int newlenbase = strlen(base) + val.length() - lenvar;
142 char *newbase = new char[newlenbase];
143 strncpy(newbase, base, cpvar - base);
144 strcpy(newbase + (cpvar - base), val.c_str());
145 strcpy(newbase + (cpvar - base) + val.length(), cpendvar + 1);
146 delete []var;
147 delete []base;
148 base = newbase;
149 }
150 cpvar = strstr(base, "$(");
151 }
152 SString sret = base;
153 delete []base;
154 return sret;
155 }
156
157 int PropSet::GetInt(const char *key, int defaultValue) {
158 SString val = Get(key);
159 if (val.length())
160 return val.value();
161 else
162 return defaultValue;
163 }
164
165 inline bool isprefix(const char *target, const char *prefix) {
166 while (*target && *prefix) {
167 if (*target != *prefix)
168 return false;
169 target++;
170 prefix++;
171 }
172 if (*prefix)
173 return false;
174 else
175 return true;
176 }
177
178 bool issuffix(const char *target, const char *suffix) {
179 int lentarget = strlen(target);
180 int lensuffix = strlen(suffix);
181 if (lensuffix > lentarget)
182 return false;
183 for (int i = lensuffix - 1; i >= 0; i--) {
184 if (target[i + lentarget - lensuffix] != suffix[i])
185 return false;
186 }
187 return true;
188 }
189
190 SString PropSet::GetWild(const char *keybase, const char *filename) {
191 for (int root=0; root < hashRoots; root++) {
192 for (Property *p=props[root]; p; p=p->next) {
193 if (isprefix(p->key, keybase)) {
194 char *orgkeyfile = p->key + strlen(keybase);
195 char *keyfile = NULL;
196
197 if (strstr(orgkeyfile, "$(") == orgkeyfile) {
198 char *cpendvar = strchr(orgkeyfile, ')');
199 if (cpendvar) {
200 *cpendvar = '\0';
201 SString s = Get(orgkeyfile + 2);
202 *cpendvar= ')';
203 keyfile = strdup(s.c_str());
204 }
205 }
206 char *keyptr = keyfile;
207
208 if (keyfile == NULL)
209 keyfile = orgkeyfile;
210
211 for (; ; ) {
212 char *del = strchr(keyfile, ';');
213 if (del == NULL)
214 del = keyfile + strlen(keyfile);
215 char delchr = *del;
216 *del = '\0';
217 if (*keyfile == '*') {
218 if (issuffix(filename, keyfile + 1)) {
219 *del = delchr;
220 free(keyptr);
221 return p->val;
222 }
223 } else if (0 == strcmp(keyfile, filename)) {
224 *del = delchr;
225 free(keyptr);
226 return p->val;
227 }
228 if (delchr == '\0')
229 break;
230 *del = delchr;
231 keyfile = del + 1;
232 }
233 free(keyptr);
234
235 if (0 == strcmp(p->key, keybase)) {
236 return p->val;
237 }
238 }
239 }
240 }
241 if (superPS) {
242 // Failed here, so try in base property set
243 return superPS->GetWild(keybase, filename);
244 } else {
245 return "";
246 }
247 }
248
249 SString PropSet::GetNewExpand(const char *keybase, const char *filename) {
250 char *base = StringDup(GetWild(keybase, filename).c_str());
251 char *cpvar = strstr(base, "$(");
252 while (cpvar) {
253 char *cpendvar = strchr(cpvar, ')');
254 if (cpendvar) {
255 int lenvar = cpendvar - cpvar - 2; // Subtract the $()
256 char *var = StringDup(cpvar+2, lenvar);
257 SString val = GetWild(var, filename);
258 int newlenbase = strlen(base) + val.length() - lenvar;
259 char *newbase = new char[newlenbase];
260 strncpy(newbase, base, cpvar - base);
261 strcpy(newbase + (cpvar - base), val.c_str());
262 strcpy(newbase + (cpvar - base) + val.length(), cpendvar + 1);
263 delete []var;
264 delete []base;
265 base = newbase;
266 }
267 cpvar = strstr(base, "$(");
268 }
269 SString sret = base;
270 delete []base;
271 return sret;
272 }
273
274 void PropSet::Clear() {
275 for (int root=0; root < hashRoots; root++) {
276 Property *p=props[root];
277 while (p) {
278 Property *pNext=p->next;
279 p->hash = 0;
280 delete p->key;
281 p->key = 0;
282 delete p->val;
283 p->val = 0;
284 delete p;
285 p = pNext;
286 }
287 props[root] = 0;
288 }
289 }
290
291 void PropSet::ReadFromMemory(const char *data, int len, const char *directoryForImports) {
292 const char *pd = data;
293 char linebuf[60000];
294 bool ifIsTrue = true;
295 while (len > 0) {
296 GetFullLine(pd, len, linebuf, sizeof(linebuf));
297 if (isalpha(linebuf[0])) // If clause ends with first non-indented line
298 ifIsTrue = true;
299 if (isprefix(linebuf, "if ")) {
300 const char *expr = linebuf + strlen("if") + 1;
301 ifIsTrue = GetInt(expr);
302 } else if (isprefix(linebuf, "import ") && directoryForImports) {
303 char importPath[1024];
304 strcpy(importPath, directoryForImports);
305 strcat(importPath, linebuf + strlen("import") + 1);
306 strcat(importPath, ".properties");
307 Read(importPath,directoryForImports);
308 } else if (isalpha(linebuf[0])) {
309 Set(linebuf);
310 } else if (isspace(linebuf[0]) && ifIsTrue) {
311 Set(linebuf);
312 }
313 }
314 }
315
316 void PropSet::Read(const char *filename, const char *directoryForImports) {
317 char propsData[60000];
318 FILE *rcfile = fopen(filename, "rb");
319 if (rcfile) {
320 int lenFile = fread(propsData, 1, sizeof(propsData), rcfile);
321 fclose(rcfile);
322 ReadFromMemory(propsData, lenFile, directoryForImports);
323 } else {
324 //printf("Could not open <%s>\n", filename);
325 }
326 }
327
328 static bool iswordsep(char ch, bool onlyLineEnds) {
329 if (!isspace(ch))
330 return false;
331 if (!onlyLineEnds)
332 return true;
333 return ch == '\r' || ch == '\n';
334 }
335
336 // Creates an array that points into each word in the string and puts \0 terminators
337 // after each word.
338 static char **ArrayFromWordList(char *wordlist, int *len, bool onlyLineEnds = false) {
339 #if 1
340 char prev = '\n';
341 int words = 0;
342 for (int j = 0; wordlist[j]; j++) {
343 if (!iswordsep(wordlist[j], onlyLineEnds) && iswordsep(prev, onlyLineEnds))
344 words++;
345 prev = wordlist[j];
346 }
347 char **keywords = new char * [words + 1];
348 if (keywords) {
349 words = 0;
350 prev = '\0';
351 int slen = strlen(wordlist);
352 for (int k = 0; k < slen; k++) {
353 if (!iswordsep(wordlist[k], onlyLineEnds)) {
354 if (!prev) {
355 keywords[words] = &wordlist[k];
356 words++;
357 }
358 } else {
359 wordlist[k] = '\0';
360 }
361 prev = wordlist[k];
362 }
363 keywords[words] = &wordlist[slen];
364 *len = words;
365 } else {
366 *len = 0;
367 }
368 #else
369 int words = 0; // length of the returned buffer of pointers
370 #undef APICHUNK // how many pointers will be pre-allocated (to avoid buffer reallocation on each new pointer)
371 #define APICHUNK 256
372 int size = APICHUNK; // real size of the returned buffer of pointers
373 char **keywords; // buffer for the pointers returned
374 int slen = strlen(wordlist); //length of the buffer with api file
375 keywords = (char**) malloc((size + 1) * sizeof (*keywords));
376 words = 0;
377 for (int k = 0;;) {
378 while (iswordsep(wordlist[k], onlyLineEnds))
379 wordlist[k++] = '\0';
380 if (k >= slen)
381 break;
382 if (words >= size) {
383 do
384 size += APICHUNK;
385 while (size <= words);
386 keywords = (char**) realloc(keywords, (size + 1) * sizeof (*keywords));
387 }
388 keywords[words++] = wordlist + k;
389 do
390 if (k < slen)
391 k++;
392 else
393 goto out;
394 while (!iswordsep(wordlist[k], onlyLineEnds));
395 }
396 out:
397 keywords[words] = wordlist + slen;
398 *len = words;
399 #endif
400 return keywords;
401 }
402
403 void WordList::Clear() {
404 if (words) {
405 delete []list;
406 #if 1
407 delete []words;
408 #else
409 free(words);
410 #endif
411 free(wordsNoCase);
412 }
413 words = 0;
414 wordsNoCase = 0;
415 list = 0;
416 len = 0;
417 sorted = false;
418 }
419
420 void WordList::Set(const char *s) {
421 list = StringDup(s);
422 sorted = false;
423 words = ArrayFromWordList(list, &len, onlyLineEnds);
424 wordsNoCase = (char**) malloc ((len + 1) * sizeof (*wordsNoCase));
425 memcpy(wordsNoCase, words, (len + 1) * sizeof (*words));
426 }
427
428 char *WordList::Allocate(int size) {
429 list = new char[size + 1];
430 list[size] = '\0';
431 return list;
432 }
433
434 void WordList::SetFromAllocated() {
435 sorted = false;
436 words = ArrayFromWordList(list, &len, onlyLineEnds);
437 wordsNoCase = (char**) malloc ((len + 1) * sizeof (*wordsNoCase));
438 memcpy(wordsNoCase, words, (len + 1) * sizeof (*words));
439 }
440
441 int cmpString(const void *a1, const void *a2) {
442 // Can't work out the correct incantation to use modern casts here
443 return strcmp(*(char**)(a1), *(char**)(a2));
444 }
445
446 int cmpStringNoCase(const void *a1, const void *a2) {
447 // Can't work out the correct incantation to use modern casts here
448 return strcasecmp(*(char**)(a1), *(char**)(a2));
449 }
450
451 static void SortWordList(char **words, char **wordsNoCase, unsigned int len) {
452 qsort(reinterpret_cast<void*>(words), len, sizeof(*words),
453 cmpString);
454 qsort(reinterpret_cast<void*>(wordsNoCase), len, sizeof(*wordsNoCase),
455 cmpStringNoCase);
456 }
457
458 bool WordList::InList(const char *s) {
459 if (0 == words)
460 return false;
461 if (!sorted) {
462 sorted = true;
463 SortWordList(words, wordsNoCase, len);
464 for (unsigned int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++)
465 starts[k] = -1;
466 for (int l = len - 1; l >= 0; l--) {
467 unsigned char indexChar = words[l][0];
468 starts[indexChar] = l;
469 }
470 }
471 unsigned char firstChar = s[0];
472 int j = starts[firstChar];
473 if (j >= 0) {
474 while (words[j][0] == firstChar) {
475 if (s[1] == words[j][1]) {
476 const char *a = words[j] + 1;
477 const char *b = s + 1;
478 while (*a && *a == *b) {
479 a++;
480 b++;
481 }
482 if (!*a && !*b)
483 return true;
484 }
485 j++;
486 }
487 }
488 return false;
489 }
490
491 /**
492 * Returns an element (complete) of the wordlist array which has the beginning
493 * the same as the passed string. The length of the word to compare is passed
494 * too. Letter case can be ignored or preserved (default).
495 */
496 const char *WordList::GetNearestWord(const char *wordStart, int searchLen /*= -1*/, bool ignoreCase /*= false*/) {
497 int start = 0; // lower bound of the api array block to search
498 int end = len - 1; // upper bound of the api array block to search
499 int pivot; // index of api array element just being compared
500 int cond; // comparison result (in the sense of strcmp() result)
501 const char *word; // api array element just being compared
502
503 if (0 == words)
504 return NULL;
505 if (!sorted) {
506 sorted = true;
507 SortWordList(words, wordsNoCase, len);
508 }
509 if (ignoreCase)
510 while (start <= end) { // binary searching loop
511 pivot = (start + end) >> 1;
512 word = wordsNoCase[pivot];
513 cond = strncasecmp(wordStart, word, searchLen);
514 if (!cond && nonFuncChar(word[searchLen])) // maybe there should be a "non-word character" test here?
515 return word; // result must not be freed with free()
516 else if (cond < 0)
517 end = pivot - 1;
518 else if (cond > 0)
519 start = pivot + 1;
520 }
521 else // preserve the letter case
522 while (start <= end) { // binary searching loop
523 pivot = (start + end) >> 1;
524 word = words[pivot];
525 cond = strncmp(wordStart, word, searchLen);
526 if (!cond && nonFuncChar(word[searchLen])) // maybe there should be a "non-word character" test here?
527 return word; // result must not be freed with free()
528 else if (cond >= 0)
529 start = pivot + 1;
530 else if (cond < 0)
531 end = pivot - 1;
532 }
533 return NULL;
534 }
535
536 /**
537 * Returns elements (first words of them) of the wordlist array which have
538 * the beginning the same as the passed string. The length of the word to
539 * compare is passed too. Letter case can be ignored or preserved (default).
540 * If there are more words meeting the condition they are returned all of
541 * them in the ascending order separated with spaces.
542 *
543 * NOTE: returned buffer has to be freed with a free() call.
544 */
545 char *WordList::GetNearestWords(const char *wordStart, int searchLen /*= -1*/, bool ignoreCase /*= false*/) {
546 int wordlen; // length of the word part (before the '(' brace) of the api array element
547 int length = 0; // length of the returned buffer of words (string)
548 int newlength; // length of the new buffer before the reallocating itself
549 #undef WORDCHUNK // how many characters will be pre-allocated (to avoid buffer reallocation on each new word)
550 #define WORDCHUNK 100
551 int size = WORDCHUNK; // real size of the returned buffer of words
552 char *buffer; // buffer for the words returned
553 int start = 0; // lower bound of the api array block to search
554 int end = len - 1; // upper bound of the api array block to search
555 int pivot; // index of api array element just being compared
556 int cond; // comparison result (in the sense of strcmp() result)
557 int oldpivot; // pivot storage to be able to browse the api array upwards and then downwards
558 const char *word; // api array element just being compared
559 const char *brace; // position of the opening brace in the api array element just being compared
560
561 if (0 == words)
562 return NULL;
563 if (!sorted) {
564 sorted = true;
565 SortWordList(words, wordsNoCase, len);
566 }
567 buffer = (char*) malloc(size);
568 *buffer = '\0';
569 if (ignoreCase)
570 while (start <= end) { // binary searching loop
571 pivot = (start + end) >> 1;
572 word = wordsNoCase[pivot];
573 cond = strncasecmp(wordStart, word, searchLen);
574 if (!cond) {
575 oldpivot = pivot;
576 do { // browse sequentially the rest after the hit
577 brace = strchr(word, '(');
578 if (brace)
579 do
580 if (--brace < word)
581 break;
582 while (isspace(*brace));
583 else {
584 brace = word + strlen(word);
585 do
586 if (--brace < word)
587 break;
588 while (isspace(*brace));
589 }
590 wordlen = brace - word + 1;
591 newlength = length + wordlen; // stretch the buffer
592 if (length)
593 newlength++;
594 if (newlength >= size) {
595 do
596 size += WORDCHUNK;
597 while (size <= newlength);
598 buffer = (char*) realloc(buffer, size);
599 }
600 if (length) // append a new entry
601 buffer[length++] = ' ';
602 memcpy(buffer + length, word, wordlen);
603 length = newlength;
604 buffer[length] = '\0';
605 if (++pivot > end)
606 break;
607 word = wordsNoCase[pivot];
608 } while (!strncasecmp(wordStart, word, searchLen));
609
610 pivot = oldpivot;
611 for (;;) { // browse sequentially the rest before the hit
612 if (--pivot < start)
613 break;
614 word = wordsNoCase[pivot];
615 if (strncasecmp(wordStart, word, searchLen))
616 break;
617 brace = strchr(word, '(');
618 if (brace)
619 do
620 if (--brace < word)
621 break;
622 while (isspace(*brace));
623 else {
624 brace = word + strlen(word);
625 do
626 if (--brace < word)
627 break;
628 while (isspace(*brace));
629 }
630 wordlen = brace - word + 1;
631 newlength = length + wordlen; // stretch the buffer
632 if (length)
633 newlength++;
634 if (newlength >= size)
635 {
636 do
637 size += WORDCHUNK;
638 while (size <= newlength);
639 buffer = (char*) realloc(buffer, size);
640 }
641 if (length) // append a new entry
642 buffer[length++] = ' ';
643 memcpy(buffer + length, word, wordlen);
644 length = newlength;
645 buffer[length] = '\0';
646 }
647 return buffer; // result has to be freed with free()
648 }
649 else if (cond < 0)
650 end = pivot - 1;
651 else if (cond > 0)
652 start = pivot + 1;
653 }
654 else // preserve the letter case
655 while (start <= end) { // binary searching loop
656 pivot = (start + end) >> 1;
657 word = words[pivot];
658 cond = strncmp(wordStart, word, searchLen);
659 if (!cond) {
660 oldpivot = pivot;
661 do { // browse sequentially the rest after the hit
662 brace = strchr(word, '(');
663 if (brace)
664 do
665 if (--brace < word)
666 break;
667 while (isspace(*brace));
668 else {
669 brace = word + strlen(word);
670 do
671 if (--brace < word)
672 break;
673 while (isspace(*brace));
674 }
675 wordlen = brace - word + 1;
676 newlength = length + wordlen; // stretch the buffer
677 if (length)
678 newlength++;
679 if (newlength >= size)
680 {
681 do
682 size += WORDCHUNK;
683 while (size <= newlength);
684 buffer = (char*) realloc(buffer, size);
685 }
686 if (length) // append a new entry
687 buffer[length++] = ' ';
688 memcpy(buffer + length, word, wordlen);
689 length = newlength;
690 buffer[length] = '\0';
691 if (++pivot > end)
692 break;
693 word = words[pivot];
694 } while (!strncmp(wordStart, word, searchLen));
695
696 pivot = oldpivot;
697 for (;;) { // browse sequentially the rest before the hit
698 if (--pivot < start)
699 break;
700 word = words[pivot];
701 if (strncmp(wordStart, word, searchLen))
702 break;
703 brace = strchr(word, '(');
704 if (brace)
705 do
706 if (--brace < word)
707 break;
708 while (isspace(*brace));
709 else {
710 brace = word + strlen(word);
711 do
712 if (--brace < word)
713 break;
714 while (isspace(*brace));
715 }
716 wordlen = brace - word + 1;
717 newlength = length + wordlen; // stretch the buffer
718 if (length)
719 newlength++;
720 if (newlength >= size)
721 {
722 do
723 size += WORDCHUNK;
724 while (size <= newlength);
725 buffer = (char*) realloc(buffer, size);
726 }
727 if (length) // append a new entry
728 buffer[length++] = ' ';
729 memcpy(buffer + length, word, wordlen);
730 length = newlength;
731 buffer[length] = '\0';
732 }
733 return buffer; // result has to be freed with free()
734 }
735 else if (cond < 0)
736 end = pivot - 1;
737 else if (cond > 0)
738 start = pivot + 1;
739 }
740 free(buffer);
741 return NULL;
742 }