feisty meow concerns codebase 2.140
ini_configurator.cpp
Go to the documentation of this file.
1/*****************************************************************************\
2* *
3* Name : ini_configurator *
4* Author : Chris Koeritz *
5* *
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\*****************************************************************************/
14
15#include "ini_configurator.h"
17#include "variable_tokenizer.h"
18
20#include <basis/astring.h>
21#include <basis/environment.h>
22#include <basis/functions.h>
23#include <basis/mutex.h>
27#include <filesystem/filename.h>
32
33#include <stdio.h>
34
35#undef LOG
36#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s())
37
38using namespace basis;
39using namespace filesystem;
40using namespace structures;
41
42namespace configuration {
43
44//#define DEBUG_INI_CONFIGURATOR
45 // uncomment for noisy version.
46
47const int MAXIMUM_LINE_INI_CONFIG = 16384;
48
49// a default we hope never to see in an ini file.
50SAFE_STATIC_CONST(astring, ini_configurator::ini_str_fake_default, ("NoTomatoesNorPotatoesNorQuayle"))
51
54: configurator(behavior),
55 _ini_name(new filename),
56#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
57 _parser(new ini_parser("", behavior)),
58#endif
59 _where(where),
60 _add_spaces(false)
61{
62 FUNCDEF("constructor");
63 name(ini_filename); // set name properly.
64//LOG(astring("calculated ini name as: '") + _ini_name->raw() + "'");
65}
66
68{
69 WHACK(_ini_name);
70#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
71 WHACK(_parser);
72#endif
73}
74
75astring ini_configurator::name() const { return _ini_name->raw(); }
76
78{
79#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
80 write_ini_file();
81 WHACK(_parser);
82 _parser = new ini_parser("", behavior());
83#endif
84}
85
87{
88 *_ini_name = name;
89
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;
95//#ifdef _MSC_VER
96// use_appdir = true;
97//#endif
98 // we must create the filename if they specified no directory at all.
99 if (!_ini_name->had_directory()) {
100 if (use_appdir) {
101 // this is needed in case there is an ini right with the file; our
102 // standard is to check there first.
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.
110 *_ini_name = filename(environment::get("ALLUSERSPROFILE")
112 _ini_name->basename());
113 }
114 }
115#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
116 // read in the file's contents.
117 read_ini_file();
118#endif
119}
120
122{
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.
127 astring line_found;
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());
139 list += line_found;
140 }
141}
142
143//hmmm: refactor section_exists to use the sections call, if it's faser?
145{
146//#ifdef _MSC_VER
147// string_table infos;
148// // heavy-weight call here...
149// return get_section(section, infos);
150//#else
151 return _parser->section_exists(section);
152//#endif
153}
154
155#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
156void ini_configurator::read_ini_file()
157{
158#ifdef DEBUG_INI_CONFIGURATOR
159 FUNCDEF("read_ini_file");
160#endif
161 _parser->reset(""); // clear out our current contents.
162 byte_filer ini_file;
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);
167#endif
168 if (!open_ret || !ini_file.good()) {
169 return; // failure.
170 }
171 int file_size = ini_file.length(); // get the file length.
172 // read the file.
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);
177}
178
179void ini_configurator::write_ini_file()
180{
181#ifdef DEBUG_INI_CONFIGURATOR
182 FUNCDEF("write_ini_file");
183#endif
184
185//hmmm: just set dirty flag and use that for deciding whether to write.
186//hmmm: future version, have a thread scheduled to write.
187
188 // open filer with new mode for cleaning.
189 byte_filer ini_file;
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);
195#endif
196 if (!open_ret || !ini_file.good()) return; // failure.
197
198 // output table's contents to text.
199 astring text;
200 _parser->restate(text, _add_spaces);
201 ini_file.write((abyte *)text.observe(), text.length());
202}
203#endif //UNIX
204
206{
207//#ifdef _MSC_VER
208// return put_profile_string(section, "", "");
209//#else
210 // zap the section.
211 bool to_return = _parser->delete_section(section);
212 // schedule the file to write.
213 write_ini_file();
214 return to_return;
215//#endif
216}
217
218bool ini_configurator::delete_entry(const astring &section, const astring &ent)
219{
220//#ifdef _MSC_VER
221// return put_profile_string(section, ent, "");
222//#else
223 // zap the entry.
224 bool to_return = _parser->delete_entry(section, ent);
225 // schedule the file to write.
226 write_ini_file();
227 return to_return;
228//#endif
229}
230
231bool ini_configurator::put(const astring &section, const astring &entry,
232 const astring &to_store)
233{
234 FUNCDEF("put");
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;
238//#ifdef _MSC_VER
239// return put_profile_string(section, entry, to_store);
240//#else
241 // write the entry.
242 bool to_return = _parser->put(section, entry, to_store);
243 // schedule file write.
244 write_ini_file();
245 return to_return;
246//#endif
247}
248
249bool ini_configurator::get(const astring &section, const astring &entry,
250 astring &found)
251{
252#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
253 return _parser->get(section, entry, found);
254#else
256 temp_buffer[0] = 0;
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);
261#endif
262}
263
265{
266 FUNCDEF("get_section");
267#if defined(__UNIX__) || defined(__GNU_WINDOWS__)
268 return _parser->get_section(section, info);
269#else
270 info.reset();
271 const int buffer_size = 200000;
272
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.
277
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.
280
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.
288 break;
289 } else if (!low_buff[i]) {
290 low_buff[i] = '\1'; // replace with a separator.
291 last_was_nil = true;
292 } else last_was_nil = false; // reset the nil flag.
293 }
294
295 // now convert to a simple astring.
296 astring buff = from_unicode_temp(low_buff);
297 int length = buff.length();
298 buff.shrink();
299 variable_tokenizer parser("\1", "=");
300 parser.parse(buff);
301 info = parser.table();
302 return true;
303#endif
304}
305
307 const string_table &info)
308{
309/*
310#ifdef _MSC_VER
311 variable_tokenizer parser("\1", "=");
312 parser.table() = info;
313 astring flat = parser.text_form();
314 flat += "\1\1"; // add terminating guard.
315 int len = flat.length();
316 for (int i = 0; i < len; i++) {
317 if (flat[i] == '\1') {
318 flat[i] = '\0';
319 if (flat[i+1] == ' ') {
320 // if the space character is next, shift it before the nil to avoid
321 // keys with a preceding space.
322 flat[i] = ' ';
323 flat[i + 1] = '\0';
324 }
325 }
326 }
327 return WritePrivateProfileSection(to_unicode_temp(section),
328 to_unicode_temp(flat), to_unicode_temp(name()));
329#else
330*/
331 // write the section.
332 bool to_return = _parser->put_section(section, info);
333 // schedule file write.
334 write_ini_file();
335 return to_return;
336//#endif
337}
338
339/*
340#ifdef _MSC_VER
341bool ini_configurator::put_profile_string(const astring &section,
342 const astring &entry, const astring &to_store)
343{
344 return bool(WritePrivateProfileString(to_unicode_temp(section),
345 entry.length() ? (flexichar *)to_unicode_temp(entry) : NULL_POINTER,
346 to_store.length() ? (flexichar *)to_unicode_temp(to_store) : NULL_POINTER,
347 to_unicode_temp(name())));
348}
349
350void ini_configurator::get_profile_string(const astring &section,
351 const astring &entry, const astring &default_value,
352 flexichar *return_buffer, int buffer_size)
353{
354 GetPrivateProfileString(section.length() ?
355 (flexichar *)to_unicode_temp(section) : NULL_POINTER,
356 entry.length() ? (flexichar *)to_unicode_temp(entry) : NULL_POINTER,
357 to_unicode_temp(default_value),
358 return_buffer, buffer_size, to_unicode_temp(name()));
359}
360#endif
361*/
362
363} //namespace.
364
365
#define LOG(s)
const int buffer_size
Definition checker.cpp:37
Provides a dynamically resizable ASCII character string.
Definition astring.h:35
virtual void zap(int start, int end)
Deletes the characters between "start" and "end" inclusively.
Definition astring.cpp:524
void strip_white_spaces(how_to_strip way=FROM_BOTH_SIDES)
like strip_spaces, but includes tabs in the list to strip.
Definition astring.h:329
void shrink()
changes all occurrences of "to_replace" into "new_string".
Definition astring.cpp:168
int end() const
returns the index of the last (non-null) character in the string.
Definition astring.h:86
int length() const
Returns the current length of the string.
Definition astring.cpp:132
int find(char to_find, int position=0, bool reverse=false) const
Locates "to_find" in "this".
Definition astring.cpp:577
virtual const char * observe() const
observes the underlying pointer to the zero-terminated string.
Definition astring.cpp:140
static astring get(const astring &variable_name)
looks up the "variable_name" in the current environment variables.
static basis::astring application_directory()
returns the directory name where this application is running from.
static const char * software_product_name()
This global function is available to the system at large for branding info.
Provides a base class for configuration repositories.
treatment_of_defaults behavior() const
observes the behavior chosen for the load() function.
Supports a configurator-based interface on text initialization files.
virtual bool section_exists(const basis::astring &section)
returns true if the "section" was found in the file.
virtual bool delete_entry(const basis::astring &section, const basis::astring &entry)
removes the entry specified by the "section" and "entry" name.
file_location_default
chooses where the ini file is located if no path to it is provided.
@ OS_DIRECTORY
config files live in operating system directory.
@ ALL_USERS_DIRECTORY
config files live in the "all users" account.
virtual void sections(structures::string_array &list)
retrieves the section names into "list".
basis::astring name() const
observes the name of the file used for ini entries.
virtual bool get(const basis::astring &section, const basis::astring &entry, basis::astring &found)
implements the configurator retrieval function.
virtual bool delete_section(const basis::astring &section)
removes the entire "section" specified.
virtual bool put_section(const basis::astring &section, const structures::string_table &info)
writes a table called "info" into the "section" in the INI file.
virtual bool get_section(const basis::astring &section, structures::string_table &info)
reads the entire "section" into a table called "info".
virtual bool put(const basis::astring &section, const basis::astring &entry, const basis::astring &to_store)
implements the configurator storage function.
void refresh()
useful mainly on unix/linux, where the file is parsed and held in memory.
Parses strings in the fairly well-known INI file format.
Definition ini_parser.h:74
void reset(const basis::astring &to_parse)
drops any existing information and processes the string "to_parse".
bool restate(basis::astring &new_ini, bool add_spaces=false)
stores a cleaned version of the internal state into "new_ini".
virtual bool delete_entry(const basis::astring &section, const basis::astring &entry)
removes the entry specified by the "section" and "entry" name.
virtual bool put(const basis::astring &section, const basis::astring &entry, const basis::astring &to_store)
implements the configurator storage function.
virtual bool delete_section(const basis::astring &section)
removes the entire "section" specified.
virtual bool get(const basis::astring &section, const basis::astring &entry, basis::astring &found)
implements the configurator retrieval function.
virtual bool section_exists(const basis::astring &section)
true if the "section" is presently in the table config.
virtual bool put_section(const basis::astring &section, const structures::string_table &info)
writes a table called "info" into the "section" held here.
virtual bool get_section(const basis::astring &section, structures::string_table &info)
reads the entire table held under "section" into a table called "info".
Manages a bank of textual definitions of variables.
const structures::string_table & table() const
provides a constant peek at the string_table holding the values.
bool parse(const basis::astring &to_tokenize)
parses the string using our established sentinel characters.
Provides file managment services using the standard I/O support.
Definition byte_filer.h:32
int write(const basis::abyte *buffer, int buffer_size)
writes "buffer_size" bytes into the file from "buffer".
int read(basis::abyte *buffer, int buffer_size)
reads "buffer_size" bytes from the file into "buffer".
size_t length()
returns the file's total length, in bytes.
bool good()
returns true if the file seems to be in the appropriate desired state.
bool open(const basis::astring &fname, const basis::astring &permissions)
opens a file with "fname" and "permissions" as in the constructor.
static bool make_directory(const basis::astring &path)
returns true if the directory "path" could be created.
Provides operations commonly needed on file names.
Definition filename.h:64
bool had_directory() const
returns true if the name that we were given had a non-empty directory.
Definition filename.h:139
const basis::astring & raw() const
returns the astring that we're holding onto for the path.
Definition filename.cpp:97
filename basename() const
returns the base of the filename; no directory.
Definition filename.cpp:385
An array of strings with some additional helpful methods.
Provides a symbol_table that holds strings as the content.
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
The guards collection helps in testing preconditions and reporting errors.
Definition array.h:30
void WHACK(contents *&ptr)
deletion with clearing of the pointer.
Definition functions.h:121
unsigned char abyte
A fairly important unit which is seldom defined...
Definition definitions.h:51
bool negative(const type &a)
negative returns true if "a" is less than zero.
Definition functions.h:43
char flexichar
Definition definitions.h:58
const int MAXIMUM_LINE_INI_CONFIG
A platform independent way to obtain the timestamp of a file.
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
#define SAFE_STATIC_CONST(type, func_name, parms)
this version returns a constant object instead.
Support for unicode builds.
Aids in achievement of platform independence.