Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

VestaConfig.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 #include <Basics.H>
00020 #include <Generics.H>
00021 #include "VestaConfig.H"
00022 #include <OS.H>
00023 
00024 #include <sstream>
00025 
00026 using std::ostringstream;
00027 using std::istringstream;
00028 using std::ifstream;
00029 
00030 //  **********************************************************
00031 //  * Configuration parameters (documented in VestaConfig.H) *
00032 //  **********************************************************
00033 
00034 #define VESTA_CONFIG "VESTACONFIG"
00035 #define DEFAULT_CONFIG_FILE_NAME "vesta.cfg"
00036 #define DEFAULT_GLOBAL_CONFIG_FILE_NAME "/etc/vesta.cfg"
00037 #define HOME_DIRECTORY "HOME"
00038 
00039 #define MAX_INCLUDE_DEPTH 10
00040 
00041 //  *******************************************
00042 //  * Global variables (static class members) *
00043 //  *******************************************
00044 
00045 static Basics::mutex m;        // initialized by constructor
00046 static Text  location("");     // configuration file location - don't
00047                                // know where it is, yet
00048 static int   include_depth = 0;
00049 
00050 // Data structures holding parsed config file representation
00051 static TextTextTbl *tbl = NULL;
00052 typedef Table<Text,TextSeq *>::Default SectionVarsTbl;
00053 typedef Table<Text,TextSeq *>::Iterator SectionVarsIter;
00054 static SectionVarsTbl *vars_tbl = NULL;
00055 
00056 //  *************
00057 //  * Utilities *
00058 //  *************
00059 
00060 static void report_error(const Text &msg, int line_no = -1)
00061 {
00062   Text error_msg(msg);
00063   if (location != "") {
00064     ostringstream s;
00065     if (line_no >= 0) s << ", line #" << line_no;
00066     error_msg = error_msg + " (file: " + location + s.str() + ")";
00067   };
00068   throw(VestaConfig::failure(error_msg));
00069 }
00070 
00071 static void delete_parse()
00072 {
00073   include_depth = 0;
00074   if (tbl != NULL) { delete tbl; tbl = NULL; }
00075   if (vars_tbl != NULL)
00076     {
00077       SectionVarsIter it(vars_tbl);
00078       Text sect;
00079       TextSeq *vars;
00080       while(it.Next(sect, vars))
00081         delete vars;
00082       delete vars_tbl; vars_tbl = NULL;
00083     }
00084 }
00085 
00086 static Text make_key(const Text &section, const Text &name) {
00087   int len1 = section.Length();
00088   int len2 = name.Length() + 1; // include terminating NULL character
00089   char* buff = NEW_PTRFREE_ARRAY(char, 2 + len1 + len2);
00090   buff[0] = '['; buff[1+len1] = ']';
00091   memcpy(buff+1, section.cchars(), len1);
00092   memcpy(buff+2+len1, name.cchars(), len2);
00093   Text t(buff);
00094   delete [] buff;
00095   return t;
00096 }
00097 
00098 static void locate_file();
00099 static void parse();
00100 static void parse_file(const Text &file_name);
00101 static bool include_file(const Text &line);
00102 
00103 //  *********************************
00104 //  * Configuration file processing *
00105 //  *********************************
00106 
00107 void VestaConfig::set_location(const Text &file_name)
00108 {
00109   m.lock();
00110   delete_parse();
00111   location = file_name;
00112   m.unlock();
00113 }
00114 
00115 Text VestaConfig::get_location() throw(VestaConfig::failure)
00116 {
00117   Text loc;
00118   m.lock();
00119   try {
00120     locate_file();
00121   } catch (...) { m.unlock(); throw; }
00122   loc = location;
00123   m.unlock();
00124   return loc;
00125 }
00126 
00127 static void locate_file()
00128 {
00129   // Assumes m is locked.
00130   if (location != "") return;
00131 
00132   // set_location was never called
00133   char *v(getenv(VESTA_CONFIG));
00134   if (v != NULL) { location = v; return; }
00135 
00136   // The environment variable VESTA_CONFIG isn't defined.
00137   // Try home directory.
00138   struct stat dummy;
00139   v = getenv(HOME_DIRECTORY);
00140   if (v != NULL) {
00141     Text path = Text(v) + PathnameSep + DEFAULT_CONFIG_FILE_NAME;
00142     if (stat(path.chars(), &dummy) == SYSOK) { location = path; return; }
00143   }
00144 
00145   // Try default global configuration file.
00146   if (stat(DEFAULT_GLOBAL_CONFIG_FILE_NAME, &dummy) == SYSOK) {
00147       location = Text(DEFAULT_GLOBAL_CONFIG_FILE_NAME);
00148       return;
00149   }
00150 
00151   report_error("Can't find configuration file!");
00152 }
00153 
00154 static Text strip_whitespace(const Text &t)
00155 {
00156   int j = t.Length()-1;
00157   while (j > 0 && (t[j] == ' ' || t[j] == '\t')) j--;
00158   int i = 0;
00159   while (i <= j && (t[i] == ' ' || t[i] == '\t')) i++;
00160   return t.Sub(i, j-i+1);
00161 }
00162 
00163 static void parse()
00164 {
00165   // Assumes m is locked.
00166 
00167   if (tbl != NULL) return;     // already parsed
00168 
00169   locate_file();
00170 
00171   try {
00172     parse_file(location.chars());
00173   } catch (VestaConfig::failure f) { delete_parse(); throw(f); }
00174 
00175   if (tbl == NULL) report_error("Empty configuration file");
00176 }
00177 
00178 static void parse_file(const Text &file_name) {
00179   ifstream cf(file_name.chars());
00180   if (!cf) throw VestaConfig::failure("Can't open file: " + file_name);
00181 
00182   int line_no = 0;
00183   Text section;
00184 
00185   try {
00186     while (!cf.eof()) {
00187       Text line("");
00188       for (;;)  {
00189         char buff[100];
00190         (void) cf.getline(buff, sizeof(buff));
00191         line = line + buff;
00192 #if defined(__DECCXX)
00193         // Work around a bug in the Compaq C++ iostream library.
00194         // According to the ISO C++ standard, getline is supposed to
00195         // set the 'fail' state bit when the buffer is too small for
00196         // the line.  The Compaq C++ RTL doesn't do so.  This makes it
00197         // impossible to differentiate between a long line which we
00198         // should continue to read and a line which fills the buffer
00199         // an integral number of times.
00200         if (cf.gcount() != (sizeof(buff) - 1)) break;
00201 #else
00202         if(!cf.fail() || cf.eof()) break; 
00203 #endif
00204         // we overflowed the buffer, so compliant implementations of
00205         // basic_ios will set the fail bit.  We clear it now, as we're
00206         // about to read the next chunk of the line.
00207         cf.clear();
00208       }
00209 
00210       line_no++;
00211       line = strip_whitespace(line);
00212 
00213       if (!(line.Length() == 0 || line[0] == ';' ||
00214             (line.Length() > 1 && line[0] == '/' && line[1] == '/'))) {
00215         // Not a blank or comment line; analyze it.
00216         if (line[0] == '[') {
00217           if (line[line.Length()-1] != ']') 
00218             throw VestaConfig::failure("Missing ]");
00219           line = strip_whitespace(line.Sub(1, line.Length()-2));
00220           // Look for special case of [ include <filename> ]
00221           if (include_file(line))
00222               section = Text("");
00223           else
00224               section = line;
00225         }
00226         else {
00227           int i = line.FindChar('=');
00228           if (i < 0) throw VestaConfig::failure("Missing =");
00229           if (section.Empty())
00230             throw VestaConfig::failure("Missing section name");
00231           Text name = strip_whitespace(line.Sub(0, i));
00232           if (name.Empty())
00233             throw VestaConfig::failure("Missing name field");
00234           Text key(make_key(section, name));
00235           Text value(strip_whitespace(line.Sub(i+1)));
00236           if (tbl == NULL)
00237             tbl = NEW(TextTextTbl);
00238           bool intbl = tbl->Put(key, value);
00239           if(!intbl)
00240             {
00241               // No previous value for this setting.  Record it in
00242               // vars_tbl.
00243               if(vars_tbl == 0)
00244                 vars_tbl = NEW(SectionVarsTbl);
00245               TextSeq *sect_vars;
00246               if(!vars_tbl->Get(section, sect_vars))
00247                 {
00248                   sect_vars = NEW(TextSeq);
00249                   vars_tbl->Put(section, sect_vars);
00250                 }
00251               sect_vars->addhi(name);
00252             }
00253         }
00254       }
00255     }
00256   } catch (VestaConfig::failure f) {
00257     Text error_msg;
00258     ostringstream s;
00259     s << ", line #" << line_no;
00260     // Add backtrace line to error report.
00261     error_msg = f.msg + "\nfile: " + file_name + s.str();
00262     throw(VestaConfig::failure(error_msg));
00263   }
00264   
00265 }
00266 
00267 static bool include_file(const Text &line) {
00268   int i;
00269   const Text incl("include");
00270 
00271   if ((i = line.FindChar(' ')) < 0 || line.Sub(0, i) != incl) return false;
00272   Text file_name(strip_whitespace(line.Sub(i)));
00273   if (file_name.Length() == 0)
00274       return false;  // ordinary section named [include]
00275 
00276   include_depth++;
00277   if (include_depth > MAX_INCLUDE_DEPTH)
00278     throw VestaConfig::failure("[include] statements are nested too deeply.");
00279 
00280   parse_file(file_name);
00281 
00282   include_depth--;
00283 
00284   return true;  
00285 }
00286 
00287 //  *****************
00288 //  * Interrogation *
00289 //  *****************
00290 
00291 bool VestaConfig::get(const Text &section, const Text &name, Text &value)
00292      throw(VestaConfig::failure)
00293 {
00294   m.lock();
00295 
00296   try {
00297     parse();
00298   } catch (failure f) { m.unlock(); throw(f); }
00299 
00300   Text key(make_key(section, name));
00301 
00302   bool result = tbl->Get(key, value);
00303 
00304   m.unlock();
00305   return result;
00306 }
00307 
00308 Text VestaConfig::get_Text(const Text &section, const Text &name)
00309     throw(VestaConfig::failure)
00310 {
00311   Text value;
00312   if (get(section, name, value)) return value;
00313   report_error("The name '" + name + "' in section '" + section +
00314                 "' could not be found");
00315   return value; // not reached
00316 }
00317 
00318 int VestaConfig::get_int(const Text &section, const Text &name)
00319     throw(VestaConfig::failure)
00320 {
00321   Text txt(get_Text(section, name));
00322   istringstream ss(txt.chars());
00323   int value;
00324   if (!(ss >> value))
00325     report_error("The value for ([" + section + "], "
00326                  + name + ") isn't an integer");
00327   return value;
00328 }
00329 
00330 bool VestaConfig::get_bool(const Text &section, const Text &name)
00331   throw(VestaConfig::failure)
00332 {
00333   Text txt(get_Text(section, name));
00334 
00335   if((strcasecmp("yes", txt.cchars()) == 0) ||
00336      (strcasecmp("on", txt.cchars()) == 0) ||
00337      (strcasecmp("true", txt.cchars()) == 0))
00338     {
00339       return true;
00340     }
00341   else if((strcasecmp("no", txt.cchars()) == 0) ||
00342           (strcasecmp("off", txt.cchars()) == 0) ||
00343           (strcasecmp("false", txt.cchars()) == 0))
00344     {
00345       return false;
00346     }
00347   else
00348     {
00349       istringstream ss(txt.chars());
00350       int value;
00351       if (!(ss >> value))
00352         report_error("The value for ([" + section + "], "
00353                      + name + ") isn't a boolean");
00354       return (value != 0);
00355     }
00356 }
00357 
00358 bool VestaConfig::is_set(const Text &section, const Text &name)
00359   throw(VestaConfig::failure)
00360 {
00361   m.lock();
00362 
00363   try {
00364     parse();
00365   } catch (failure f) { m.unlock(); throw(f); }
00366 
00367   Text key(make_key(section, name));
00368 
00369   Text value;
00370   bool result = tbl->Get(key, value);
00371 
00372   m.unlock();
00373   return result;
00374 }
00375 
00376 float VestaConfig::get_float(const Text &section, const Text &name)
00377     throw(VestaConfig::failure)
00378 {
00379   Text txt(get_Text(section, name));
00380   istringstream ss(txt.chars());
00381   float value;
00382   if (!(ss >> value))
00383     report_error("The value for ([" + section + "], "
00384                  + name + ") isn't a floating-point number");
00385   return value;
00386 }
00387 
00388 TextSeq VestaConfig::sections()
00389   throw(VestaConfig::failure)
00390 {
00391   m.lock();
00392 
00393   try {
00394     parse();
00395   } catch (failure f) { m.unlock(); throw(f); }
00396 
00397   TextSeq result;
00398 
00399   if(vars_tbl != 0)
00400     {
00401       SectionVarsIter it(vars_tbl);
00402       Text sect;
00403       TextSeq *vars;
00404       while(it.Next(sect, vars))
00405         result.addhi(sect);
00406     }
00407 
00408   m.unlock();
00409 
00410   return result;
00411 }
00412 
00413 TextSeq VestaConfig::section_vars(const Text &section)
00414   throw(VestaConfig::failure)
00415 {
00416   m.lock();
00417 
00418   try {
00419     parse();
00420   } catch (failure f) { m.unlock(); throw(f); }
00421 
00422   TextSeq result, *sect_vars = 0;
00423 
00424   if((vars_tbl != 0) && vars_tbl->Get(section, sect_vars))
00425     result = *sect_vars;
00426 
00427   m.unlock();
00428 
00429   return result;
00430 }

Generated on Mon May 8 00:48:37 2006 for Vesta by  doxygen 1.4.2