ini_configurator.cpp

Go to the documentation of this file.
00001 /*****************************************************************************\
00002 *                                                                             *
00003 *  Name   : ini_configurator                                                  *
00004 *  Author : Chris Koeritz                                                     *
00005 *                                                                             *
00006 *******************************************************************************
00007 * Copyright (c) 2000-$now By Author.  This program is free software; you can  *
00008 * redistribute it and/or modify it under the terms of the GNU General Public  *
00009 * License as published by the Free Software Foundation; either version 2 of   *
00010 * the License or (at your option) any later version.  This is online at:      *
00011 *     http://www.fsf.org/copyleft/gpl.html                                    *
00012 * Please send any updates to: fred@gruntose.com                               *
00013 \*****************************************************************************/
00014 
00015 #include "ini_configurator.h"
00016 #include "application_configuration.h"
00017 #include "variable_tokenizer.h"
00018 
00019 #include <application/windoze_helper.h>
00020 #include <basis/astring.h>
00021 #include <basis/environment.h>
00022 #include <basis/functions.h>
00023 #include <basis/mutex.h>
00024 #include <basis/utf_conversion.h>
00025 #include <filesystem/byte_filer.h>
00026 #include <filesystem/directory.h>
00027 #include <filesystem/filename.h>
00028 #include <structures/static_memory_gremlin.h>
00029 #include <structures/string_array.h>
00030 #include <structures/string_table.h>
00031 #include <structures/symbol_table.h>
00032 
00033 #undef LOG
00034 #define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s())
00035 
00036 using namespace basis;
00037 using namespace filesystem;
00038 using namespace structures;
00039 
00040 namespace configuration {
00041 
00042 //#define DEBUG_INI_CONFIGURATOR
00043   // uncomment for noisy version.
00044 
00045 const int MAXIMUM_LINE_INI_CONFIG = 16384;
00046 
00047 // a default we hope never to see in an ini file.
00048 SAFE_STATIC_CONST(astring, ini_configurator::ini_str_fake_default, ("NoTomatoesNorPotatoesNorQuayle"))
00049 
00050 ini_configurator::ini_configurator(const astring &ini_filename,
00051       treatment_of_defaults behavior, file_location_default where)
00052 : configurator(behavior),
00053   _ini_name(new filename),
00054 #ifdef __UNIX__
00055   _parser(new ini_parser("", behavior)),
00056 #endif
00057   _where(where),
00058   _add_spaces(false)
00059 {
00060   name(ini_filename);  // set name properly.
00061 }
00062 
00063 ini_configurator::~ini_configurator()
00064 {
00065   WHACK(_ini_name);
00066 #ifdef __UNIX__
00067   WHACK(_parser);
00068 #endif
00069 }
00070 
00071 astring ini_configurator::name() const { return _ini_name->raw(); }
00072 
00073 void ini_configurator::refresh()
00074 {
00075 #ifdef __UNIX__
00076   write_ini_file();
00077   WHACK(_parser);
00078   _parser = new ini_parser("", behavior());
00079 #endif
00080 }
00081 
00082 void ini_configurator::name(const astring &name)
00083 {
00084   *_ini_name = name;
00085 
00086   bool use_appdir = true;
00087     // true if we should put files where programs start for those filenames
00088     // that don't include a directory name.
00089   if (_where == OS_DIRECTORY) use_appdir = false;
00090   if (_where == ALL_USERS_DIRECTORY) use_appdir = false;
00091 #ifndef __WIN32__
00092   use_appdir = true;
00093 #endif
00094   // we must create the filename if they specified no directory at all.
00095   if (!_ini_name->had_directory()) {
00096     if (use_appdir) {
00097       // this is needed in case there is an ini right with the file; our
00098       // standard is to check there first.
00099       *_ini_name = filename(application_configuration::application_directory(),
00100           _ini_name->basename());
00101     } else if (!use_appdir && (_where == ALL_USERS_DIRECTORY) ) {
00102       // when the location default is all users, we get that from the
00103       // environment.  for the OS dir choice, we leave out the path entirely.
00104       directory::make_directory(environment::get("ALLUSERSPROFILE")
00105           + "/" + application_configuration::software_product_name());
00106       *_ini_name = filename(environment::get("ALLUSERSPROFILE")
00107           + "/" + application_configuration::software_product_name(),
00108           _ini_name->basename());
00109     }
00110   }
00111 #ifdef __UNIX__
00112   // read in the file's contents.
00113   read_ini_file();
00114 #endif
00115 }
00116 
00117 void ini_configurator::sections(string_array &list)
00118 {
00119   list = string_array();
00120   // open our ini file directly as a file.
00121   byte_filer section8(*_ini_name, "rb");
00122   if (!section8.good()) return;  // not a healthy file.
00123   astring line_found;
00124   // iterate through the lines of the ini file and see if we can't find a
00125   // bunch of section names.
00126   while (section8.read(line_found, MAXIMUM_LINE_INI_CONFIG) > 0) {
00127     // is the line in the format "^[ \t]*\[\([^\]]+\)\].*$" ?
00128     // if it is in that format, we add the matched \1 into our list.
00129     line_found.strip_white_spaces();
00130     if (line_found[0] != '[') continue;  // no opening bracket.  skip line.
00131     line_found.zap(0, 0);  // toss opening bracket.
00132     int close_brack_indy = line_found.find(']');
00133     if (negative(close_brack_indy)) continue;  // no closing bracket.
00134     line_found.zap(close_brack_indy, line_found.end());
00135     list += line_found;
00136   }
00137 }
00138 
00139 //hmmm: refactor section_exists to use the sections call, if it's faser?
00140 bool ini_configurator::section_exists(const astring &section)
00141 {
00142 #ifdef __WIN32__
00143   string_table infos;
00144   // heavy-weight call here...
00145   return get_section(section, infos);
00146 #else
00147   return _parser->section_exists(section);
00148 #endif
00149 }
00150 
00151 #ifdef __UNIX__
00152 void ini_configurator::read_ini_file()
00153 {
00154 #ifdef DEBUG_INI_CONFIGURATOR
00155   FUNCDEF("read_ini_file");
00156 #endif
00157   _parser->reset("");  // clear out our current contents.
00158   byte_filer ini_file;
00159   bool open_ret = ini_file.open(*_ini_name, "rb");  // simple reading.
00160 #ifdef DEBUG_INI_CONFIGURATOR
00161   if (!open_ret) LOG(astring("failed to open ini file: ") + *_ini_name);
00162   if (!ini_file.good()) LOG(astring("ini file not good: ") + *_ini_name);
00163 #endif
00164   if (!open_ret || !ini_file.good()) {
00165     return;  // failure.
00166   }
00167   int file_size = ini_file.length();  // get the file length.
00168   // read the file.
00169   astring contents(' ', file_size + 3);
00170   int bytes_read = ini_file.read((abyte *)contents.observe(), file_size);
00171   contents.zap(bytes_read + 1, contents.end());
00172   _parser->reset(contents);
00173 }
00174 
00175 void ini_configurator::write_ini_file()
00176 {
00177 #ifdef DEBUG_INI_CONFIGURATOR
00178   FUNCDEF("write_ini_file");
00179 #endif
00180 
00181 //hmmm: just set dirty flag and use that for deciding whether to write.
00182 //hmmm: future version, have a thread scheduled to write.
00183 
00184   // open filer with new mode for cleaning.
00185   byte_filer ini_file;
00186   bool open_ret = ini_file.open(*_ini_name, "wb");
00187     // open the file for binary read/write and drop previous contents.
00188 #ifdef DEBUG_INI_CONFIGURATOR
00189   if (!open_ret) LOG(astring("failed to open ini file: ") + *_ini_name);
00190   if (!ini_file.good()) LOG(astring("ini file not good: ") + *_ini_name);
00191 #endif
00192   if (!open_ret || !ini_file.good()) return;  // failure.
00193 
00194   // output table's contents to text.
00195   astring text;
00196   _parser->restate(text, _add_spaces);
00197   ini_file.write((abyte *)text.observe(), text.length());
00198 }
00199 #endif //UNIX
00200 
00201 bool ini_configurator::delete_section(const astring &section)
00202 {
00203 #ifdef __WIN32__
00204   return put_profile_string(section, "", ""); 
00205 #else
00206   // zap the section.
00207   bool to_return = _parser->delete_section(section);
00208   // schedule the file to write.
00209   write_ini_file();
00210   return to_return;
00211 #endif
00212 }
00213 
00214 bool ini_configurator::delete_entry(const astring &section, const astring &ent)
00215 {
00216 #ifdef __WIN32__
00217   return put_profile_string(section, ent, "");
00218 #else
00219   // zap the entry.
00220   bool to_return = _parser->delete_entry(section, ent);
00221   // schedule the file to write.
00222   write_ini_file();
00223   return to_return;
00224 #endif
00225 }
00226 
00227 bool ini_configurator::put(const astring &section, const astring &entry,
00228     const astring &to_store)
00229 {
00231   if (!to_store.length()) return delete_entry(section, entry);
00232   else if (!entry.length()) return delete_section(section);
00233   else if (!section.length()) return false;
00234 #ifdef __WIN32__
00235   return put_profile_string(section, entry, to_store);
00236 #else
00237   // write the entry.
00238   bool to_return = _parser->put(section, entry, to_store);
00239   // schedule file write.
00240   write_ini_file();
00241   return to_return;
00242 #endif
00243 }
00244 
00245 bool ini_configurator::get(const astring &section, const astring &entry,
00246     astring &found)
00247 {
00248 #ifndef __WIN32__
00249   return _parser->get(section, entry, found);
00250 #else
00251   flexichar temp_buffer[MAXIMUM_LINE_INI_CONFIG];
00252   temp_buffer[0] = 0;
00253   get_profile_string(section, entry, ini_str_fake_default(),
00254       temp_buffer, MAXIMUM_LINE_INI_CONFIG - 1);
00255   found = from_unicode_temp(temp_buffer);
00256   return !(ini_str_fake_default() == found);
00257 #endif
00258 }
00259 
00260 bool ini_configurator::get_section(const astring &section, string_table &info)
00261 {
00263 #ifndef __WIN32__
00264   return _parser->get_section(section, info);
00265 #else
00266   info.reset();
00267   const int buffer_size = 200000;
00268 
00269   flexichar low_buff[buffer_size + 3];
00270   int read_len = GetPrivateProfileSection(to_unicode_temp(section.observe()),
00271       low_buff, buffer_size - 1, to_unicode_temp(name()));
00272   if (!read_len) return false;  // assume the API means there was no section.
00273 
00274   low_buff[read_len] = '\1';  // signal beyond the end of the stuff.
00275   low_buff[read_len + 1] = '\0';  // make sure we're still zero terminated.
00276 
00277   bool last_was_nil = false;
00278   // this loop replaces all the embedded nils with separators to allow the
00279   // variable_tokenizer to retrieve all the strings from the section.
00280   for (int i = 0; i < read_len; i++) {
00281     if (!low_buff[i] && last_was_nil) {
00282       // termination condition; we got two nils in a row.
00283       // this is just paranoia; the length should tell us.
00284       break;
00285     } else if (!low_buff[i]) {
00286       low_buff[i] = '\1';  // replace with a separator.
00287       last_was_nil = true;
00288     } else last_was_nil = false;  // reset the nil flag.
00289   }
00290 
00291   // now convert to a simple astring.
00292   astring buff = from_unicode_temp(low_buff);
00293   int length = buff.length();
00294   buff.shrink();
00295   variable_tokenizer parser("\1", "=");
00296   parser.parse(buff);
00297   info = parser.table();
00298   return true;
00299 #endif
00300 }
00301 
00302 bool ini_configurator::put_section(const astring &section,
00303     const string_table &info)
00304 {
00305 #ifdef __WIN32__
00306   variable_tokenizer parser("\1", "=");
00307   parser.table() = info;
00308   astring flat = parser.text_form();
00309   flat += "\1\1";  // add terminating guard.
00310   int len = flat.length();
00311   for (int i = 0; i < len; i++) {
00312     if (flat[i] == '\1') {
00313       flat[i] = '\0';
00314       if (flat[i+1] == ' ') {
00315         // if the space character is next, shift it before the nil to avoid
00316         // keys with a preceding space.
00317         flat[i] = ' ';
00318         flat[i + 1] = '\0';
00319       }
00320     }
00321   }
00322   return WritePrivateProfileSection(to_unicode_temp(section),
00323       to_unicode_temp(flat), to_unicode_temp(name()));
00324 #else
00325   // write the section.
00326   bool to_return = _parser->put_section(section, info);
00327   // schedule file write.
00328   write_ini_file();
00329   return to_return;
00330 #endif
00331 }
00332 
00333 #ifdef __WIN32__
00334 bool ini_configurator::put_profile_string(const astring &section,
00335     const astring &entry, const astring &to_store)
00336 {
00337   return bool(WritePrivateProfileString(to_unicode_temp(section),
00338       entry.length() ? (flexichar *)to_unicode_temp(entry) : NIL,
00339       to_store.length() ? (flexichar *)to_unicode_temp(to_store) : NIL,
00340       to_unicode_temp(name())));
00341 }
00342 
00343 void ini_configurator::get_profile_string(const astring &section,
00344     const astring &entry, const astring &default_value,
00345     flexichar *return_buffer, int buffer_size)
00346 {
00347   GetPrivateProfileString(section.length() ?
00348       (flexichar *)to_unicode_temp(section) : NIL,
00349       entry.length() ? (flexichar *)to_unicode_temp(entry) : NIL,
00350       to_unicode_temp(default_value),
00351       return_buffer, buffer_size, to_unicode_temp(name()));
00352 }
00353 #endif
00354 
00355 } //namespace.
00356 
00357 
Generated on Sat Jan 28 04:22:17 2012 for hoople2 project by  doxygen 1.6.3