/* * Copyright (C) 2004-2005 Vadim Berezniker * http://www.kryptolus.com * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "stdafx.h" #include "common.h" #include "sabbu.h" #include "stringutils.h" #include "kryTextFileReader.h" /* * Creates a new preference manager. * * Returns a handle to be used with preference functions. */ kryPrefManager::kryPrefManager() : m_filename(NULL), m_section_stack(NULL), m_section_current(NULL) { this->m_sections = new kryHash (g_str_hash, g_str_equal, kry_free_minimal, (GDestroyNotify) kryPrefSection::FreeStatic); } /* * Destroys the preferences. */ kryPrefManager::~kryPrefManager() { delete this->m_sections; if(this->m_filename) kry_free(this->m_filename); while(this->m_section_stack) this->PopSection(); } void kryPrefManager::Clear() { delete this->m_sections; this->m_sections = new kryHash (g_str_hash, g_str_equal, kry_free_minimal, (GDestroyNotify) kryPrefSection::FreeStatic); } /* * Saves the given configuration to the application configuration file. * On success returns TRUE * On failure returns FALSE and sets the error string. The error string must be freed. * * prefs: preferences handle * error: pointer to a string that will be updated on error */ gboolean kryPrefManager::Save() { return this->Save(this->m_filename); } /* * Saves the given configuration to the specified file. * On success returns TRUE. * On failure returns FALSE and sets the error string. The error string must be freed. * * prefs: preferences handle * file: the filename to save the preferences to * error: pointer to a string that will be updated on error */ gboolean kryPrefManager::Save(char *filename) { FILE *fh; char *buffer; kryPrefValue *pref; fh = fopen(filename, "w"); if(!fh) return FALSE; kryListIterator iter; this->m_sections->GetKeysIterator(&iter); while(char *section_name = iter.GetNext()) { kryPrefSection *section = this->m_sections->Lookup(section_name); buffer = kry_strdup_printf(KRY_LOC "[%s]\n", section_name); fwrite((void *) buffer, strlen(buffer), 1, fh); kry_free(buffer); // Iterate through the key/value pairs kryListIterator iter; section->GetKeysIterator(&iter); char *key; while((key = iter.GetNext())) { pref = section->Lookup(key); if(pref->GetType() == kryPrefValue::PREF_STRING) { buffer = kry_strdup_printf(KRY_LOC "%s: %s\n", key, (char *) pref->GetData()); fwrite((void *) buffer, strlen(buffer), 1, fh); kry_free(buffer); } else if(pref->GetType() == kryPrefValue::PREF_LIST) { GList *list_sub = (GList *) pref->GetData(); while(list_sub) { pref = (kryPrefValue *) list_sub->data; buffer = kry_strdup_printf(KRY_LOC "%s: %s\n", key, (char *) pref->GetData()); fwrite((void *) buffer, strlen(buffer), 1, fh); kry_free(buffer); list_sub = list_sub -> next; } } } buffer = kry_strdup("\n"); fwrite(buffer, strlen(buffer), 1, fh); kry_free(buffer); } fclose(fh); if(!this->m_filename || strcmp(this->m_filename, filename)) this->SetFilename(filename); return TRUE; } /* * Unsets the given key in the given section. * * prefs: preferences handle * section_str: the section to modify * key: the key to unset */ void kryPrefManager::Remove(char *section_name, char *key) { kryPrefSection *section = this->m_sections->Lookup(section_name); kryPrefValue *pref; if(!section) return; pref = section->Lookup(key); if(!pref) return; section->Remove(key); } void kryPrefManager::Remove(char *key) { if(!this->GetSection()) { g_warning("No section name set!"); return; } this->Remove(this->GetSection(), key); } /* * Returns the given setting as an integer. * Returns the default value if the setting is not present. * * prefs: preferences handle * section_str: the section to modify * key: the key to retrieve * defval: the value to return if the key is not present */ int kryPrefManager::GetInt(char *section_name, char *key, int defval) { char *val = this->GetString(section_name, key); int rv; if(!val) return defval; // TODO: error checking rv = atoi(val); return rv; } int kryPrefManager::GetInt(char *key, int defval) { if(!this->GetSection()) { g_warning("No section name set!"); return defval; } return this->GetInt(this->GetSection(), key, defval); } /* * Returns the given setting or NULL if it's not present. * The returned string must not be freed. * * prefs: preferences handle * section_str: the section to modify * key: the key to retrieve */ char *kryPrefManager::GetString(char *section_name, char *key) { kryPrefSection *section = this->m_sections->Lookup(section_name); kryPrefValue *pref; if(!section) return NULL; pref = section->Lookup(key); if(!pref) return NULL; return (char *) pref->GetData(); } char *kryPrefManager::GetString(char *key) { if(!this->GetSection()) { g_warning("No section name set!"); return NULL; } return this->GetString(this->GetSection(), key); } /* * Returns the given setting as a list or NULL if it's not present. * The returned list is a list of 'pref' structs. * If the given pref is not a list, but a string, the pref is converted * into a list and it can no longer be retrieved with the prefs_get function. * * prefs: preferences handle * section_str: the section to modify * key: the key to retrieve */ GList *kryPrefManager::GetList(char *section_name, char *key) { kryPrefSection *section = this->m_sections->Lookup(section_name); kryPrefValue *pref; if(!section) return NULL; pref = section->Lookup(key); if(!pref) return NULL; /* We don't know which pref is supposed to be a string, and integer, or a list so if we're asked for a list but he pref is a string, we convert this pref into a list. (We assume this pref is meant to be a list.) */ if(pref->GetType() == kryPrefValue::PREF_STRING) { kryPrefValue *new_pref = new kryPrefValue(); new_pref->SetType(kryPrefValue::PREF_STRING); new_pref->SetData(kry_strdup((const gchar *) pref->GetData())); kry_free(pref->GetData()); pref->SetType(kryPrefValue::PREF_LIST); pref->SetData(g_list_append(NULL, new_pref)); } return (GList *) pref->GetData(); } GList *kryPrefManager::GetList(char *key) { if(!this->GetSection()) { g_warning("No section name set!"); return NULL; } return this->GetList(this->GetSection(), key); } /* * Adds a new section to to preference handle and returns the newly created section. * This function is meant to be used when the section is known not to exist, * so it does not check for the existence of a section with the given name. * * prefs: preferences handle * section_str: name of section to add */ kryPrefSection *kryPrefManager::AddSection(char *section_name) { kryPrefSection *section = new kryPrefSection(); section->SetName(kry_strdup(section_name)); #ifdef _DEBUG if(this->m_sections->Lookup(section_name)) g_warning("Section '%s' already exists in prefs", section_name); #endif char *section_copy = kry_strdup(section_name); this->m_sections->Insert(section_copy, section); return section; } /* * Sets the given setting to the given string. * If the setting already exists, it is replaced. * * prefs: preferences handle * section_str: the section to modify * key: the key to set * value: the value to set key to */ void kryPrefManager::SetString(char *section_name, char *key, char *value) { kryPrefSection *section = this->m_sections->Lookup(section_name); kryPrefValue *pref; #ifndef _DEBUG if(!key) { g_warning("empty key passed to prefs_set"); return; } #endif /* If the section doesn't exist, we need to create it */ if(!section) section = this->AddSection(section_name); pref = new kryPrefValue(); pref->SetData(kry_strdup(value)); pref->SetType(kryPrefValue::PREF_STRING); char *key_copy = kry_strdup(key); section->Replace(key_copy, pref); } void kryPrefManager::SetString(char *key, char *value) { if(!this->GetSection()) { g_warning("No section name set!"); return; } this->SetString(this->GetSection(), key, value); } void kryPrefManager::SetInt(char *section_name, char *key, int val) { char *val_str = kry_strdup_printf(KRY_LOC "%d", val); this->SetString(section_name, key, val_str); kry_free(val_str); } void kryPrefManager::SetInt(char *key, int val) { if(!this->GetSection()) { g_warning("No section name set!"); return; } this->SetInt(this->GetSection(), key, val); } /* * Adds a string value to the given list. * If there's no such key, a new list is created. * * prefs: preferences handle * section_str: the section to modify * key: the key under which the list is stored * value: the value to add to the list */ void kryPrefManager::SetListAddString(char *section_name, char *key, char *value) { kryPrefSection *section = this->m_sections->Lookup(section_name); kryPrefValue *pref; kryPrefValue *old_pref; /* If the section doesn't exist, we must create it */ if(!section) section = this->AddSection(section_name); pref = new kryPrefValue(); pref->SetData(kry_strdup(value)); /* If the key doesn't exist, we simply insert the value as a STRING pref */ if((old_pref = section->Lookup(key)) == NULL) { pref->SetType(kryPrefValue::PREF_STRING); char *key_copy = kry_strdup(key); section->Replace(key_copy, pref); } /* If it exists and is a string, we must convert the STRING pref into a LIST pref. */ else if(old_pref->GetType() == kryPrefValue::PREF_STRING) { kryPrefValue *pref_copy = new kryPrefValue(); GList *list = NULL; *pref_copy = *old_pref; old_pref->SetType(kryPrefValue::PREF_LIST); list = g_list_append(list, pref_copy); old_pref->SetData(list); pref->SetType(kryPrefValue::PREF_STRING); list = g_list_append(list, pref); } /* If it's already a list, then we just append the new value */ else if(old_pref->GetType() == kryPrefValue::PREF_LIST) { pref->SetType(kryPrefValue::PREF_STRING); old_pref->SetData(g_list_append((GList *) old_pref->GetData(), pref)); } } void kryPrefManager::SetListAddString(char *key, char *value) { this->SetListAddString(this->GetSection(), key, value); } gboolean kryPrefManager::Load() { return this->Load(this->m_filename); } /* * Loads preferences from the specified file. * * If the file cannot be opened, returns an empty handle. * TODO: Error handling */ gboolean kryPrefManager::Load(char *filename) { char *buffer = NULL; char *ptr; int len; kryPrefSection *section_cur = NULL; gboolean in_section = FALSE; FILE *fh; kryTextFileReader *reader = new kryTextFileReader(filename); fh = fopen(filename, "r"); if(!reader->Open()) { delete reader; return FALSE; } for(;;) { if(buffer) kry_free(buffer); buffer = reader->GetLine(); if(!buffer) break; ptr = buffer; len = strlen(buffer); /* Line matches section definition */ if(len > 3 && buffer[0] == '[' && buffer[len - 1] == ']') { char *section_str; ptr++; section_str = string_read_token(&ptr, ']'); if(!section_str) { // should never get here g_warning("Error extraction section name"); continue; } /* If the section doesn't exist, we create it */ if(!(section_cur = this->m_sections->Lookup(section_str))) section_cur = this->AddSection(section_str); in_section = TRUE; kry_free(section_str); continue; } /* Key value pair */ if(in_section) { char *key = string_read_token(&ptr, ':'); char *value; if(!key) /* Invalid line (no ':') */ continue; ptr = string_ignore_whitespce(ptr); value = string_read_token_end(&ptr); if(!value) { kry_free(key); continue; } this->SetListAddString(section_cur->GetName(), key, value); kry_free(key); kry_free(value); } } if(buffer) kry_free(buffer); this->SetFilename(filename); reader->Close(); delete reader; return TRUE; } void kryPrefManager::SetFilename(char *filename) { if(this->m_filename != NULL && !strcmp(this->m_filename, filename)) return; if(this->m_filename) kry_free(filename); this->m_filename = kry_strdup(filename); } void kryPrefManager::PushSection(char *section) { char *section_copy = kry_strdup(section); this->m_section_current = section_copy; this->m_section_stack = g_list_prepend(this->m_section_stack, section_copy); } char *kryPrefManager::GetSection() { if(!this->m_section_stack) { g_warning("no sections on stack"); return NULL; } return this->m_section_current; } void kryPrefManager::PopSection() { if(!this->m_section_stack) return; this->m_section_stack = g_list_delete_link(this->m_section_stack, this->m_section_stack); if(this->m_section_stack) this->m_section_current = (char *) this->m_section_stack->data; else this->m_section_current = NULL; } void kryPrefManager::SetSection(char *section) { while(this->m_section_stack) this->PopSection(); this->PushSection(section); }