#pragma implementation "fileconf.h"
#endif
+// ============================================================================
+// declarations
+// ============================================================================
+
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "wx/wxprec.h"
-#include "wx/fileconf.h"
-
-#ifdef wxUSE_CONFIG
-
#ifdef __BORLANDC__
#pragma hdrstop
#endif //__BORLANDC__
#include "wx/log.h"
#include "wx/textfile.h"
#include "wx/config.h"
+#include "wx/fileconf.h"
#include "wx/utils.h" // for wxGetHomeDir
// global functions declarations
// ----------------------------------------------------------------------------
-// is 'c' a valid character in group name?
-// NB: wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR must be valid chars,
-// but _not_ ']' (group name delimiter)
-inline bool IsValid(char c) { return isalnum(c) || strchr("@_/-!.*%", c); }
-
// compare functions for sorting the arrays
-static int CompareEntries(wxFileConfig::ConfigEntry *p1,
- wxFileConfig::ConfigEntry *p2);
-static int CompareGroups(wxFileConfig::ConfigGroup *p1,
- wxFileConfig::ConfigGroup *p2);
+static int CompareEntries(ConfigEntry *p1, ConfigEntry *p2);
+static int CompareGroups(ConfigGroup *p1, ConfigGroup *p2);
// filter strings
-static wxString FilterIn(const wxString& str);
-static wxString FilterOut(const wxString& str);
+static wxString FilterInValue(const wxString& str);
+static wxString FilterOutValue(const wxString& str);
+
+static wxString FilterInEntryName(const wxString& str);
+static wxString FilterOutEntryName(const wxString& str);
// ============================================================================
// implementation
pEnd = pStart;
while ( *++pEnd != ']' ) {
- if ( !IsValid(*pEnd) && *pEnd != ' ' ) // allow spaces in group names
- break;
+ if ( *pEnd == '\n' || *pEnd == '\0' )
+ break;
}
if ( *pEnd != ']' ) {
// group name here is always considered as abs path
wxString strGroup;
pStart++;
- strGroup << wxCONFIG_PATH_SEPARATOR << wxString(pStart, pEnd - pStart);
+ strGroup << wxCONFIG_PATH_SEPARATOR
+ << FilterInEntryName(wxString(pStart, pEnd - pStart));
// will create it if doesn't yet exist
SetPath(strGroup);
}
else { // a key
const char *pEnd = pStart;
- while ( IsValid(*pEnd) )
+ while ( !isspace(*pEnd) ) {
+ if ( *pEnd == '\\' ) {
+ // next character may be space or not - still take it because it's
+ // quoted
+ pEnd++;
+ }
+
pEnd++;
+ }
- wxString strKey(pStart, pEnd);
+ wxString strKey(FilterInEntryName(wxString(pStart, pEnd)));
// skip whitespace
while ( isspace(*pEnd) )
while ( isspace(*pEnd) )
pEnd++;
- pEntry->SetValue(FilterIn(pEnd), FALSE /* read from file */);
+ pEntry->SetValue(FilterInValue(pEnd), FALSE /* read from file */);
}
}
}
wxString strName = path.Name();
if ( strName.IsEmpty() ) {
// setting the value of a group is an error
- wxASSERT_MSG( IsEmpty(szValue), _("can't set value of a group!") );
+ wxASSERT_MSG( IsEmpty(szValue), "can't set value of a group!" );
// ... except if it's empty in which case it's a way to force it's creation
m_pCurrentGroup->SetDirty();
// check that the name is reasonable
if ( strName[0u] == wxCONFIG_IMMUTABLE_PREFIX ) {
- wxLogError(_("Entry name can't start with '%c'."),
+ wxLogError(_("Config entry name cannot start with '%c'."),
wxCONFIG_IMMUTABLE_PREFIX);
return FALSE;
}
- for ( const char *pc = strName; *pc != '\0'; pc++ ) {
- if ( !IsValid(*pc) ) {
- wxLogError(_("Character '%c' is invalid in a config entry name."),
- *pc);
- return FALSE;
- }
- }
+ strName = FilterOutEntryName(strName);
ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(strName);
if ( pEntry == NULL )
return file.Commit();
}
+// ----------------------------------------------------------------------------
+// renaming groups/entries
+// ----------------------------------------------------------------------------
+
+bool wxFileConfig::RenameEntry(const wxString& oldName,
+ const wxString& newName)
+{
+ // check that the entry exists
+ ConfigEntry *oldEntry = m_pCurrentGroup->FindEntry(oldName);
+ if ( !oldEntry )
+ return FALSE;
+
+ // check that the new entry doesn't already exist
+ if ( m_pCurrentGroup->FindEntry(newName) )
+ return FALSE;
+
+ // delete the old entry, create the new one
+ wxString value = oldEntry->Value();
+ if ( !m_pCurrentGroup->DeleteEntry(oldName) )
+ return FALSE;
+
+ ConfigEntry *newEntry = m_pCurrentGroup->AddEntry(newName);
+ newEntry->SetValue(value);
+
+ return TRUE;
+}
+
+bool wxFileConfig::RenameGroup(const wxString& oldName,
+ const wxString& newName)
+{
+ // check that the group exists
+ ConfigGroup *group = m_pCurrentGroup->FindSubgroup(oldName);
+ if ( !group )
+ return FALSE;
+
+ // check that the new group doesn't already exist
+ if ( m_pCurrentGroup->FindSubgroup(newName) )
+ return FALSE;
+
+ group->Rename(newName);
+
+ return TRUE;
+}
+
// ----------------------------------------------------------------------------
// delete groups/entries
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// append a new line to the end of the list
-wxFileConfig::LineList *wxFileConfig::LineListAppend(const wxString& str)
+LineList *wxFileConfig::LineListAppend(const wxString& str)
{
LineList *pLine = new LineList(str);
}
// insert a new line after the given one or in the very beginning if !pLine
-wxFileConfig::LineList *wxFileConfig::LineListInsert(const wxString& str,
+LineList *wxFileConfig::LineListInsert(const wxString& str,
LineList *pLine)
{
if ( pLine == m_linesTail )
// ----------------------------------------------------------------------------
// ctor
-wxFileConfig::ConfigGroup::ConfigGroup(wxFileConfig::ConfigGroup *pParent,
+ConfigGroup::ConfigGroup(ConfigGroup *pParent,
const wxString& strName,
wxFileConfig *pConfig)
: m_aEntries(CompareEntries),
}
// dtor deletes all children
-wxFileConfig::ConfigGroup::~ConfigGroup()
+ConfigGroup::~ConfigGroup()
{
// entries
size_t n, nCount = m_aEntries.Count();
// line
// ----------------------------------------------------------------------------
-void wxFileConfig::ConfigGroup::SetLine(LineList *pLine)
+void ConfigGroup::SetLine(LineList *pLine)
{
wxASSERT( m_pLine == NULL ); // shouldn't be called twice
backwards in the config file (OTOH, it's not that important) and as we
would still need to do it for the subgroups the code wouldn't have been
significantly less complicated.
- */
+*/
// Return the line which contains "[our name]". If we're still not in the list,
// add our line to it immediately after the last line of our parent group if we
// have it or in the very beginning if we're the root group.
-wxFileConfig::LineList *wxFileConfig::ConfigGroup::GetGroupLine()
+LineList *ConfigGroup::GetGroupLine()
{
if ( m_pLine == NULL ) {
ConfigGroup *pParent = Parent();
// Return the last line belonging to the subgroups of this group (after which
// we can add a new subgroup), if we don't have any subgroups or entries our
// last line is the group line (m_pLine) itself.
-wxFileConfig::LineList *wxFileConfig::ConfigGroup::GetLastGroupLine()
+LineList *ConfigGroup::GetLastGroupLine()
{
// if we have any subgroups, our last line is the last line of the last
// subgroup
if ( m_pLastGroup != NULL ) {
- wxFileConfig::LineList *pLine = m_pLastGroup->GetLastGroupLine();
+ LineList *pLine = m_pLastGroup->GetLastGroupLine();
wxASSERT( pLine != NULL ); // last group must have !NULL associated line
return pLine;
// return the last line belonging to the entries of this group (after which
// we can add a new entry), if we don't have any entries we will add the new
// one immediately after the group line itself.
-wxFileConfig::LineList *wxFileConfig::ConfigGroup::GetLastEntryLine()
+LineList *ConfigGroup::GetLastEntryLine()
{
if ( m_pLastEntry != NULL ) {
- wxFileConfig::LineList *pLine = m_pLastEntry->GetLine();
+ LineList *pLine = m_pLastEntry->GetLine();
wxASSERT( pLine != NULL ); // last entry must have !NULL associated line
return pLine;
// group name
// ----------------------------------------------------------------------------
-wxString wxFileConfig::ConfigGroup::GetFullName() const
+void ConfigGroup::Rename(const wxString& newName)
+{
+ m_strName = newName;
+
+ LineList *line = GetGroupLine();
+ wxString strFullName;
+ strFullName << "[" << (GetFullName().c_str() + 1) << "]"; // +1: no '/'
+ line->SetText(strFullName);
+
+ SetDirty();
+}
+
+wxString ConfigGroup::GetFullName() const
{
if ( Parent() )
return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR + Name();
// ----------------------------------------------------------------------------
// use binary search because the array is sorted
-wxFileConfig::ConfigEntry *
-wxFileConfig::ConfigGroup::FindEntry(const char *szName) const
+ConfigEntry *
+ConfigGroup::FindEntry(const char *szName) const
{
size_t i,
lo = 0,
hi = m_aEntries.Count();
int res;
- wxFileConfig::ConfigEntry *pEntry;
+ ConfigEntry *pEntry;
while ( lo < hi ) {
i = (lo + hi)/2;
return NULL;
}
-wxFileConfig::ConfigGroup *
-wxFileConfig::ConfigGroup::FindSubgroup(const char *szName) const
+ConfigGroup *
+ConfigGroup::FindSubgroup(const char *szName) const
{
size_t i,
lo = 0,
hi = m_aSubgroups.Count();
int res;
- wxFileConfig::ConfigGroup *pGroup;
+ ConfigGroup *pGroup;
while ( lo < hi ) {
i = (lo + hi)/2;
// ----------------------------------------------------------------------------
// create a new entry and add it to the current group
-wxFileConfig::ConfigEntry *
-wxFileConfig::ConfigGroup::AddEntry(const wxString& strName, int nLine)
+ConfigEntry *
+ConfigGroup::AddEntry(const wxString& strName, int nLine)
{
wxASSERT( FindEntry(strName) == NULL );
}
// create a new group and add it to the current group
-wxFileConfig::ConfigGroup *
-wxFileConfig::ConfigGroup::AddSubgroup(const wxString& strName)
+ConfigGroup *
+ConfigGroup::AddSubgroup(const wxString& strName)
{
wxASSERT( FindSubgroup(strName) == NULL );
delete several of them.
*/
-bool wxFileConfig::ConfigGroup::DeleteSubgroupByName(const char *szName)
+bool ConfigGroup::DeleteSubgroupByName(const char *szName)
{
return DeleteSubgroup(FindSubgroup(szName));
}
// doesn't delete the subgroup itself, but does remove references to it from
// all other data structures (and normally the returned pointer should be
// deleted a.s.a.p. because there is nothing much to be done with it anyhow)
-bool wxFileConfig::ConfigGroup::DeleteSubgroup(ConfigGroup *pGroup)
+bool ConfigGroup::DeleteSubgroup(ConfigGroup *pGroup)
{
wxCHECK( pGroup != NULL, FALSE ); // deleting non existing group?
}
// and subgroups of this sungroup
+#if 0
+ // pGroup->m_aSubgroups.Count() gets decremented in DeleteSubgroup(),
+ // so we cannot do this.
+
nCount = pGroup->m_aSubgroups.Count();
for ( size_t nGroup = 0; nGroup < nCount; nGroup++ ) {
pGroup->DeleteSubgroup(pGroup->m_aSubgroups[nGroup]);
}
-
+#endif
+ while(pGroup->m_aSubgroups.Count() > 0)
+ pGroup->DeleteSubgroup(pGroup->m_aSubgroups[0]);
+
LineList *pLine = pGroup->m_pLine;
if ( pLine != NULL ) {
// notice that we may do this test inside the previous "if" because the
return TRUE;
}
-bool wxFileConfig::ConfigGroup::DeleteEntry(const char *szName)
+bool ConfigGroup::DeleteEntry(const char *szName)
{
ConfigEntry *pEntry = FindEntry(szName);
wxCHECK( pEntry != NULL, FALSE ); // deleting non existing item?
// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------
-void wxFileConfig::ConfigGroup::SetDirty()
+void ConfigGroup::SetDirty()
{
m_bDirty = TRUE;
if ( Parent() != NULL ) // propagate upwards
// ----------------------------------------------------------------------------
// ctor
// ----------------------------------------------------------------------------
-wxFileConfig::ConfigEntry::ConfigEntry(wxFileConfig::ConfigGroup *pParent,
+ConfigEntry::ConfigEntry(ConfigGroup *pParent,
const wxString& strName,
int nLine)
: m_strName(strName)
// set value
// ----------------------------------------------------------------------------
-void wxFileConfig::ConfigEntry::SetLine(LineList *pLine)
+void ConfigEntry::SetLine(LineList *pLine)
{
if ( m_pLine != NULL ) {
wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
// second parameter is FALSE if we read the value from file and prevents the
// entry from being marked as 'dirty'
-void wxFileConfig::ConfigEntry::SetValue(const wxString& strValue, bool bUser)
+void ConfigEntry::SetValue(const wxString& strValue, bool bUser)
{
if ( bUser && IsImmutable() ) {
wxLogWarning(_("attempt to change immutable key '%s' ignored."),
m_strValue = strValue;
if ( bUser ) {
- wxString strVal = FilterOut(strValue);
+ wxString strVal = FilterOutValue(strValue);
wxString strLine;
strLine << m_strName << " = " << strVal;
}
else {
// add a new line to the file
- wxASSERT( m_nLine == NOT_FOUND ); // consistency check
+ wxASSERT( m_nLine == wxNOT_FOUND ); // consistency check
m_pLine = Group()->Config()->LineListInsert(strLine,
Group()->GetLastEntryLine());
}
}
-void wxFileConfig::ConfigEntry::SetDirty()
+void ConfigEntry::SetDirty()
{
m_bDirty = TRUE;
Group()->SetDirty();
// compare functions for array sorting
// ----------------------------------------------------------------------------
-int CompareEntries(wxFileConfig::ConfigEntry *p1,
- wxFileConfig::ConfigEntry *p2)
+int CompareEntries(ConfigEntry *p1,
+ ConfigEntry *p2)
{
#if wxCONFIG_CASE_SENSITIVE
return strcmp(p1->Name(), p2->Name());
#endif
}
-int CompareGroups(wxFileConfig::ConfigGroup *p1,
- wxFileConfig::ConfigGroup *p2)
+int CompareGroups(ConfigGroup *p1,
+ ConfigGroup *p2)
{
#if wxCONFIG_CASE_SENSITIVE
return strcmp(p1->Name(), p2->Name());
// filter functions
// ----------------------------------------------------------------------------
-// undo FilterOut
-wxString FilterIn(const wxString& str)
+// undo FilterOutValue
+static wxString FilterInValue(const wxString& str)
{
wxString strResult;
strResult.Alloc(str.Len());
}
// quote the string before writing it to file
-wxString FilterOut(const wxString& str)
+static wxString FilterOutValue(const wxString& str)
{
- if(str.IsEmpty())
+ if ( !str )
return str;
wxString strResult;
return strResult;
}
-#endif
+// undo FilterOutEntryName
+static wxString FilterInEntryName(const wxString& str)
+{
+ wxString strResult;
+ strResult.Alloc(str.Len());
+
+ for ( const char *pc = str.c_str(); *pc != '\0'; pc++ ) {
+ if ( *pc == '\\' )
+ pc++;
+
+ strResult += *pc;
+ }
+
+ return strResult;
+}
+
+// sanitize entry or group name: insert '\\' before any special characters
+static wxString FilterOutEntryName(const wxString& str)
+{
+ wxString strResult;
+ strResult.Alloc(str.Len());
+
+ for ( const char *pc = str.c_str(); *pc != '\0'; pc++ ) {
+ char c = *pc;
+
+ // we explicitly allow some of "safe" chars and 8bit ASCII characters
+ // which will probably never have special meaning
+ // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
+ // should *not* be quoted
+ if ( !isalnum(c) && !strchr("@_/-!.*%", c) && ((c & 0x80) == 0) )
+ strResult += '\\';
+
+ strResult += c;
+ }
+
+ return strResult;
+}
- // wxUSE_CONFIG