]>
git.saurik.com Git - wxWidgets.git/blob - 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.
6 // Maintain a dictionary of properties
17 bool EqualCaseInsensitive(const char *a
, const char *b
) {
19 return 0 == strcasecmp(a
, b
);
21 return 0 == stricmp(a
, b
);
23 return 0 == wxStricmp(a
, b
);
27 SString::size_type
SString::npos
= -1;
29 inline unsigned int HashString(const char *s
) {
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;
43 while ((len
> 1) && lenData
> 0) {
47 if ((ch
== '\r') || (ch
== '\n')) {
49 if ((lenData
> 0) && (ch
== '\r') && ((*fpc
) == '\n')) {
50 // munch the second half of a crlf
57 } else if ((ch
== '\\') && (lenData
> 0) && ((*fpc
== '\r') || (*fpc
== '\n'))) {
71 for (int root
=0; root
< hashRoots
; root
++)
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
86 p
->val
= StringDup(val
);
91 Property
*pNew
= new Property
;
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
;
101 void PropSet::Set(char *keyval
) {
102 while (isspace(*keyval
))
104 char *eqat
= strchr(keyval
, '=');
107 Set(keyval
, eqat
+ 1);
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
))) {
120 // Failed here, so try in base property set
121 return superPS
->Get(key
);
127 SString
PropSet::GetExpanded(const char *key
) {
128 SString val
= Get(key
);
129 return Expand(val
.c_str());
132 SString
PropSet::Expand(const char *withvars
) {
133 char *base
= StringDup(withvars
);
134 char *cpvar
= strstr(base
, "$(");
136 char *cpendvar
= strchr(cpvar
, ')');
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);
150 cpvar
= strstr(base
, "$(");
157 int PropSet::GetInt(const char *key
, int defaultValue
) {
158 SString val
= Get(key
);
165 inline bool isprefix(const char *target
, const char *prefix
) {
166 while (*target
&& *prefix
) {
167 if (*target
!= *prefix
)
178 bool issuffix(const char *target
, const char *suffix
) {
179 int lentarget
= strlen(target
);
180 int lensuffix
= strlen(suffix
);
181 if (lensuffix
> lentarget
)
183 for (int i
= lensuffix
- 1; i
>= 0; i
--) {
184 if (target
[i
+ lentarget
- lensuffix
] != suffix
[i
])
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
;
197 if (strstr(orgkeyfile
, "$(") == orgkeyfile
) {
198 char *cpendvar
= strchr(orgkeyfile
, ')');
201 SString s
= Get(orgkeyfile
+ 2);
203 keyfile
= strdup(s
.c_str());
206 char *keyptr
= keyfile
;
209 keyfile
= orgkeyfile
;
212 char *del
= strchr(keyfile
, ';');
214 del
= keyfile
+ strlen(keyfile
);
217 if (*keyfile
== '*') {
218 if (issuffix(filename
, keyfile
+ 1)) {
223 } else if (0 == strcmp(keyfile
, filename
)) {
235 if (0 == strcmp(p
->key
, keybase
)) {
242 // Failed here, so try in base property set
243 return superPS
->GetWild(keybase
, filename
);
249 SString
PropSet::GetNewExpand(const char *keybase
, const char *filename
) {
250 char *base
= StringDup(GetWild(keybase
, filename
).c_str());
251 char *cpvar
= strstr(base
, "$(");
253 char *cpendvar
= strchr(cpvar
, ')');
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);
267 cpvar
= strstr(base
, "$(");
274 void PropSet::Clear() {
275 for (int root
=0; root
< hashRoots
; root
++) {
276 Property
*p
=props
[root
];
278 Property
*pNext
=p
->next
;
291 void PropSet::ReadFromMemory(const char *data
, int len
, const char *directoryForImports
) {
292 const char *pd
= data
;
294 bool ifIsTrue
= true;
296 GetFullLine(pd
, len
, linebuf
, sizeof(linebuf
));
297 if (isalpha(linebuf
[0])) // If clause ends with first non-indented line
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])) {
310 } else if (isspace(linebuf
[0]) && ifIsTrue
) {
316 void PropSet::Read(const char *filename
, const char *directoryForImports
) {
317 char propsData
[60000];
318 FILE *rcfile
= fopen(filename
, "rb");
320 int lenFile
= fread(propsData
, 1, sizeof(propsData
), rcfile
);
322 ReadFromMemory(propsData
, lenFile
, directoryForImports
);
324 //printf("Could not open <%s>\n", filename);
328 static bool iswordsep(char ch
, bool onlyLineEnds
) {
333 return ch
== '\r' || ch
== '\n';
336 // Creates an array that points into each word in the string and puts \0 terminators
338 static char **ArrayFromWordList(char *wordlist
, int *len
, bool onlyLineEnds
= false) {
342 for (int j
= 0; wordlist
[j
]; j
++) {
343 if (!iswordsep(wordlist
[j
], onlyLineEnds
) && iswordsep(prev
, onlyLineEnds
))
347 char **keywords
= new char * [words
+ 1];
351 int slen
= strlen(wordlist
);
352 for (int k
= 0; k
< slen
; k
++) {
353 if (!iswordsep(wordlist
[k
], onlyLineEnds
)) {
355 keywords
[words
] = &wordlist
[k
];
363 keywords
[words
] = &wordlist
[slen
];
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)
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
));
378 while (iswordsep(wordlist
[k
], onlyLineEnds
))
379 wordlist
[k
++] = '\0';
385 while (size
<= words
);
386 keywords
= (char**) realloc(keywords
, (size
+ 1) * sizeof (*keywords
));
388 keywords
[words
++] = wordlist
+ k
;
394 while (!iswordsep(wordlist
[k
], onlyLineEnds
));
397 keywords
[words
] = wordlist
+ slen
;
403 void WordList::Clear() {
420 void WordList::Set(const char *s
) {
423 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
424 wordsNoCase
= (char**) malloc ((len
+ 1) * sizeof (*wordsNoCase
));
425 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
428 char *WordList::Allocate(int size
) {
429 list
= new char[size
+ 1];
434 void WordList::SetFromAllocated() {
436 words
= ArrayFromWordList(list
, &len
, onlyLineEnds
);
437 wordsNoCase
= (char**) malloc ((len
+ 1) * sizeof (*wordsNoCase
));
438 memcpy(wordsNoCase
, words
, (len
+ 1) * sizeof (*words
));
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
));
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
));
451 static void SortWordList(char **words
, char **wordsNoCase
, unsigned int len
) {
452 qsort(reinterpret_cast<void*>(words
), len
, sizeof(*words
),
454 qsort(reinterpret_cast<void*>(wordsNoCase
), len
, sizeof(*wordsNoCase
),
458 bool WordList::InList(const char *s
) {
463 SortWordList(words
, wordsNoCase
, len
);
464 for (unsigned int k
= 0; k
< (sizeof(starts
) / sizeof(starts
[0])); k
++)
466 for (int l
= len
- 1; l
>= 0; l
--) {
467 unsigned char indexChar
= words
[l
][0];
468 starts
[indexChar
] = l
;
471 unsigned char firstChar
= s
[0];
472 int j
= starts
[firstChar
];
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
) {
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).
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
507 SortWordList(words
, wordsNoCase
, len
);
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()
521 else // preserve the letter case
522 while (start
<= end
) { // binary searching loop
523 pivot
= (start
+ end
) >> 1;
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()
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.
543 * NOTE: returned buffer has to be freed with a free() call.
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
565 SortWordList(words
, wordsNoCase
, len
);
567 buffer
= (char*) malloc(size
);
570 while (start
<= end
) { // binary searching loop
571 pivot
= (start
+ end
) >> 1;
572 word
= wordsNoCase
[pivot
];
573 cond
= strncasecmp(wordStart
, word
, searchLen
);
576 do { // browse sequentially the rest after the hit
577 brace
= strchr(word
, '(');
582 while (isspace(*brace
));
584 brace
= word
+ strlen(word
);
588 while (isspace(*brace
));
590 wordlen
= brace
- word
+ 1;
591 newlength
= length
+ wordlen
; // stretch the buffer
594 if (newlength
>= size
) {
597 while (size
<= newlength
);
598 buffer
= (char*) realloc(buffer
, size
);
600 if (length
) // append a new entry
601 buffer
[length
++] = ' ';
602 memcpy(buffer
+ length
, word
, wordlen
);
604 buffer
[length
] = '\0';
607 word
= wordsNoCase
[pivot
];
608 } while (!strncasecmp(wordStart
, word
, searchLen
));
611 for (;;) { // browse sequentially the rest before the hit
614 word
= wordsNoCase
[pivot
];
615 if (strncasecmp(wordStart
, word
, searchLen
))
617 brace
= strchr(word
, '(');
622 while (isspace(*brace
));
624 brace
= word
+ strlen(word
);
628 while (isspace(*brace
));
630 wordlen
= brace
- word
+ 1;
631 newlength
= length
+ wordlen
; // stretch the buffer
634 if (newlength
>= size
)
638 while (size
<= newlength
);
639 buffer
= (char*) realloc(buffer
, size
);
641 if (length
) // append a new entry
642 buffer
[length
++] = ' ';
643 memcpy(buffer
+ length
, word
, wordlen
);
645 buffer
[length
] = '\0';
647 return buffer
; // result has to be freed with free()
654 else // preserve the letter case
655 while (start
<= end
) { // binary searching loop
656 pivot
= (start
+ end
) >> 1;
658 cond
= strncmp(wordStart
, word
, searchLen
);
661 do { // browse sequentially the rest after the hit
662 brace
= strchr(word
, '(');
667 while (isspace(*brace
));
669 brace
= word
+ strlen(word
);
673 while (isspace(*brace
));
675 wordlen
= brace
- word
+ 1;
676 newlength
= length
+ wordlen
; // stretch the buffer
679 if (newlength
>= size
)
683 while (size
<= newlength
);
684 buffer
= (char*) realloc(buffer
, size
);
686 if (length
) // append a new entry
687 buffer
[length
++] = ' ';
688 memcpy(buffer
+ length
, word
, wordlen
);
690 buffer
[length
] = '\0';
694 } while (!strncmp(wordStart
, word
, searchLen
));
697 for (;;) { // browse sequentially the rest before the hit
701 if (strncmp(wordStart
, word
, searchLen
))
703 brace
= strchr(word
, '(');
708 while (isspace(*brace
));
710 brace
= word
+ strlen(word
);
714 while (isspace(*brace
));
716 wordlen
= brace
- word
+ 1;
717 newlength
= length
+ wordlen
; // stretch the buffer
720 if (newlength
>= size
)
724 while (size
<= newlength
);
725 buffer
= (char*) realloc(buffer
, size
);
727 if (length
) // append a new entry
728 buffer
[length
++] = ' ';
729 memcpy(buffer
+ length
, word
, wordlen
);
731 buffer
[length
] = '\0';
733 return buffer
; // result has to be freed with free()