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>
34 #define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s())
36 using namespace basis;
37 using namespace filesystem;
38 using namespace structures;
40 namespace configuration {
42 //#define DEBUG_INI_CONFIGURATOR
43 // uncomment for noisy version.
45 const int MAXIMUM_LINE_INI_CONFIG = 16384;
47 // a default we hope never to see in an ini file.
48 SAFE_STATIC_CONST(astring, ini_configurator::ini_str_fake_default, ("NoTomatoesNorPotatoesNorQuayle"))
50 ini_configurator::ini_configurator(const astring &ini_filename,
51 treatment_of_defaults behavior, file_location_default where)
52 : configurator(behavior),
53 _ini_name(new filename),
54 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
55 _parser(new ini_parser("", behavior)),
60 name(ini_filename); // set name properly.
63 ini_configurator::~ini_configurator()
66 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
71 astring ini_configurator::name() const { return _ini_name->raw(); }
73 void ini_configurator::refresh()
75 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
78 _parser = new ini_parser("", behavior());
82 void ini_configurator::name(const astring &name)
86 bool use_appdir = true;
87 // true if we should put files where programs start for those filenames
88 // that don't include a directory name.
89 if (_where == OS_DIRECTORY) use_appdir = false;
90 if (_where == ALL_USERS_DIRECTORY) use_appdir = false;
94 // we must create the filename if they specified no directory at all.
95 if (!_ini_name->had_directory()) {
97 // this is needed in case there is an ini right with the file; our
98 // standard is to check there first.
99 *_ini_name = filename(application_configuration::application_directory(),
100 _ini_name->basename());
101 } else if (!use_appdir && (_where == ALL_USERS_DIRECTORY) ) {
102 // when the location default is all users, we get that from the
103 // environment. for the OS dir choice, we leave out the path entirely.
104 directory::make_directory(environment::get("ALLUSERSPROFILE")
105 + "/" + application_configuration::software_product_name());
106 *_ini_name = filename(environment::get("ALLUSERSPROFILE")
107 + "/" + application_configuration::software_product_name(),
108 _ini_name->basename());
111 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
112 // read in the file's contents.
117 void ini_configurator::sections(string_array &list)
119 list = string_array();
120 // open our ini file directly as a file.
121 byte_filer section8(*_ini_name, "rb");
122 if (!section8.good()) return; // not a healthy file.
124 // iterate through the lines of the ini file and see if we can't find a
125 // bunch of section names.
126 while (section8.read(line_found, MAXIMUM_LINE_INI_CONFIG) > 0) {
127 // is the line in the format "^[ \t]*\[\([^\]]+\)\].*$" ?
128 // if it is in that format, we add the matched \1 into our list.
129 line_found.strip_white_spaces();
130 if (line_found[0] != '[') continue; // no opening bracket. skip line.
131 line_found.zap(0, 0); // toss opening bracket.
132 int close_brack_indy = line_found.find(']');
133 if (negative(close_brack_indy)) continue; // no closing bracket.
134 line_found.zap(close_brack_indy, line_found.end());
139 //hmmm: refactor section_exists to use the sections call, if it's faser?
140 bool ini_configurator::section_exists(const astring §ion)
144 // heavy-weight call here...
145 return get_section(section, infos);
147 return _parser->section_exists(section);
151 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
152 void ini_configurator::read_ini_file()
154 #ifdef DEBUG_INI_CONFIGURATOR
155 FUNCDEF("read_ini_file");
157 _parser->reset(""); // clear out our current contents.
159 bool open_ret = ini_file.open(*_ini_name, "rb"); // simple reading.
160 #ifdef DEBUG_INI_CONFIGURATOR
161 if (!open_ret) LOG(astring("failed to open ini file: ") + *_ini_name);
162 if (!ini_file.good()) LOG(astring("ini file not good: ") + *_ini_name);
164 if (!open_ret || !ini_file.good()) {
167 int file_size = ini_file.length(); // get the file length.
169 astring contents(' ', file_size + 3);
170 int bytes_read = ini_file.read((abyte *)contents.observe(), file_size);
171 contents.zap(bytes_read + 1, contents.end());
172 _parser->reset(contents);
175 void ini_configurator::write_ini_file()
177 #ifdef DEBUG_INI_CONFIGURATOR
178 FUNCDEF("write_ini_file");
181 //hmmm: just set dirty flag and use that for deciding whether to write.
182 //hmmm: future version, have a thread scheduled to write.
184 // open filer with new mode for cleaning.
186 bool open_ret = ini_file.open(*_ini_name, "wb");
187 // open the file for binary read/write and drop previous contents.
188 #ifdef DEBUG_INI_CONFIGURATOR
189 if (!open_ret) LOG(astring("failed to open ini file: ") + *_ini_name);
190 if (!ini_file.good()) LOG(astring("ini file not good: ") + *_ini_name);
192 if (!open_ret || !ini_file.good()) return; // failure.
194 // output table's contents to text.
196 _parser->restate(text, _add_spaces);
197 ini_file.write((abyte *)text.observe(), text.length());
201 bool ini_configurator::delete_section(const astring §ion)
204 return put_profile_string(section, "", "");
207 bool to_return = _parser->delete_section(section);
208 // schedule the file to write.
214 bool ini_configurator::delete_entry(const astring §ion, const astring &ent)
217 return put_profile_string(section, ent, "");
220 bool to_return = _parser->delete_entry(section, ent);
221 // schedule the file to write.
227 bool ini_configurator::put(const astring §ion, const astring &entry,
228 const astring &to_store)
231 if (!to_store.length()) return delete_entry(section, entry);
232 else if (!entry.length()) return delete_section(section);
233 else if (!section.length()) return false;
235 return put_profile_string(section, entry, to_store);
238 bool to_return = _parser->put(section, entry, to_store);
239 // schedule file write.
245 bool ini_configurator::get(const astring §ion, const astring &entry,
248 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
249 return _parser->get(section, entry, found);
251 flexichar temp_buffer[MAXIMUM_LINE_INI_CONFIG];
253 get_profile_string(section, entry, ini_str_fake_default(),
254 temp_buffer, MAXIMUM_LINE_INI_CONFIG - 1);
255 found = from_unicode_temp(temp_buffer);
256 return !(ini_str_fake_default() == found);
260 bool ini_configurator::get_section(const astring §ion, string_table &info)
262 FUNCDEF("get_section");
263 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
264 return _parser->get_section(section, info);
267 const int buffer_size = 200000;
269 flexichar low_buff[buffer_size + 3];
270 int read_len = GetPrivateProfileSection(to_unicode_temp(section.observe()),
271 low_buff, buffer_size - 1, to_unicode_temp(name()));
272 if (!read_len) return false; // assume the API means there was no section.
274 low_buff[read_len] = '\1'; // signal beyond the end of the stuff.
275 low_buff[read_len + 1] = '\0'; // make sure we're still zero terminated.
277 bool last_was_nil = false;
278 // this loop replaces all the embedded nils with separators to allow the
279 // variable_tokenizer to retrieve all the strings from the section.
280 for (int i = 0; i < read_len; i++) {
281 if (!low_buff[i] && last_was_nil) {
282 // termination condition; we got two nils in a row.
283 // this is just paranoia; the length should tell us.
285 } else if (!low_buff[i]) {
286 low_buff[i] = '\1'; // replace with a separator.
288 } else last_was_nil = false; // reset the nil flag.
291 // now convert to a simple astring.
292 astring buff = from_unicode_temp(low_buff);
293 int length = buff.length();
295 variable_tokenizer parser("\1", "=");
297 info = parser.table();
302 bool ini_configurator::put_section(const astring §ion,
303 const string_table &info)
306 variable_tokenizer parser("\1", "=");
307 parser.table() = info;
308 astring flat = parser.text_form();
309 flat += "\1\1"; // add terminating guard.
310 int len = flat.length();
311 for (int i = 0; i < len; i++) {
312 if (flat[i] == '\1') {
314 if (flat[i+1] == ' ') {
315 // if the space character is next, shift it before the nil to avoid
316 // keys with a preceding space.
322 return WritePrivateProfileSection(to_unicode_temp(section),
323 to_unicode_temp(flat), to_unicode_temp(name()));
325 // write the section.
326 bool to_return = _parser->put_section(section, info);
327 // schedule file write.
334 bool ini_configurator::put_profile_string(const astring §ion,
335 const astring &entry, const astring &to_store)
337 return bool(WritePrivateProfileString(to_unicode_temp(section),
338 entry.length() ? (flexichar *)to_unicode_temp(entry) : NULL_POINTER,
339 to_store.length() ? (flexichar *)to_unicode_temp(to_store) : NULL_POINTER,
340 to_unicode_temp(name())));
343 void ini_configurator::get_profile_string(const astring §ion,
344 const astring &entry, const astring &default_value,
345 flexichar *return_buffer, int buffer_size)
347 GetPrivateProfileString(section.length() ?
348 (flexichar *)to_unicode_temp(section) : NULL_POINTER,
349 entry.length() ? (flexichar *)to_unicode_temp(entry) : NULL_POINTER,
350 to_unicode_temp(default_value),
351 return_buffer, buffer_size, to_unicode_temp(name()));