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>
24 #include <basis/utf_conversion.h>
25 #include <filesystem/byte_filer.h>
26 #include <filesystem/directory.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 
38 using namespace basis;
39 using namespace filesystem;
40 using namespace structures;
41 
42 namespace configuration {
43 
44 //#define DEBUG_INI_CONFIGURATOR
45  // uncomment for noisy version.
46 
47 const int MAXIMUM_LINE_INI_CONFIG = 16384;
48 
49 // a default we hope never to see in an ini file.
50 SAFE_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 
67 ini_configurator::~ini_configurator()
68 {
69  WHACK(_ini_name);
70 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
71  WHACK(_parser);
72 #endif
73 }
74 
75 astring ini_configurator::name() const { return _ini_name->raw(); }
76 
77 void ini_configurator::refresh()
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 
86 void ini_configurator::name(const astring &name)
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.
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());
113  }
114  }
115 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
116  // read in the file's contents.
117  read_ini_file();
118 #endif
119 }
120 
121 void ini_configurator::sections(string_array &list)
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?
144 bool ini_configurator::section_exists(const astring &section)
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__)
156 void 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 
179 void 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 
205 bool ini_configurator::delete_section(const astring &section)
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 
218 bool 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 
231 bool 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 
249 bool 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
255  flexichar temp_buffer[MAXIMUM_LINE_INI_CONFIG];
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 
264 bool ini_configurator::get_section(const astring &section, string_table &info)
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 
306 bool ini_configurator::put_section(const astring &section,
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
341 bool 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 
350 void 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 
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:521
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:574
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.
Definition: environment.cpp:57
Provides a base class for configuration repositories.
Definition: configurator.h:34
Supports a configurator-based interface on text initialization files.
file_location_default
chooses where the ini file is located if no path to it is provided.
Parses strings in the fairly well-known INI file format.
Definition: ini_parser.h:74
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".
Definition: byte_filer.cpp:126
int read(basis::abyte *buffer, int buffer_size)
reads "buffer_size" bytes from the file into "buffer".
Definition: byte_filer.cpp:123
size_t length()
returns the file's total length, in bytes.
Definition: byte_filer.cpp:140
bool good()
returns true if the file seems to be in the appropriate desired state.
Definition: byte_filer.cpp:103
bool open(const basis::astring &fname, const basis::astring &permissions)
opens a file with "fname" and "permissions" as in the constructor.
Definition: byte_filer.cpp:86
Provides operations commonly needed on file names.
Definition: filename.h:64
An array of strings with some additional helpful methods.
Definition: string_array.h:32
Provides a symbol_table that holds strings as the content.
Definition: string_table.h:32
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:57
#define LOG(to_print)
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.
Definition: byte_filer.cpp:37
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.
string_array(1, math_list))) const char *addr_list[]
Support for unicode builds.
Aids in achievement of platform independence.