File indexing completed on 2024-04-28 16:08:34

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2017 by Linuxstopmotion contributors;              *
0003  *   see the AUTHORS file for details.                                     *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0019  ***************************************************************************/
0020 
0021 #include "preferencestool.h"
0022 #include "logger.h"
0023 #include "uiexception.h"
0024 
0025 #include <string.h>
0026 #include <stdio.h>
0027 #include <stdlib.h>
0028 #include <iostream>
0029 #include <sys/file.h>
0030 
0031 #include <libxml/parser.h>
0032 
0033 using namespace std;
0034 
0035 
0036 PreferencesTool* PreferencesTool::preferencesTool = 0;
0037 
0038 
0039 class XmlProp {
0040     xmlChar* p;
0041 public:
0042     XmlProp(const _xmlNode* node, const char* name) : p(0) {
0043         p = xmlGetProp(node, BAD_CAST name);
0044     }
0045     ~XmlProp() {
0046         xmlFree(p);
0047     }
0048     const char* value() const {
0049         return reinterpret_cast<char*>(p);
0050     }
0051 };
0052 
0053 
0054 PreferencesTool::PreferencesTool() :
0055         doc(0), dtd(0), rootNode(0), preferences(0), versionNode(0),
0056         dirty(false), preferencesFile(0) {
0057     LIBXML_TEST_VERSION;
0058 }
0059 
0060 
0061 PreferencesTool::~PreferencesTool() {
0062     cleanTree();
0063     delete[] preferencesFile;
0064 }
0065 
0066 
0067 PreferencesTool* PreferencesTool::get() {
0068     if (preferencesTool == NULL) {
0069         preferencesTool = new PreferencesTool();
0070     }
0071     return preferencesTool;
0072 }
0073 
0074 void PreferencesTool::setSavePath(const char* s, bool wantSave) {
0075     unsigned long length = strlen(s) + 1;
0076     preferencesFile = new char[length];
0077     strncpy(preferencesFile, s, length);
0078     if (wantSave)
0079         dirty = true;
0080 }
0081 
0082 bool PreferencesTool::load(const char* filePath) {
0083     if (preferencesFile) {
0084         delete [] preferencesFile;
0085         preferencesFile = NULL;
0086     }
0087     
0088     if (doc) {
0089         cleanTree();
0090     }
0091     dirty = false;
0092 
0093     doc = xmlReadFile(filePath, NULL, 0);
0094     if (!doc)
0095         return false;
0096 
0097     rootNode = xmlDocGetRootElement(doc);
0098     if (!doc)
0099         return false;
0100 
0101     xmlNode* node = rootNode->children;
0102     for (; node; node = node->next) {
0103         if (node->type == XML_ELEMENT_NODE) {
0104             if (xmlStrEqual(node->name, BAD_CAST "version")) {
0105                 versionNode = node;
0106             }
0107         }
0108         if (node->type == XML_ELEMENT_NODE) {
0109             if (xmlStrEqual(node->name, BAD_CAST "preferences")) {
0110                 preferences = node;
0111             }
0112         }
0113     }
0114     if (!preferences || !versionNode) {
0115         Logger::get().logWarning("Error while parsing preferences file");
0116         return false;
0117     }
0118     return true;
0119 }
0120 
0121 void PreferencesTool::setDefaultPreferences(const char* version) {
0122     delete[] preferencesFile;
0123     preferencesFile = 0;
0124     dirty = true;
0125 
0126     doc = xmlNewDoc(BAD_CAST "1.0");
0127     dtd = xmlCreateIntSubset(doc, BAD_CAST "root", NULL, NULL);
0128 
0129     rootNode = xmlNewNode(NULL, BAD_CAST "root");
0130     xmlNewProp(rootNode, BAD_CAST "xml:lang", BAD_CAST "en");
0131     xmlNewProp(rootNode, BAD_CAST "title", BAD_CAST "Preferences");
0132     xmlDocSetRootElement(doc, rootNode);
0133 
0134     versionNode = xmlNewChild(rootNode, NULL, BAD_CAST "version", NULL);
0135     xmlNewProp(versionNode, BAD_CAST "version", BAD_CAST version);
0136     preferences = xmlNewChild(rootNode, NULL, BAD_CAST "preferences", NULL);
0137 }
0138 
0139 bool PreferencesTool::isVersion(const char* version) {
0140     XmlProp v(versionNode, "version");
0141     const char* versionLoaded = v.value();
0142     return versionLoaded && strcmp(versionLoaded, version) == 0;
0143 }
0144 
0145 
0146 void PreferencesTool::setVersion(const char* version) {
0147     checkInitialized();
0148     xmlSetProp(versionNode, BAD_CAST "version", BAD_CAST version);
0149     dirty = true;
0150 }
0151 
0152 
0153 void PreferencesTool::setPreference(const char* key, const char* attribute) {
0154     checkInitialized();
0155     xmlNodePtr node = NULL;
0156     node = findNode(key);
0157     
0158     if (node == NULL) {
0159         node = xmlNewChild(preferences, NULL, BAD_CAST "pref", NULL);
0160         xmlNewProp(node, BAD_CAST "key", BAD_CAST key);
0161         xmlNewProp(node, BAD_CAST "attribute", BAD_CAST attribute);
0162         dirty = true;
0163     } else {
0164         const char* currentValue = (const char*) xmlGetProp(node, BAD_CAST "attribute");
0165         if (strcmp(currentValue, attribute) != 0) {
0166             xmlSetProp(node, BAD_CAST "attribute", BAD_CAST attribute);
0167             dirty = true;
0168         }
0169     }
0170 }
0171 
0172 
0173 void PreferencesTool::setPreference(const char* key, const int attribute) {
0174     char tmp[11] = {0};
0175     snprintf(tmp, 11, "%d", attribute);
0176     setPreference(key, tmp);
0177 }
0178 
0179 
0180 const char* PreferencesTool::getPreference(const char* key) {
0181     checkInitialized();
0182     xmlNode *node = findNode(key);
0183     return (const char*)xmlGetProp(node, BAD_CAST "attribute");
0184 }
0185 
0186 
0187 int PreferencesTool::getPreference(const char* key, const int defaultValue) {
0188     checkInitialized();
0189     xmlNode *node = findNode(key);
0190     if (!node) {
0191         return defaultValue;
0192     }
0193     xmlChar *tmp = xmlGetProp(node, BAD_CAST "attribute");
0194     int ret = atoi((char*)tmp);
0195     xmlFree(tmp);
0196     return ret;
0197 }
0198 
0199 
0200 void PreferencesTool::removePreference(const char* key) {
0201     checkInitialized();
0202     xmlNode *node = findNode(key);
0203     if (node != NULL) {
0204         xmlUnlinkNode(node);
0205         xmlFreeNode(node);
0206         dirty = true;
0207     }
0208 }
0209 
0210 
0211 // looks after a locked, writable file
0212 class WriteableFile {
0213     FILE* f;
0214 public:
0215     WriteableFile() : f(0) {
0216     }
0217     ~WriteableFile() {
0218         if (f)
0219             fclose(f);
0220     }
0221     bool open(const char* filename) {
0222         f = fopen(filename, "w");
0223         if (!f)
0224             return false;
0225         flock(fileno(f), LOCK_EX);
0226         return true;
0227     }
0228     FILE* file() const {
0229         return f;
0230     }
0231 };
0232 
0233 void PreferencesTool::flush() {
0234     if (dirty) {
0235         WriteableFile prefs;
0236         if (!prefs.open(preferencesFile)
0237                 || xmlDocFormatDump(prefs.file(), doc, 1) == -1) {
0238             throw UiException(UiException::failedToWriteToPreferencesFile,
0239                     preferencesFile);
0240         }
0241         dirty = false;
0242     }
0243 }
0244 
0245 
0246 xmlNodePtr PreferencesTool::findNode(const char * key) {
0247     //Search through the preferences for the element with a key which
0248     //equals the key parameter.
0249     xmlNode *node = preferences->children;
0250     for (; node; node = node->next) {
0251         if (node->type == XML_ELEMENT_NODE) {
0252             xmlChar *prop = xmlGetProp(node, BAD_CAST "key");
0253             int found = xmlStrEqual(prop, BAD_CAST key);
0254             xmlFree(prop);
0255             if (found)
0256                 return node;
0257         }
0258     }
0259     return 0;
0260 }
0261 
0262 
0263 void PreferencesTool::checkInitialized() {
0264     if (doc == NULL) {
0265         Logger::get().logFatal("A preferencesfile has to be specified before "
0266                 "using the PreferencesTool.");
0267         exit(1);
0268     }
0269 }
0270 
0271 
0272 void PreferencesTool::cleanTree() {
0273     xmlFreeDoc(doc);
0274     xmlCleanupParser();
0275     
0276     doc             = NULL;
0277     dtd             = NULL;
0278     rootNode        = NULL;
0279     preferences     = NULL;
0280 }
0281 
0282 Preference::Preference(const char* key) : val(0), owns(false) {
0283     val = PreferencesTool::get()->getPreference(key);
0284     if (val)
0285         owns = true;
0286 }
0287 
0288 Preference::Preference(const char* key, const char* defaultValue)
0289     : val(0), owns(false) {
0290     val = PreferencesTool::get()->getPreference(key);
0291     if (val) {
0292         owns = true;
0293     } else {
0294         val = defaultValue;
0295     }
0296 }
0297 
0298 Preference::~Preference() {
0299     if (owns)
0300         xmlFree((xmlChar*)val);
0301 }
0302 
0303 const char* Preference::get() const {
0304     return val;
0305 }
0306 
0307 bool Preference::equals(const char* str) {
0308     if (str) {
0309         return val? 0 == strcmp(val, str) : false;
0310     } else {
0311         return !val;
0312     }
0313 }