// SciTE - Scintilla based Text Editor // PropSet.cxx - a java style properties file module // Copyright 1998-2000 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. // Maintain a dictionary of properties #include #include #include #include #include "Platform.h" #include "PropSet.h" bool EqualCaseInsensitive(const char *a, const char *b) { #if PLAT_GTK return 0 == strcasecmp(a, b); #elif PLAT_WIN return 0 == stricmp(a, b); #elif PLAT_WX return 0 == wxStricmp(a, b); #endif } // Get a line of input. If end of line escaped with '\\' then continue reading. static bool GetFullLine(const char *&fpc, int &lenData, char *s, int len) { bool continuation = true; s[0] = '\0'; while ((len > 1) && lenData > 0) { char ch = *fpc; fpc++; lenData--; if ((ch == '\r') || (ch == '\n')) { if (!continuation) { if ((lenData > 0) && (ch == '\r') && ((*fpc) == '\n')) { // munch the second half of a crlf fpc++; lenData--; } *s = '\0'; return true; } } else if ((ch == '\\') && (lenData > 0) && ((*fpc == '\r') || (*fpc == '\n'))) { continuation = true; } else { continuation = false; *s++ = ch; *s = '\0'; len--; } } return false; } PropSet::PropSet() { superPS = 0; size = 10; used = 0; vals = new char * [size]; } PropSet::~PropSet() { superPS = 0; Clear(); delete []vals; } void PropSet::EnsureCanAddEntry() { if (used >= size - 2) { int newsize = size + 10; char **newvals = new char * [newsize]; for (int i = 0; i < used; i++) { newvals[i] = vals[i]; } delete []vals; vals = newvals; size = newsize; } } void PropSet::Set(const char *key, const char *val) { EnsureCanAddEntry(); for (int i = 0; i < used; i += 2) { if (EqualCaseInsensitive(vals[i], key)) { // Replace current value delete [](vals[i + 1]); vals[i + 1] = StringDup(val); return; } } // Not found vals[used++] = StringDup(key); vals[used++] = StringDup(val); } void PropSet::Set(char *keyval) { char *eqat = strchr(keyval, '='); if (eqat) { *eqat = '\0'; Set(keyval, eqat + 1); *eqat = '='; } } SString PropSet::Get(const char *key) { for (int i = 0; i < used; i += 2) { if (EqualCaseInsensitive(vals[i], key)) { return vals[i + 1]; } } if (superPS) { // Failed here, so try in base property set return superPS->Get(key); } else { return ""; } } int PropSet::GetInt(const char *key, int defaultValue) { SString val = Get(key); if (val.length()) return Get(key).value(); else return defaultValue; } bool isprefix(const char *target, const char *prefix) { while (*target && *prefix) { if (toupper(*target) != toupper(*prefix)) return false; target++; prefix++; } if (*prefix) return false; else return true; } bool issuffix(const char *target, const char *suffix) { int lentarget = strlen(target); int lensuffix = strlen(suffix); if (lensuffix > lentarget) return false; for (int i = lensuffix - 1; i >= 0; i--) { if (toupper(target[i + lentarget - lensuffix]) != toupper(suffix[i])) return false; } return true; } SString PropSet::GetWild(const char *keybase, const char *filename) { for (int i = 0; i < used; i += 2) { if (isprefix(vals[i], keybase)) { char *orgkeyfile = vals[i] + strlen(keybase); char *keyfile = NULL; if (strstr(orgkeyfile, "$(") == orgkeyfile) { char *cpendvar = strchr(orgkeyfile, ')'); if (cpendvar) { int lenvar = cpendvar - orgkeyfile - 2; // Subtract the $() char *var = static_cast(malloc(lenvar + 1)); strncpy(var, orgkeyfile + 2, lenvar); var[lenvar] = '\0'; SString s = Get(var); free(var); keyfile = strdup(s.c_str()); } } char *keyptr = keyfile; if (keyfile == NULL) keyfile = orgkeyfile; for (; ; ) { char *del = strchr(keyfile, ';'); if (del == NULL) del = keyfile + strlen(keyfile); char delchr = *del; *del = '\0'; if (*keyfile == '*') { if (issuffix(filename, keyfile + 1)) { *del = delchr; free(keyptr); return vals[i + 1]; } } else if (EqualCaseInsensitive(keyfile, filename)) { *del = delchr; free(keyptr); return vals[i + 1]; } if (delchr == '\0') break; *del = delchr; keyfile = del + 1; } free(keyptr); if (EqualCaseInsensitive(vals[i], keybase)) { return vals[i + 1]; } } } if (superPS) { // Failed here, so try in base property set return superPS->GetWild(keybase, filename); } else { return ""; } } SString PropSet::GetNewExpand(const char *keybase, const char *filename) { char *base = StringDup(GetWild(keybase, filename).c_str()); char *cpvar = strstr(base, "$("); while (cpvar) { char *cpendvar = strchr(cpvar, ')'); if (cpendvar) { int lenvar = cpendvar - cpvar - 2; // Subtract the $() char *var = new char[lenvar + 1]; strncpy(var, cpvar + 2, lenvar); var[lenvar] = '\0'; SString val = GetWild(var, filename); int newlenbase = strlen(base) + val.length() - lenvar; char *newbase = new char[newlenbase]; strncpy(newbase, base, cpvar - base); strcpy(newbase + (cpvar - base), val.c_str()); strcpy(newbase + (cpvar - base) + val.length(), cpendvar + 1); delete []var; delete []base; base = newbase; } cpvar = strstr(base, "$("); } SString sret = base; delete []base; return sret; } void PropSet::Clear() { for (int i = 0; i < used; i++) { delete [](vals[i]); vals[i] = 0; } used = 0; } void PropSet::ReadFromMemory(const char *data, int len) { if (len > 0) { const char *pd = data; char linebuf[60000]; while (GetFullLine(pd, len, linebuf, sizeof(linebuf))) { if (isalpha(linebuf[0])) Set(linebuf); } // If there is a final line: if (isalpha(linebuf[0])) Set(linebuf); } } void PropSet::Read(const char *filename) { //printf("Opening properties <%s>\n", filename); Clear(); char propsData[60000]; FILE *rcfile = fopen(filename, "rb"); if (rcfile) { int lenFile = fread(propsData, 1, sizeof(propsData), rcfile); fclose(rcfile); ReadFromMemory(propsData, lenFile); } else { //printf("Could not open <%s>\n", filename); } } static bool iswordsep(char ch, bool onlyLineEnds) { if (!isspace(ch)) return false; if (!onlyLineEnds) return true; return ch == '\r' || ch == '\n'; } // Creates an array that points into each word in the string and puts \0 terminators // after each word. static char **ArrayFromWordList(char *wordlist, bool onlyLineEnds = false) { char prev = '\n'; int words = 0; for (int j = 0; wordlist[j]; j++) { if (!iswordsep(wordlist[j], onlyLineEnds) && iswordsep(prev, onlyLineEnds)) words++; prev = wordlist[j]; } char **keywords = new char * [words + 1]; if (keywords) { words = 0; prev = '\0'; int len = strlen(wordlist); for (int k = 0; k < len; k++) { if (!iswordsep(wordlist[k], onlyLineEnds)) { if (!prev) { keywords[words] = &wordlist[k]; words++; } } else { wordlist[k] = '\0'; } prev = wordlist[k]; } keywords[words] = &wordlist[len]; } return keywords; } void WordList::Clear() { if (words) { delete []words; delete []list; } words = 0; list = 0; len = 0; } void WordList::Set(const char *s) { len = 0; list = StringDup(s); words = ArrayFromWordList(list, onlyLineEnds); } char *WordList::Allocate(int size) { list = new char[size + 1]; list[size] = '\0'; return list; } void WordList::SetFromAllocated() { len = 0; words = ArrayFromWordList(list, onlyLineEnds); } // Shell sort based upon public domain C implementation by Raymond Gardner 1991 // Used here because of problems with mingw qsort. static void SortWordList(char **words, unsigned int len) { unsigned int gap = len / 2; while (gap > 0) { unsigned int i = gap; while (i < len) { unsigned int j = i; char **a = words + j; do { j -= gap; char **b = a; a -= gap; if (strcmp(*a, *b) > 0) { char *tmp = *a; *a = *b; *b = tmp; } else { break; } } while (j >= gap); i++; } gap = gap / 2; } } bool WordList::InList(const char *s) { if (0 == words) return false; if (len == 0) { for (int i = 0; words[i][0]; i++) len++; SortWordList(words, len); for (unsigned int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++) starts[k] = -1; for (int l = len - 1; l >= 0; l--) { unsigned char indexChar = words[l][0]; starts[indexChar] = l; } } unsigned char firstChar = s[0]; int j = starts[firstChar]; if (j >= 0) { while (words[j][0] == firstChar) { if (s[1] == words[j][1]) { const char *a = words[j] + 1; const char *b = s + 1; while (*a && *a == *b) { a++; b++; } if (!*a && !*b) return true; } j++; } } return false; }