BESKeys.cc

Go to the documentation of this file.
00001 // BESKeys.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include "config.h"
00034 
00035 #ifdef __cplusplus
00036 extern "C" {
00037 #include <sys/types.h>
00038 #include "regex.h"
00039 }
00040 #endif
00041 
00042 #include <cerrno>
00043 #include <cstring>
00044 
00045 #if HAVE_UNISTD_H
00046 #include <unistd.h>
00047 #endif
00048 
00049 #include "BESKeys.h"
00050 #include "BESUtil.h"
00051 #include "BESFSDir.h"
00052 #include "BESFSFile.h"
00053 #include "BESInternalFatalError.h"
00054 #include "BESSyntaxUserError.h"
00055 
00056 #define BES_INCLUDE_KEY "BES.Include"
00057 
00074 BESKeys::BESKeys( const string &keys_file_name )
00075     : _keys_file( 0 ),
00076       _keys_file_name( keys_file_name ),
00077       _the_keys( 0 ),
00078       _own_keys( true )
00079 {
00080     _the_keys = new map<string,vector<string> >;
00081     initialize_keys( ) ;
00082 }
00083 
00084 BESKeys::BESKeys( const string &keys_file_name, map<string,vector<string> > *keys)
00085     : _keys_file( 0 ),
00086       _keys_file_name( keys_file_name ),
00087       _the_keys( keys ),
00088       _own_keys( false )
00089 {
00090     initialize_keys( ) ;
00091 }
00092 
00095 BESKeys::~BESKeys()
00096 {
00097     clean() ;
00098 }
00099 
00100 void
00101 BESKeys::initialize_keys( )
00102 {
00103     _keys_file = new ifstream( _keys_file_name.c_str() ) ;
00104     int myerrno = errno ;
00105     if( !(*_keys_file) )
00106     {
00107         char path[500] ;
00108         getcwd( path, sizeof( path ) ) ;
00109         string s = string("BES: fatal, cannot open BES configuration file ")
00110                    + _keys_file_name + ": " ;
00111         char *err = strerror( myerrno ) ;
00112         if( err )
00113             s += err ;
00114         else
00115             s += "Unknown error" ;
00116 
00117         s += (string)".\n" + "The current working directory is " + path + "\n" ;
00118         throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00119     }
00120 
00121     try
00122     {
00123         load_keys() ;
00124     }
00125     catch( BESError &e )
00126     {
00127         // be sure we're throwing a fatal error, since the BES can't run
00128         // within the configuration file
00129         clean() ;
00130         throw BESInternalFatalError( e.get_message(),
00131                                      e.get_file(), e.get_line() ) ;
00132     }
00133     catch(...)
00134     {
00135         clean() ;
00136         string s = (string)"Undefined exception while trying to load keys "
00137                    + "from bes configuration file " + _keys_file_name ;
00138         throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00139     }
00140 }
00141 
00142 void
00143 BESKeys::clean()
00144 {
00145     if( _keys_file )
00146     {
00147         _keys_file->close() ;
00148         delete _keys_file ;
00149     }
00150     if( _the_keys && _own_keys )
00151     {
00152         delete _the_keys ;
00153     }
00154 }
00155 
00156 void
00157 BESKeys::load_keys()
00158 {
00159     char buffer[255];
00160     string key,value;
00161     while(!(*_keys_file).eof())
00162     {
00163         if((*_keys_file).getline(buffer,255))
00164         {
00165             bool addto = false ;
00166             if( break_pair( buffer, key, value, addto ) )
00167             {
00168                 if( key == BES_INCLUDE_KEY )
00169                 {
00170                     load_include_files( value ) ;
00171                 }
00172                 else
00173                 {
00174                     set_key( key, value, addto ) ;
00175                 }
00176             }
00177         }
00178     }
00179 }
00180 
00181 // The string contained in the character buffer b should be of the
00182 // format key=value or key+=value. The pair is broken apart, storing the
00183 // key in the key parameter and the value of the key in the value
00184 // parameter. If += is used, then the value should be added to the value
00185 // of key, not replacing.
00186 //
00187 // It used to be that we would validate the key=value line. Instead,
00188 // anything after the equal sign is considered the value of the key.
00189 inline bool
00190 BESKeys::break_pair( const char* b, string& key, string &value, bool &addto )
00191 {
00192     addto = false ;
00193     // Ignore comments and lines with only spaces
00194     if( b && ( b[0] != '#' ) && ( !only_blanks( b ) ) )
00195     {
00196         register size_t l = strlen( b ) ;
00197         if( l > 1 )
00198         {
00199             int pos = 0 ;
00200             bool done = false ;
00201             for( register size_t j = 0; j < l && !done ; j++ )
00202             {
00203                 if( b[j] == '=' )
00204                 {
00205                     if( !addto ) pos = j ;
00206                     else
00207                     {
00208                         if( pos != j-1 )
00209                         {
00210                             string s = string("BES: Invalid entry ") + b
00211                                        + " in configuration file "
00212                                        + _keys_file_name
00213                                        + " '+' character found in variable name"
00214                                        + " or attempting '+=' with space"
00215                                        + " between the characters.\n" ;
00216                             throw BESInternalFatalError( s, __FILE__, __LINE__);
00217                         }
00218                     }
00219                     done = true ;
00220                 }
00221                 else if( b[j] == '+' )
00222                 {
00223                     addto = true ;
00224                     pos = j ;
00225                 }
00226             }
00227             if( !done )
00228             {
00229                 string s = string("BES: Invalid entry ") + b
00230                            + " in configuration file "
00231                            + _keys_file_name + ": "
00232                            + " '=' character not found.\n" ;
00233                 throw BESInternalFatalError( s, __FILE__, __LINE__);
00234             }
00235 
00236             string s = b ;
00237             key = s.substr( 0, pos ) ;
00238             BESUtil::removeLeadingAndTrailingBlanks( key ) ;
00239             if( addto ) value = s.substr( pos+2, s.size() ) ;
00240             else value = s.substr( pos+1, s.size() ) ;
00241             BESUtil::removeLeadingAndTrailingBlanks( value ) ;
00242 
00243             return true;
00244         }
00245 
00246         return false;
00247     }
00248 
00249     return false;
00250 }
00251 
00261 void
00262 BESKeys::load_include_files( const string &files )
00263 {
00264     string newdir ;
00265     BESFSFile allfiles( files ) ;
00266 
00267     // If the files specified begin with a /, then use that directory
00268     // instead of the current keys file directory.
00269     if( !files.empty() && files[0] == '/' )
00270     {
00271         newdir = allfiles.getDirName() ;
00272     }
00273     else
00274     {
00275         // determine the directory of the current keys file. All included
00276         // files will be relative to this file.
00277         BESFSFile currfile( _keys_file_name ) ;
00278         string currdir = currfile.getDirName() ;
00279 
00280         string alldir = allfiles.getDirName() ;
00281 
00282         if( ( currdir == "./" || currdir == "." )
00283             && ( alldir == "./" || alldir == "." ) ) newdir = "./" ;
00284         else newdir = currdir + "/" + alldir ;
00285     }
00286 
00287     // load the files one at a time. If the directory doesn't exist,
00288     // then don't load any configuration files
00289     BESFSDir fsd( newdir, allfiles.getFileName() ) ;
00290     BESFSDir::fileIterator i = fsd.beginOfFileList() ;
00291     BESFSDir::fileIterator e = fsd.endOfFileList() ;
00292     for( ; i != e; i++ )
00293     {
00294         load_include_file( (*i).getFullPath() ) ;
00295     }
00296 }
00297 
00304 void
00305 BESKeys::load_include_file( const string &file )
00306 {
00307     // make sure the file exists and is readable
00308     // throws exception if unable to read
00309     BESKeys tmp( file, _the_keys ) ;
00310 }
00311 
00312 bool
00313 BESKeys::only_blanks(const char *line)
00314 {
00315     int val;
00316     regex_t rx;
00317     string expr = "[^[:space:]]" ;
00318     val = regcomp( &rx, expr.c_str(), REG_ICASE ) ;
00319 
00320     if( val != 0 )
00321     {
00322         string s = (string)"Regular expression " + expr
00323                    + " did not compile correctly "
00324                    + " in configuration file " + _keys_file_name ;
00325         throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00326     }
00327     val = regexec( &rx, line, 0, 0, REG_NOTBOL ) ;
00328     if( val == 0 )
00329     {
00330         regfree( &rx ) ;
00331         return false ;
00332     }
00333     else
00334     {
00335         if( val == REG_NOMATCH )
00336         {
00337             regfree( &rx ) ;
00338             return true ;
00339         }
00340         else if( val == REG_ESPACE )
00341         {
00342             string s = (string)"Execution of regular expression out of space"
00343                        + " in configuration file " + _keys_file_name ;
00344             throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00345         }
00346         else
00347         {
00348             string s = (string)"Execution of regular expression has unknown "
00349                        + " problem in configuration file " + _keys_file_name ;
00350             throw BESInternalFatalError( s, __FILE__, __LINE__ ) ;
00351         }
00352     }
00353 }
00354 
00371 void
00372 BESKeys::set_key( const string &key, const string &val, bool addto )
00373 {
00374     map<string,vector<string> >::iterator i ;
00375     i = _the_keys->find( key ) ;
00376     if( i == _the_keys->end() )
00377     {
00378         vector<string> vals ;
00379         (*_the_keys)[key] = vals ;
00380     }
00381     if( !addto ) (*_the_keys)[key].clear() ;
00382     if( !val.empty() )
00383     {
00384         (*_the_keys)[key].push_back( val ) ;
00385     }
00386 }
00387 
00399 void
00400 BESKeys::set_key( const string &pair )
00401 {
00402     string key ;
00403     string val ;
00404     bool addto = false ;
00405     break_pair( pair.c_str(), key, val, addto ) ;
00406     set_key( key, val, addto ) ;
00407 }
00408 
00423 void
00424 BESKeys::get_value( const string& s, string &val, bool &found ) 
00425 {
00426     found = false ;
00427     map<string,vector<string> >::iterator i ;
00428     i = _the_keys->find( s ) ;
00429     if( i != _the_keys->end() )
00430     {
00431         found = true ;
00432         if( (*i).second.size() > 1 )
00433         {
00434             string err = string("Multiple values for the key ") + s
00435                          + " found, should only be one." ;
00436             throw BESSyntaxUserError( err, __FILE__, __LINE__ ) ;
00437         }
00438         if( (*i).second.size() == 1 )
00439         {
00440             val = (*i).second[0] ;
00441         }
00442         else
00443         {
00444             val = "" ;
00445         }
00446     }
00447 }
00448 
00460 void
00461 BESKeys::get_values( const string& s, vector<string> &vals, bool &found ) 
00462 {
00463     found = false ;
00464     map<string,vector<string> >::iterator i ;
00465     i = _the_keys->find( s ) ;
00466     if( i != _the_keys->end() )
00467     {
00468         found = true ;
00469         vals = (*i).second ;
00470     }
00471 }
00472 
00479 void
00480 BESKeys::dump( ostream &strm ) const
00481 {
00482     strm << BESIndent::LMarg << "BESKeys::dump - ("
00483                              << (void *)this << ")" << endl ;
00484     BESIndent::Indent() ;
00485     strm << BESIndent::LMarg << "key file:" << _keys_file_name << endl ;
00486     if( _keys_file && *_keys_file )
00487     {
00488         strm << BESIndent::LMarg << "key file is valid" << endl ;
00489     }
00490     else
00491     {
00492         strm << BESIndent::LMarg << "key file is NOT valid" << endl ;
00493     }
00494     if( _the_keys && _the_keys->size() )
00495     {
00496         strm << BESIndent::LMarg << "    keys:" << endl ;
00497         BESIndent::Indent() ;
00498         Keys_citer i = _the_keys->begin() ;
00499         Keys_citer ie = _the_keys->end() ;
00500         for( ; i != ie; i++ )
00501         {
00502             strm << BESIndent::LMarg << (*i).first << ":" << endl ;
00503             BESIndent::Indent() ;
00504             vector<string>::const_iterator v = (*i).second.begin() ;
00505             vector<string>::const_iterator ve = (*i).second.end() ;
00506             for( ; v != ve; v++ )
00507             {
00508                 strm << (*v) << endl ;
00509             }
00510             BESIndent::UnIndent() ;
00511         }
00512         BESIndent::UnIndent() ;
00513     }
00514     else
00515     {
00516         strm << BESIndent::LMarg << "keys: none" << endl ;
00517     }
00518     BESIndent::UnIndent() ;
00519 }
00520