ini_config.cpp

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

Generated on Thu Nov 20 04:29:04 2008 for HOOPLE Libraries by  doxygen 1.5.1