1 /*****************************************************************************\
3 * Name : ini_configurator *
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 2000-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "ini_configurator.h"
16 #include "application_configuration.h"
17 #include "variable_tokenizer.h"
19 #include <application/windoze_helper.h>
20 #include <basis/astring.h>
21 #include <basis/environment.h>
22 #include <basis/functions.h>
23 #include <basis/mutex.h>
24 #include <basis/utf_conversion.h>
25 #include <filesystem/byte_filer.h>
26 #include <filesystem/directory.h>
27 #include <filesystem/filename.h>
28 #include <structures/static_memory_gremlin.h>
29 #include <structures/string_array.h>
30 #include <structures/string_table.h>
31 #include <structures/symbol_table.h>
36 #define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s())
38 using namespace basis;
39 using namespace filesystem;
40 using namespace structures;
42 namespace configuration {
44 //#define DEBUG_INI_CONFIGURATOR
45 // uncomment for noisy version.
47 const int MAXIMUM_LINE_INI_CONFIG = 16384;
49 // a default we hope never to see in an ini file.
50 SAFE_STATIC_CONST(astring, ini_configurator::ini_str_fake_default, ("NoTomatoesNorPotatoesNorQuayle"))
52 ini_configurator::ini_configurator(const astring &ini_filename,
53 treatment_of_defaults behavior, file_location_default where)
54 : configurator(behavior),
55 _ini_name(new filename),
56 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
57 _parser(new ini_parser("", behavior)),
62 FUNCDEF("constructor");
63 name(ini_filename); // set name properly.
64 LOG(astring("calculated ini name as: '") + _ini_name->raw() + "'");
67 ini_configurator::~ini_configurator()
70 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
75 astring ini_configurator::name() const { return _ini_name->raw(); }
77 void ini_configurator::refresh()
79 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
82 _parser = new ini_parser("", behavior());
86 void ini_configurator::name(const astring &name)
90 bool use_appdir = true;
91 // true if we should put files where programs start for those filenames
92 // that don't include a directory name.
93 if (_where == OS_DIRECTORY) use_appdir = false;
94 if (_where == ALL_USERS_DIRECTORY) use_appdir = false;
98 // we must create the filename if they specified no directory at all.
99 if (!_ini_name->had_directory()) {
101 // this is needed in case there is an ini right with the file; our
102 // standard is to check there first.
103 *_ini_name = filename(application_configuration::application_directory(),
104 _ini_name->basename());
105 } else if (!use_appdir && (_where == ALL_USERS_DIRECTORY) ) {
106 // when the location default is all users, we get that from the
107 // environment. for the OS dir choice, we leave out the path entirely.
108 directory::make_directory(environment::get("ALLUSERSPROFILE")
109 + "/" + application_configuration::software_product_name());
110 *_ini_name = filename(environment::get("ALLUSERSPROFILE")
111 + "/" + application_configuration::software_product_name(),
112 _ini_name->basename());
115 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
116 // read in the file's contents.
121 void ini_configurator::sections(string_array &list)
123 list = string_array();
124 // open our ini file directly as a file.
125 byte_filer section8(*_ini_name, "rb");
126 if (!section8.good()) return; // not a healthy file.
128 // iterate through the lines of the ini file and see if we can't find a
129 // bunch of section names.
130 while (section8.read(line_found, MAXIMUM_LINE_INI_CONFIG) > 0) {
131 // is the line in the format "^[ \t]*\[\([^\]]+\)\].*$" ?
132 // if it is in that format, we add the matched \1 into our list.
133 line_found.strip_white_spaces();
134 if (line_found[0] != '[') continue; // no opening bracket. skip line.
135 line_found.zap(0, 0); // toss opening bracket.
136 int close_brack_indy = line_found.find(']');
137 if (negative(close_brack_indy)) continue; // no closing bracket.
138 line_found.zap(close_brack_indy, line_found.end());
143 //hmmm: refactor section_exists to use the sections call, if it's faser?
144 bool ini_configurator::section_exists(const astring §ion)
148 // heavy-weight call here...
149 return get_section(section, infos);
151 return _parser->section_exists(section);
155 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
156 void ini_configurator::read_ini_file()
158 #ifdef DEBUG_INI_CONFIGURATOR
159 FUNCDEF("read_ini_file");
161 _parser->reset(""); // clear out our current contents.
163 bool open_ret = ini_file.open(*_ini_name, "rb"); // simple reading.
164 #ifdef DEBUG_INI_CONFIGURATOR
165 if (!open_ret) LOG(astring("failed to open ini file: ") + *_ini_name);
166 if (!ini_file.good()) LOG(astring("ini file not good: ") + *_ini_name);
168 if (!open_ret || !ini_file.good()) {
171 int file_size = ini_file.length(); // get the file length.
173 astring contents(' ', file_size + 3);
174 int bytes_read = ini_file.read((abyte *)contents.observe(), file_size);
175 contents.zap(bytes_read + 1, contents.end());
176 _parser->reset(contents);
179 void ini_configurator::write_ini_file()
181 #ifdef DEBUG_INI_CONFIGURATOR
182 FUNCDEF("write_ini_file");
185 //hmmm: just set dirty flag and use that for deciding whether to write.
186 //hmmm: future version, have a thread scheduled to write.
188 // open filer with new mode for cleaning.
190 bool open_ret = ini_file.open(*_ini_name, "wb");
191 // open the file for binary read/write and drop previous contents.
192 #ifdef DEBUG_INI_CONFIGURATOR
193 if (!open_ret) LOG(astring("failed to open ini file: ") + *_ini_name);
194 if (!ini_file.good()) LOG(astring("ini file not good: ") + *_ini_name);
196 if (!open_ret || !ini_file.good()) return; // failure.
198 // output table's contents to text.
200 _parser->restate(text, _add_spaces);
201 ini_file.write((abyte *)text.observe(), text.length());
205 bool ini_configurator::delete_section(const astring §ion)
208 return put_profile_string(section, "", "");
211 bool to_return = _parser->delete_section(section);
212 // schedule the file to write.
218 bool ini_configurator::delete_entry(const astring §ion, const astring &ent)
221 return put_profile_string(section, ent, "");
224 bool to_return = _parser->delete_entry(section, ent);
225 // schedule the file to write.
231 bool ini_configurator::put(const astring §ion, const astring &entry,
232 const astring &to_store)
235 if (!to_store.length()) return delete_entry(section, entry);
236 else if (!entry.length()) return delete_section(section);
237 else if (!section.length()) return false;
239 return put_profile_string(section, entry, to_store);
242 bool to_return = _parser->put(section, entry, to_store);
243 // schedule file write.
249 bool ini_configurator::get(const astring §ion, const astring &entry,
252 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
253 return _parser->get(section, entry, found);
255 flexichar temp_buffer[MAXIMUM_LINE_INI_CONFIG];
257 get_profile_string(section, entry, ini_str_fake_default(),
258 temp_buffer, MAXIMUM_LINE_INI_CONFIG - 1);
259 found = from_unicode_temp(temp_buffer);
260 return !(ini_str_fake_default() == found);
264 bool ini_configurator::get_section(const astring §ion, string_table &info)
266 FUNCDEF("get_section");
267 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
268 return _parser->get_section(section, info);
271 const int buffer_size = 200000;
273 flexichar low_buff[buffer_size + 3];
274 int read_len = GetPrivateProfileSection(to_unicode_temp(section.observe()),
275 low_buff, buffer_size - 1, to_unicode_temp(name()));
276 if (!read_len) return false; // assume the API means there was no section.
278 low_buff[read_len] = '\1'; // signal beyond the end of the stuff.
279 low_buff[read_len + 1] = '\0'; // make sure we're still zero terminated.
281 bool last_was_nil = false;
282 // this loop replaces all the embedded nils with separators to allow the
283 // variable_tokenizer to retrieve all the strings from the section.
284 for (int i = 0; i < read_len; i++) {
285 if (!low_buff[i] && last_was_nil) {
286 // termination condition; we got two nils in a row.
287 // this is just paranoia; the length should tell us.
289 } else if (!low_buff[i]) {
290 low_buff[i] = '\1'; // replace with a separator.
292 } else last_was_nil = false; // reset the nil flag.
295 // now convert to a simple astring.
296 astring buff = from_unicode_temp(low_buff);
297 int length = buff.length();
299 variable_tokenizer parser("\1", "=");
301 info = parser.table();
306 bool ini_configurator::put_section(const astring §ion,
307 const string_table &info)
310 variable_tokenizer parser("\1", "=");
311 parser.table() = info;
312 astring flat = parser.text_form();
313 flat += "\1\1"; // add terminating guard.
314 int len = flat.length();
315 for (int i = 0; i < len; i++) {
316 if (flat[i] == '\1') {
318 if (flat[i+1] == ' ') {
319 // if the space character is next, shift it before the nil to avoid
320 // keys with a preceding space.
326 return WritePrivateProfileSection(to_unicode_temp(section),
327 to_unicode_temp(flat), to_unicode_temp(name()));
329 // write the section.
330 bool to_return = _parser->put_section(section, info);
331 // schedule file write.
338 bool ini_configurator::put_profile_string(const astring §ion,
339 const astring &entry, const astring &to_store)
341 return bool(WritePrivateProfileString(to_unicode_temp(section),
342 entry.length() ? (flexichar *)to_unicode_temp(entry) : NULL_POINTER,
343 to_store.length() ? (flexichar *)to_unicode_temp(to_store) : NULL_POINTER,
344 to_unicode_temp(name())));
347 void ini_configurator::get_profile_string(const astring §ion,
348 const astring &entry, const astring &default_value,
349 flexichar *return_buffer, int buffer_size)
351 GetPrivateProfileString(section.length() ?
352 (flexichar *)to_unicode_temp(section) : NULL_POINTER,
353 entry.length() ? (flexichar *)to_unicode_temp(entry) : NULL_POINTER,
354 to_unicode_temp(default_value),
355 return_buffer, buffer_size, to_unicode_temp(name()));