]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/PropSet.cxx
7e2a906a478902d1cd2f399c9851df8ede19bd99
[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 // Get a line of input. If end of line escaped with '\\' then continue reading.
28 static bool GetFullLine(const char *&fpc, int &lenData, char *s, int len) {
29 bool continuation = true;
30 while ((len > 1) && lenData > 0) {
31 char ch = *fpc;
32 fpc++;
33 lenData--;
34 if ((ch == '\r') || (ch == '\n')) {
35 if (!continuation) {
36 if ((lenData > 0) && (ch == '\r') && ((*fpc) == '\n')) {
37 // munch the second half of a crlf
38 fpc++;
39 lenData--;
40 }
41 *s++ = '\0';
42 return true;
43 }
44 } else if ((ch == '\\') && (lenData > 0) && ((*fpc == '\r') || (*fpc == '\n'))) {
45 continuation = true;
46 } else {
47 continuation = false;
48 *s++ = ch;
49 len--;
50 }
51 }
52 return false;
53 }
54
55 PropSet::PropSet() {
56 superPS = 0;
57 size = 10;
58 used = 0;
59 vals = new char * [size];
60 }
61
62 PropSet::~PropSet() {
63 superPS = 0;
64 Clear();
65 delete []vals;
66 }
67
68 void PropSet::EnsureCanAddEntry() {
69 if (used >= size - 2) {
70 int newsize = size + 10;
71 char **newvals = new char * [newsize];
72
73 for (int i = 0; i < used; i++) {
74 newvals[i] = vals[i];
75 }
76 delete []vals;
77 vals = newvals;
78 size = newsize;
79 }
80 }
81
82 void PropSet::Set(const char *key, const char *val) {
83 EnsureCanAddEntry();
84 for (int i = 0; i < used; i += 2) {
85 if (EqualCaseInsensitive(vals[i], key)) {
86 // Replace current value
87 delete [](vals[i + 1]);
88 vals[i + 1] = StringDup(val);
89 return;
90 }
91 }
92 // Not found
93 vals[used++] = StringDup(key);
94 vals[used++] = StringDup(val);
95 }
96
97 void PropSet::Set(char *keyval) {
98 char *eqat = strchr(keyval, '=');
99 if (eqat) {
100 *eqat = '\0';
101 Set(keyval, eqat + 1);
102 *eqat = '=';
103 }
104 }
105
106 SString PropSet::Get(const char *key) {
107 for (int i = 0; i < used; i += 2) {
108 if (EqualCaseInsensitive(vals[i], key)) {
109 return vals[i + 1];
110 }
111 }
112 if (superPS) {
113 // Failed here, so try in base property set
114 return superPS->Get(key);
115 } else {
116 return "";
117 }
118 }
119
120 int PropSet::GetInt(const char *key, int defaultValue) {
121 SString val = Get(key);
122 if (val.length())
123 return Get(key).value();
124 else
125 return defaultValue;
126 }
127
128 bool isprefix(const char *target, const char *prefix) {
129 while (*target && *prefix) {
130 if (toupper(*target) != toupper(*prefix))
131 return false;
132 target++;
133 prefix++;
134 }
135 if (*prefix)
136 return false;
137 else
138 return true;
139 }
140
141 bool issuffix(const char *target, const char *suffix) {
142 int lentarget = strlen(target);
143 int lensuffix = strlen(suffix);
144 if (lensuffix > lentarget)
145 return false;
146 for (int i = lensuffix - 1; i >= 0; i--) {
147 if (toupper(target[i + lentarget - lensuffix]) != toupper(suffix[i]))
148 return false;
149 }
150 return true;
151 }
152
153 SString PropSet::GetWild(const char *keybase, const char *filename) {
154 for (int i = 0; i < used; i += 2) {
155 if (isprefix(vals[i], keybase)) {
156 char *orgkeyfile = vals[i] + strlen(keybase);
157 char *keyfile = NULL;
158
159 if (strstr(orgkeyfile, "$(") == orgkeyfile) {
160 char *cpendvar = strchr(orgkeyfile, ')');
161 if (cpendvar) {
162 int lenvar = cpendvar - orgkeyfile - 2; // Subtract the $()
163 char *var = static_cast<char *>(malloc(lenvar + 1));
164 strncpy(var, orgkeyfile + 2, lenvar);
165 var[lenvar] = '\0';
166 SString s = Get(var);
167 free(var);
168 keyfile = strdup(s.c_str());
169 }
170 }
171 char *keyptr = keyfile;
172
173 if (keyfile == NULL)
174 keyfile = orgkeyfile;
175
176 for (; ; ) {
177 char *del = strchr(keyfile, ';');
178 if (del == NULL)
179 del = keyfile + strlen(keyfile);
180 char delchr = *del;
181 *del = '\0';
182 if (*keyfile == '*') {
183 if (issuffix(filename, keyfile + 1)) {
184 *del = delchr;
185 free(keyptr);
186 return vals[i + 1];
187 }
188 } else if (EqualCaseInsensitive(keyfile, filename)) {
189 *del = delchr;
190 free(keyptr);
191 return vals[i + 1];
192 }
193 if (delchr == '\0')
194 break;
195 *del = delchr;
196 keyfile = del + 1;
197 }
198 free(keyptr);
199
200 if (EqualCaseInsensitive(vals[i], keybase)) {
201 return vals[i + 1];
202 }
203 }
204 }
205 if (superPS) {
206 // Failed here, so try in base property set
207 return superPS->GetWild(keybase, filename);
208 } else {
209 return "";
210 }
211 }
212
213 SString PropSet::GetNewExpand(const char *keybase, const char *filename) {
214 char *base = StringDup(GetWild(keybase, filename).c_str());
215 char *cpvar = strstr(base, "$(");
216 while (cpvar) {
217 char *cpendvar = strchr(cpvar, ')');
218 if (cpendvar) {
219 int lenvar = cpendvar - cpvar - 2; // Subtract the $()
220 char *var = new char[lenvar + 1];
221 strncpy(var, cpvar + 2, lenvar);
222 var[lenvar] = '\0';
223 SString val = GetWild(var, filename);
224 int newlenbase = strlen(base) + val.length() - lenvar;
225 char *newbase = new char[newlenbase];
226 strncpy(newbase, base, cpvar - base);
227 strcpy(newbase + (cpvar - base), val.c_str());
228 strcpy(newbase + (cpvar - base) + val.length(), cpendvar + 1);
229 delete []var;
230 delete []base;
231 base = newbase;
232 }
233 cpvar = strstr(base, "$(");
234 }
235 SString sret = base;
236 delete []base;
237 return sret;
238 }
239
240 void PropSet::Clear() {
241 for (int i = 0; i < used; i++) {
242 delete [](vals[i]);
243 vals[i] = 0;
244 }
245 used = 0;
246 }
247
248 void PropSet::ReadFromMemory(const char *data, int len) {
249 if (len > 0) {
250 const char *pd = data;
251 char linebuf[60000];
252 while (GetFullLine(pd, len, linebuf, sizeof(linebuf))) {
253 if (isalpha(linebuf[0]))
254 Set(linebuf);
255 }
256 }
257 }
258
259 void PropSet::Read(const char *filename) {
260 //printf("Opening properties <%s>\n", filename);
261 Clear();
262 char propsData[60000];
263 FILE *rcfile = fopen(filename, "rb");
264 if (rcfile) {
265 int lenFile = fread(propsData, 1, sizeof(propsData), rcfile);
266 fclose(rcfile);
267 ReadFromMemory(propsData, lenFile);
268 } else {
269 //printf("Could not open <%s>\n", filename);
270 }
271 }
272
273 static bool iswordsep(char ch, bool onlyLineEnds) {
274 if (!isspace(ch))
275 return false;
276 if (!onlyLineEnds)
277 return true;
278 return ch == '\r' || ch == '\n';
279 }
280
281 // Creates an array that points into each word in the string and puts \0 terminators
282 // after each word.
283 static char **ArrayFromWordList(char *wordlist, bool onlyLineEnds = false) {
284 char prev = '\n';
285 int words = 0;
286 for (int j = 0; wordlist[j]; j++) {
287 if (!iswordsep(wordlist[j], onlyLineEnds) && iswordsep(prev, onlyLineEnds))
288 words++;
289 prev = wordlist[j];
290 }
291 char **keywords = new char * [words + 1];
292 if (keywords) {
293 words = 0;
294 prev = '\0';
295 int len = strlen(wordlist);
296 for (int k = 0; k < len; k++) {
297 if (!iswordsep(wordlist[k], onlyLineEnds)) {
298 if (!prev) {
299 keywords[words] = &wordlist[k];
300 words++;
301 }
302 } else {
303 wordlist[k] = '\0';
304 }
305 prev = wordlist[k];
306 }
307 keywords[words] = &wordlist[len];
308 }
309 return keywords;
310 }
311
312 void WordList::Clear() {
313 if (words) {
314 delete []words;
315 delete []list;
316 }
317 words = 0;
318 list = 0;
319 len = 0;
320 }
321
322 void WordList::Set(const char *s) {
323 len = 0;
324 list = StringDup(s);
325 words = ArrayFromWordList(list, onlyLineEnds);
326 }
327
328 char *WordList::Allocate(int size) {
329 list = new char[size + 1];
330 list[size] = '\0';
331 return list;
332 }
333
334 void WordList::SetFromAllocated() {
335 len = 0;
336 words = ArrayFromWordList(list, onlyLineEnds);
337 }
338
339 // Shell sort based upon public domain C implementation by Raymond Gardner 1991
340 // Used here because of problems with mingw qsort.
341 static void SortWordList(char **words, unsigned int len) {
342 unsigned int gap = len / 2;
343
344 while (gap > 0) {
345 unsigned int i = gap;
346 while (i < len) {
347 unsigned int j = i;
348 char **a = words + j;
349 do {
350 j -= gap;
351 char **b = a;
352 a -= gap;
353 if (strcmp(*a, *b) > 0) {
354 char *tmp = *a;
355 *a = *b;
356 *b = tmp;
357 } else {
358 break;
359 }
360 } while (j >= gap);
361 i++;
362 }
363 gap = gap / 2;
364 }
365 }
366
367 bool WordList::InList(const char *s) {
368 if (0 == words)
369 return false;
370 if (len == 0) {
371 for (int i = 0; words[i][0]; i++)
372 len++;
373 SortWordList(words, len);
374 for (int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++)
375 starts[k] = -1;
376 for (int l = len - 1; l >= 0; l--) {
377 unsigned char indexChar = words[l][0];
378 starts[indexChar] = l;
379 }
380 }
381 unsigned char firstChar = s[0];
382 int j = starts[firstChar];
383 if (j >= 0) {
384 while (words[j][0] == firstChar) {
385 if (s[1] == words[j][1]) {
386 const char *a = words[j] + 1;
387 const char *b = s + 1;
388 while (*a && *a == *b) {
389 a++;
390 b++;
391 }
392 if (!*a && !*b)
393 return true;
394 }
395 j++;
396 }
397 }
398 return false;
399 }