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