1 /* gobby - A GTKmm driven libobby client
2 * Copyright (C) 2005, 2006 0x539 dev group
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19 // For mkdir / CreateDirectory
20 #ifdef WIN32
21 #include <windows.h>
22 #else
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <cerrno>
26 #endif
27
28 #include <cstring>
29 #include <stdexcept>
30
31 #include <glibmm/miscutils.h>
32 #include <glibmm/fileutils.h>
33 #include <glibmm/exception.h>
34 #include <libxml++/parsers/domparser.h>
35 #include <libxml++/exceptions/exception.h>
36
37 #include <glib/gstdio.h>
38
39 #include "config.hpp"
40
41 namespace
42 {
43 // Creates a new directory
create_directory(const char * path)44 void create_directory(const char* path)
45 {
46 #ifdef WIN32
47 if(CreateDirectoryA(path, NULL) == FALSE)
48 {
49 LPVOID msgbuf;
50 DWORD err = GetLastError();
51
52 FormatMessageA(
53 FORMAT_MESSAGE_ALLOCATE_BUFFER |
54 FORMAT_MESSAGE_FROM_SYSTEM,
55 NULL,
56 err,
57 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
58 reinterpret_cast<LPSTR>(&msgbuf),
59 0,
60 NULL
61 );
62
63 std::string error_message = static_cast<LPSTR>(msgbuf);
64 LocalFree(msgbuf);
65
66 // TODO: Convert to UTF-8?
67
68 throw Gobby::Config::Error(
69 Gobby::Config::Error::PATH_CREATION_FAILED,
70 "Could not create directory " +
71 std::string(path) + ": " + error_message
72 );
73 }
74 #else
75 if(mkdir(path, 0755) == -1)
76 {
77 throw Gobby::Config::Error(
78 Gobby::Config::Error::PATH_CREATION_FAILED,
79 "Could not create directory " +
80 std::string(path) + ": " + strerror(errno)
81 );
82 }
83 #endif
84 }
85
create_path_to(const std::string & to)86 void create_path_to(const std::string& to)
87 {
88 // Directory exists, nothing to do
89 if(Glib::file_test(to, Glib::FILE_TEST_IS_DIR) )
90 return;
91
92 // Find path to the directory to create
93 Glib::ustring path_to = Glib::path_get_dirname(to);
94
95 // Create this path, if it doesn't exists
96 create_path_to(path_to);
97
98 // Create new directory
99 create_directory(to.c_str() );
100 }
101
102 template<typename Map>
ptrmap_find(const Map & map,const typename Map::key_type & key)103 typename Map::mapped_type ptrmap_find(const Map& map,
104 const typename Map::key_type& key)
105 {
106 typename Map::const_iterator iter = map.find(key);
107 if(iter == map.end() ) return NULL;
108 return iter->second;
109 }
110 }
111
Error(Code error_code,const Glib::ustring & error_message)112 Gobby::Config::Error::Error(Code error_code,
113 const Glib::ustring& error_message):
114 Glib::Error(
115 g_quark_from_static_string("GOBBY_CONFIG_ERROR"),
116 static_cast<int>(error_code),
117 error_message
118 )
119 {
120 }
121
code() const122 Gobby::Config::Error::Code Gobby::Config::Error::code() const
123 {
124 return static_cast<Code>(gobject_->code);
125 }
126
Entry(const Glib::ustring & name)127 Gobby::Config::Entry::Entry(const Glib::ustring& name):
128 m_name(name)
129 {
130 }
131
get_name() const132 const Glib::ustring& Gobby::Config::Entry::get_name() const
133 {
134 return m_name;
135 }
136
ParentEntry(const Glib::ustring & name)137 Gobby::Config::ParentEntry::ParentEntry(const Glib::ustring& name):
138 Entry(name)
139 {
140 }
141
ParentEntry(const xmlpp::Element & elem)142 Gobby::Config::ParentEntry::ParentEntry(const xmlpp::Element& elem):
143 Entry(elem.get_name() )
144 {
145 xmlpp::Node::NodeList list = elem.get_children();
146 for(xmlpp::Node::NodeList::iterator iter = list.begin();
147 iter != list.end();
148 ++ iter)
149 {
150 xmlpp::Element* child = dynamic_cast<xmlpp::Element*>(*iter);
151 if(child == NULL) continue;
152
153 if(child->get_child_text() &&
154 !child->get_child_text()->is_white_space())
155 {
156 ValueEntry* entry = new TypedValueEntry<Glib::ustring>(
157 *child
158 );
159
160 m_map[child->get_name()] = entry;
161 }
162 else
163 {
164 m_map[child->get_name()] = new ParentEntry(*child);
165 }
166 }
167 }
168
~ParentEntry()169 Gobby::Config::ParentEntry::~ParentEntry()
170 {
171 for(map_type::iterator iter = m_map.begin();
172 iter != m_map.end();
173 ++ iter)
174 {
175 delete iter->second;
176 }
177 }
178
save(xmlpp::Element & elem) const179 void Gobby::Config::ParentEntry::save(xmlpp::Element& elem) const
180 {
181 for(map_type::const_iterator iter = m_map.begin();
182 iter != m_map.end();
183 ++ iter)
184 {
185 Entry* entry = iter->second;
186 xmlpp::Element* child = elem.add_child(entry->get_name() );
187 entry->save(*child);
188 }
189 }
190
191 Gobby::Config::Entry* Gobby::Config::ParentEntry::
get_child(const Glib::ustring & name)192 get_child(const Glib::ustring& name)
193 {
194 return ptrmap_find(m_map, name);
195 }
196
197 const Gobby::Config::Entry* Gobby::Config::ParentEntry::
get_child(const Glib::ustring & name) const198 get_child(const Glib::ustring& name) const
199 {
200 return ptrmap_find(m_map, name);
201 }
202
203 Gobby::Config::ParentEntry* Gobby::Config::ParentEntry::
get_parent_child(const Glib::ustring & name)204 get_parent_child(const Glib::ustring& name)
205 {
206 return dynamic_cast<ParentEntry*>(get_child(name) );
207 }
208
209 const Gobby::Config::ParentEntry* Gobby::Config::ParentEntry::
get_parent_child(const Glib::ustring & name) const210 get_parent_child(const Glib::ustring& name) const
211 {
212 return dynamic_cast<const ParentEntry*>(get_child(name) );
213 }
214
215 Gobby::Config::ValueEntry* Gobby::Config::ParentEntry::
get_value_child(const Glib::ustring & name)216 get_value_child(const Glib::ustring& name)
217 {
218 return dynamic_cast<ValueEntry*>(get_child(name) );
219 }
220
221 const Gobby::Config::ValueEntry* Gobby::Config::ParentEntry::
get_value_child(const Glib::ustring & name) const222 get_value_child(const Glib::ustring& name) const
223 {
224 return dynamic_cast<const ValueEntry*>(get_child(name) );
225 }
226
227 Gobby::Config::ParentEntry& Gobby::Config::ParentEntry::
operator [](const Glib::ustring & name)228 operator[](const Glib::ustring& name)
229 {
230 ParentEntry* entry = get_parent_child(name);
231 if(entry != NULL) return *entry;
232 return set_parent(name);
233 }
234
235 Gobby::Config::ParentEntry& Gobby::Config::ParentEntry::
set_parent(const Glib::ustring & name)236 set_parent(const Glib::ustring& name)
237 {
238 Entry* entry = get_child(name);
239 if(entry != NULL) delete entry;
240
241 ParentEntry* child = new ParentEntry(name);
242 m_map[name] = child;
243 return *child;
244 }
245
begin()246 Gobby::Config::ParentEntry::iterator Gobby::Config::ParentEntry::begin()
247 {
248 return iterator(m_map.begin() );
249 }
250
251 Gobby::Config::ParentEntry::const_iterator Gobby::Config::ParentEntry::
begin() const252 begin() const
253 {
254 return const_iterator(m_map.begin() );
255 }
256
end()257 Gobby::Config::ParentEntry::iterator Gobby::Config::ParentEntry::end()
258 {
259 return iterator(m_map.end() );
260 }
261
262 Gobby::Config::ParentEntry::const_iterator Gobby::Config::ParentEntry::
end() const263 end() const
264 {
265 return const_iterator(m_map.end() );
266 }
267
Config(const Glib::ustring & file,const Glib::ustring & old_file)268 Gobby::Config::Config(const Glib::ustring& file,
269 const Glib::ustring& old_file):
270 m_filename(file)
271 {
272 xmlpp::DomParser parser;
273 const Glib::ustring* actual_file = &file;
274
275 if(!Glib::file_test(file, Glib::FILE_TEST_IS_REGULAR))
276 {
277 if (Glib::file_test(old_file, Glib::FILE_TEST_IS_REGULAR))
278 {
279 actual_file = &old_file;
280 }
281 else
282 {
283 m_root.reset(new ParentEntry("gobby_config") );
284 return;
285 }
286 }
287
288 try
289 {
290 parser.parse_file(*actual_file);
291 if (actual_file != &file)
292 g_unlink(old_file.c_str());
293 }
294 catch(xmlpp::exception& e)
295 {
296 // Empty config
297 m_root.reset(new ParentEntry("gobby_config") );
298 return;
299 }
300
301 xmlpp::Document* document = parser.get_document();
302 if(document == NULL)
303 {
304 m_root.reset(new ParentEntry("gobby_config") );
305 return;
306 }
307
308 xmlpp::Element* root = document->get_root_node();
309
310 // Config is present, but contains no root node
311 if(root == NULL)
312 {
313 m_root.reset(new ParentEntry("gobby_config") );
314 return;
315 }
316
317 m_root.reset(new ParentEntry(*root) );
318 }
319
~Config()320 Gobby::Config::~Config()
321 {
322 xmlpp::Document document;
323 xmlpp::Element* root = document.create_root_node("gobby_config");
324 m_root->save(*root);
325
326 try
327 {
328 Glib::ustring dirname = Glib::path_get_dirname(m_filename);
329 create_path_to(dirname);
330
331 document.write_to_file_formatted(m_filename, "UTF-8");
332 }
333 catch(Glib::Exception& e)
334 {
335 g_warning("Could not write config file: %s", e.what().c_str() );
336 }
337 catch(std::exception& e)
338 {
339 g_warning("Could not write config file: %s", e.what() );
340 }
341 }
342
get_root()343 Gobby::Config::ParentEntry& Gobby::Config::get_root()
344 {
345 return *m_root;
346 }
347
get_root() const348 const Gobby::Config::ParentEntry& Gobby::Config::get_root() const
349 {
350 return *m_root;
351 }
352
353 std::string serialise::default_context_to<Gdk::Color>::
to_string(const data_type & from) const354 to_string(const data_type& from) const
355 {
356 unsigned int red = from.get_red() * 255 / 65535;
357 unsigned int green = from.get_green() * 255 / 65535;
358 unsigned int blue = from.get_blue() * 255 / 65535;
359
360 std::stringstream stream;
361 stream << std::hex << ( (red << 16) | (green << 8) | blue);
362 return stream.str();
363 }
364
365 serialise::default_context_from<Gdk::Color>::data_type
366 serialise::default_context_from<Gdk::Color>::
from_string(const std::string & from) const367 from_string(const std::string& from) const
368 {
369 unsigned int rgb_color;
370 std::stringstream stream(from);
371 stream >> std::hex >> rgb_color;
372
373 if(stream.bad() )
374 {
375 throw conversion_error(
376 from + " should be hexadecimal color triple"
377 );
378 }
379
380 Gdk::Color color;
381 color.set_red( ((rgb_color >> 16) & 0xff) * 65535 / 255);
382 color.set_green( ((rgb_color >> 8) & 0xff) * 65535 / 255);
383 color.set_blue( ((rgb_color) & 0xff) * 65535 / 255);
384 return color;
385 }
386
387 std::string serialise::default_context_to<Glib::ustring>::
to_string(const data_type & from) const388 to_string(const data_type& from) const
389 {
390 return from;
391 }
392
393 serialise::default_context_from<Glib::ustring>::data_type
394 serialise::default_context_from<Glib::ustring>::
from_string(const std::string & from) const395 from_string(const std::string& from) const
396 {
397 return from;
398 }
399