00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
00043
00044
00045 static Basics::mutex m;
00046 static Text location("");
00047
00048 static int include_depth = 0;
00049
00050
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
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 §ion, const Text &name) {
00087 int len1 = section.Length();
00088 int len2 = name.Length() + 1;
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
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
00130 if (location != "") return;
00131
00132
00133 char *v(getenv(VESTA_CONFIG));
00134 if (v != NULL) { location = v; return; }
00135
00136
00137
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
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
00166
00167 if (tbl != NULL) return;
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
00194
00195
00196
00197
00198
00199
00200 if (cf.gcount() != (sizeof(buff) - 1)) break;
00201 #else
00202 if(!cf.fail() || cf.eof()) break;
00203 #endif
00204
00205
00206
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
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
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
00242
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
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;
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
00289
00290
00291 bool VestaConfig::get(const Text §ion, 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 §ion, 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;
00316 }
00317
00318 int VestaConfig::get_int(const Text §ion, 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 §ion, 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 §ion, 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 §ion, 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 §ion)
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 }