ConfigHandler.cc

00001 //Copyright 2013 Ben Loer
00002 //This file is part of the ConfigHandler library
00003 //It is released under the terms of the GNU General Public License v3
00004 
00005 #include "ConfigHandler.hh"
00006 #include "CommandSwitchFunctions.hh"
00007 #include "MessageHandler.hh"
00008 #include <iostream>
00009 #include <exception>
00010 #include <stdexcept>
00011 
00012 #include <sys/ioctl.h>
00013 #include <stdio.h>
00014 #include <limits.h>
00015 #include <unistd.h>
00016 #include <fstream>
00017 
00018 class ParamHelpPrinter{
00019   const VParameterNode* p;
00020 public:
00021   ParamHelpPrinter(const VParameterNode* node) : p(node){}
00022   int operator()(const char*){ p->PrintHelp(); exit(0); }
00023 };
00024 
00025 int PrintProgramUsage(const char* dummy = "")
00026 {
00027   ConfigHandler::GetInstance()->PrintSwitches(true);
00028   return 0;
00029 }
00030 
00031 int PrintAnnotatedConfig(const char* dummy = "")
00032 {
00033   MessageHandler::GetInstance()->End();
00034   ConfigHandler::GetInstance()->PrintSwitches(false,std::cout, true);
00035   std::cout<<"\n#Full sample configuration file:\n"<<std::endl;
00036   ConfigHandler::GetInstance()->WriteTo(std::cout,true,0);
00037   std::cout<<"\n\n#End sample config file"<<std::endl;
00038   exit(0);
00039   return 0;
00040 }
00041 
00042 int NoOp(const char* dummy="") { return 0; }
00043 
00044 ConfigHandler::ConfigHandler() : 
00045   ParameterList("ConfigHandler","Global container for all parameters"), 
00046   _program_usage(""), _program_description(""),
00047   _notes(), _default_cfg_file(), _saved_cfg()
00048 {
00049   AddCommandSwitch(' ',"cfg","Load global configuration data from <file>",
00050                    CommandSwitch::LoadConfigFile(this),
00051                    "file");
00052   AddCommandSwitch(' ',"no-cfg","Prevent loading of any default config file",
00053                    NoOp);
00054   AddCommandSwitch(' ',"saved-config",
00055                    "Load saved configuration from previous run from <file>",
00056                    CommandSwitch::DefaultRead<std::string>(_saved_cfg),
00057                    "file");
00058   AddCommandSwitch(' ',"show-parameters",
00059                    "Interactively browse available configuration parameters",
00060                    ParamHelpPrinter(this) );
00061   AddCommandSwitch('h',"help","Display this help page",
00062                    PrintProgramUsage) ;
00063   AddCommandSwitch(' ',"print-options",
00064                    "Print all config options with annotated description",
00065                    PrintAnnotatedConfig );
00066   
00067   RegisterParameter("notes",_notes, "Generic notes about this run, etc");
00068   //RegisterParameter("saved-config",_saved_cfg,
00069   //"Previously saved configuration file");
00070   
00071   //fill the paths to look for config files
00072   _cfg_paths.clear();
00073   //first is the current working directory
00074   _cfg_paths.push_back(".");
00075   //next is the environement variable DAQMAN_CFGDIR if defined
00076   if( getenv("DAQMAN_CFGDIR") )
00077     _cfg_paths.push_back( getenv("DAQMAN_CFGDIR") );
00078   //then relative to the location of the current executable 
00079   char result[ PATH_MAX ];
00080   ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX );
00081   if(count > 0){
00082     std::string dirpart = std::string(result,count);
00083     if(dirpart.find_last_of('/') != std::string::npos)
00084       dirpart.erase(dirpart.find_last_of('/'));
00085     _cfg_paths.push_back( dirpart+"/../cfg");
00086   }
00087   //finally look in the location where we were compiled
00088 #ifdef DAQMANBUILDDIR
00089   _cfg_paths.push_back(std::string(DAQMANBUILDDIR)+"/cfg");
00090 #endif
00091   
00092 }
00093 
00094 ConfigHandler::~ConfigHandler()
00095 {
00096   //delete the switches
00097   for(SwitchSet::iterator it = _switches.begin(); it != _switches.end(); it++){
00098     delete (*it);
00099   }
00100 }
00101 
00102 std::string ConfigHandler::FindConfigFile(const std::string& fname)
00103 {
00104   //look for the file in each of the defined locations
00105   for(size_t i=0; i<_cfg_paths.size(); ++i){
00106     std::string filepath = _cfg_paths[i]+"/"+fname;
00107     Message(DEBUG)<<"Searching for file "<<fname<<" under path "
00108                  <<filepath<<"...\n";
00109     std::ifstream test(filepath.c_str());
00110     if(test.is_open()){
00111       Message(INFO)<<"Found config file "<<fname<<" at "<<filepath<<"\n";
00112       return filepath;
00113     }
00114   }
00115   
00116   //if we get here, we couldn'f find it
00117   Message(ERROR)<<"Unable to find config file "<<fname<<" under search paths\n";
00118   return "";
00119 }
00120 
00121 int ConfigHandler::RemoveCommandSwitch(char shortname, 
00122                                        const std::string& longname){
00123   
00124   for(SwitchSet::iterator it = _switches.begin(); it != _switches.end(); it++){
00125     if( (*it)->shortname == shortname && (*it)->longname == longname){
00126       delete (*it);
00127       _switches.erase(it);
00128       return 0;
00129     }
00130   }
00131   std::cerr<<"Unable to find switch "<<shortname<<","<<longname<<std::endl;
00132   return 1;
00133 }
00134 
00135 void ConfigHandler::PrintSwitches(bool quit, std::ostream& out, bool escape)
00136 {
00137   std::string endline = "\n";
00138   if(escape) endline+="#";
00139   
00140   out<<endline;
00141 
00142   if(quit)
00143     MessageHandler::GetInstance()->End();
00144   if(_program_usage != "")
00145     out<<"Usage: "<<_program_usage<<endline;
00146   if(_program_description != "") 
00147     out<<"Description: "<<_program_description<<endline;
00148   
00149   size_t maxlong = 0;
00150   size_t maxpar = 0;
00151   //struct winsize w;
00152   //ioctl(0,TIOCGWINSZ, &w);
00153   //size_t termsize=w.ws_col;
00154   size_t termsize = 80;
00155     
00156   
00157   
00158   for(SwitchSet::iterator it = _switches.begin(); it != _switches.end(); it++){
00159     maxlong = (maxlong > (*it)->longname.size() ? 
00160                    maxlong : (*it)->longname.size() );
00161     maxpar = (maxpar > (*it)->parameter.size() ? 
00162                    maxpar : (*it)->parameter.size() );
00163   }
00164   out<<"Available Command Line Options:"<<endline;
00165   for(SwitchSet::iterator it = _switches.begin(); it != _switches.end(); it++){
00166     VCommandSwitch* cmd = *it;
00167     out<<" ";
00168     if( cmd->shortname != ' ')
00169       out<<'-'<<cmd->shortname;
00170     else
00171       out<<"  ";
00172     
00173     if( cmd->shortname != ' ' && cmd->longname != "")
00174       out<<',';
00175     else 
00176       out<<' ';
00177     if( cmd->longname != "" ) 
00178       out<<"--"<<cmd->longname;
00179     else
00180       out<<"  ";
00181     for(size_t i=0; i < maxlong - cmd->longname.size(); i++)
00182       out<<' ';
00183     if( cmd->parameter != "" )
00184       out<<" <"<<cmd->parameter<<'>';
00185     else 
00186       out<<"   ";
00187     for(size_t i=0; i < maxpar - cmd->parameter.size(); i++)
00188       out<<' ';
00189     out<<"  ";
00190     //insert line-breaks into helptext
00191     int offset=1+2+1+2+maxlong+3+maxpar+2+2; //final 2 for indent
00192     std::string spaces;
00193     spaces.assign(offset,' ');
00194     size_t length=termsize-offset;
00195     size_t mypos = 0;
00196     while(cmd->helptext.size()-mypos > length ){
00197       //look for a space and replace it with a line-break
00198       std::string chunk = cmd->helptext.substr(mypos, length);
00199       size_t spacepos = chunk.rfind(' ');
00200       if(spacepos == std::string::npos)
00201         break;
00202       out<<chunk.substr(0,spacepos)<<(escape ? "\n#":"\n")<<spaces;
00203       mypos += spacepos+1;
00204     }
00205     out<<cmd->helptext.substr(mypos)<<endline;
00206   }
00207   out<<std::endl;
00208   if(quit)
00209     exit(0);
00210 }
00211 
00212 int ConfigHandler::ProcessCommandLine(int& argc, char** argv)
00213 {
00214   int status = 0;
00215   try{
00216     //first, see if the --cfg switch was specified; if not, load the default
00217     //certain arguments don't want a default config
00218     bool cfgswitchfound = false;
00219     bool skipcfgswitchfound = false;
00220     std::set<std::string> skipcfgargs;
00221     skipcfgargs.insert("--no-cfg");
00222     skipcfgargs.insert("-h");
00223     skipcfgargs.insert("--help");
00224     
00225     for(int arg = 1; arg<argc; arg++){
00226       if(std::string(argv[arg]) == "--cfg"){
00227         cfgswitchfound = true;
00228         break;
00229       }
00230       if(skipcfgargs.find(argv[arg]) != skipcfgargs.end())
00231         skipcfgswitchfound = true;
00232     }
00233     if(!cfgswitchfound){
00234       if(_default_cfg_file != "" && !skipcfgswitchfound){
00235         Message(INFO)<<"No --cfg switch found; reading default cfg file "
00236                      <<_default_cfg_file<<"...\n";
00237         status = CommandSwitch::LoadConfigFile(this)(_default_cfg_file.c_str());
00238       }
00239       else{
00240         Message(DEBUG)<<"No --cfg switch found and no default file specified; "
00241                      <<"using compiled defaults.\n";
00242       }
00243     }
00244     
00245     for(int arg = 1; arg<argc; arg++){
00246       if(status != 0){
00247         break;
00248 
00249       }
00250       if( argv[arg][0] != '-' ){
00251         //we're done with switches
00252         for( int i=arg; i<argc; i++){
00253           _cmd_args.push_back(argv[i]);
00254           argv[1 + i - arg] = argv[i];
00255         }
00256         //reset argc, argv to remove switches
00257         argc = 1 + argc - arg;
00258         break;
00259         //return _cmd_args.size();
00260       }
00261       
00262       if( argv[arg][1] == '-' ){
00263         //we are processing a long switch
00264         bool switchfound = false;
00265         std::string key(argv[arg]+2);
00266         for(SwitchSet::iterator it = _switches.begin(); it != _switches.end(); 
00267             it++){
00268           if( (*it)->longname != key)
00269             continue;
00270           
00271           switchfound = true;
00272           //check to see if we need a parameter
00273           bool need_parameter = ((*it)->parameter != "");
00274           bool next_is_parameter = ( arg != argc-1 && argv[arg+1][0] != '-');
00275           if( !need_parameter ){
00276             status = (*it)->Process(""); //needs dummy char*
00277             break;
00278           }
00279           else if( next_is_parameter){
00280             status = (*it)->Process(argv[++arg]);
00281             break;
00282           }
00283           else{
00284             //oops! we didn't get the parameter we need!
00285             Message e(EXCEPTION);
00286             e <<"Switch --" << key
00287               <<" is missing required parameter "
00288               << (*it)->parameter<<std::endl;
00289             throw std::invalid_argument(e.str());
00290           }
00291         }
00292         if(!switchfound){
00293           Message e(EXCEPTION);
00294           e<<argv[arg]<<" is not a valid switch!\n";
00295           throw std::invalid_argument(e.str());
00296         }
00297       }
00298       else{
00299         //we are dealing with short switches
00300         std::string shortkeys(argv[arg]+1);
00301         for(size_t i=0; i<shortkeys.size(); i++){
00302           bool switchfound = false;
00303           char key = shortkeys.at(i);
00304           for(SwitchSet::iterator it = _switches.begin(); 
00305               it != _switches.end(); it++){
00306             if( (*it)->shortname != key)
00307               continue;
00308             switchfound = true;
00309             //check to see if we need a parameter
00310             bool need_parameter = ((*it)->parameter != "");
00311             bool next_is_parameter = ( i == shortkeys.size() - 1 &&
00312                                        arg != argc-1 && 
00313                                        argv[arg+1][0] != '-' );
00314             if( !need_parameter ){
00315               status = (*it)->Process(""); //needs dummy char*
00316               break;
00317             }
00318             else if( next_is_parameter){
00319               status = (*it)->Process(argv[++arg]);
00320               break;
00321             }
00322             else{
00323               //oops! we didn't get the parameter we need!
00324               Message e(EXCEPTION);
00325               e <<"Switch -" << key
00326                 <<" is missing required parameter "
00327                 << (*it)->parameter<<std::endl;
00328               throw std::invalid_argument(e.str());
00329             }
00330           }
00331           if(!switchfound){
00332             Message e(EXCEPTION);
00333             e <<"-"<<key<<" is not a valid switch!\n";
00334             throw std::invalid_argument(e.str());
00335           }
00336         }
00337       }
00338     }
00339   } //end try block
00340   catch(std::exception& e){
00341     Message(EXCEPTION)<<"An error occurred processing command line arguments:\n"
00342                       <<e.what()<<std::endl;
00343     PrintSwitches(true);
00344   }
00345 
00346   if(status){
00347     Message(ERROR)<<"Problem encountered while processing command line\n";
00348     return status;
00349   }
00350   
00351   argc = _cmd_args.size() + 1;
00352   MessageHandler::GetInstance()->UpdateThreshold();
00353   return 0;
00354 }
00355 
00356 bool ConfigHandler::OrderCommandSwitchPointers::operator()
00357   (VCommandSwitch* a, VCommandSwitch* b )
00358 {
00359   //true is a < b; i.e. a comes before b
00360   //if shortname is blank, use the first letter of longname
00361   char shorta = (a->shortname == ' ' ? a->longname.at(0) : a->shortname);
00362   char shortb = (b->shortname == ' ' ? b->longname.at(0) : b->shortname);
00363   
00364   //first order by shortname
00365   if( shorta < shortb )
00366     return true;
00367   else if( shorta > shortb )
00368     return false;
00369   else {  
00370     //shortnames must be equal
00371     if( a->longname < b->longname)
00372       return true;
00373     else 
00374       return false;
00375   }
00376   //we shouldn't ever get here
00377   return false;
00378 }
00379 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on 20 Jun 2014 for daqman by  doxygen 1.6.1