reSIProcate/rutil  9694
ConfigParse.cxx
Go to the documentation of this file.
00001 #include <iostream>
00002 #include <fstream>
00003 #include <iterator>
00004 #include <stdexcept>
00005 
00006 #include "rutil/ConfigParse.hxx"
00007 #include "rutil/Log.hxx"
00008 #include "rutil/Logger.hxx"
00009 #include "rutil/ParseBuffer.hxx"
00010 #include "rutil/WinLeakCheck.hxx"
00011 
00012 using namespace resip;
00013 using namespace std;
00014 
00015 #define RESIPROCATE_SUBSYSTEM Subsystem::SIP
00016 
00017 namespace resip
00018 {
00019 
00020 ConfigParse::ConfigParse(int argc, char** argv, const resip::Data& defaultConfigFilename)
00021 {
00022    parseCommandLine(argc, argv);  // will fill in mCmdLineConfigFilename if present
00023    if(mCmdLineConfigFilename.empty())
00024    {
00025       parseConfigFile(defaultConfigFilename);
00026    }
00027    else
00028    {
00029       parseConfigFile(mCmdLineConfigFilename);
00030    }
00031 }
00032 
00033 ConfigParse::ConfigParse()
00034 {
00035 }
00036 
00037 ConfigParse::~ConfigParse()
00038 {
00039 }
00040 
00041 bool 
00042 ConfigParse::getConfigValue(const resip::Data& name, resip::Data &value)
00043 {
00044    Data lowerName(name);  lowerName.lowercase();
00045    ConfigValuesMap::iterator it = mConfigValues.find(lowerName);
00046    if(it != mConfigValues.end())
00047    {
00048       value = it->second;
00049       return true;
00050    }
00051    // Not found
00052    return false;
00053 }
00054 
00055 Data 
00056 ConfigParse::getConfigData(const resip::Data& name, const resip::Data& defaultValue, bool useDefaultIfEmpty)
00057 {
00058    Data ret(defaultValue);
00059    if(getConfigValue(name, ret) && ret.empty() && useDefaultIfEmpty)
00060    {
00061       return defaultValue;
00062    }
00063    return ret;
00064 }
00065 
00066 bool 
00067 ConfigParse::getConfigValue(const resip::Data& name, bool &value)
00068 {
00069    Data lowerName(name);  lowerName.lowercase();
00070    ConfigValuesMap::iterator it = mConfigValues.find(lowerName);
00071    if(it != mConfigValues.end())
00072    {
00073       if(it->second == "1" || 
00074          isEqualNoCase(it->second, "true") || 
00075          isEqualNoCase(it->second, "on") || 
00076          isEqualNoCase(it->second, "enable"))
00077       {
00078          value = true;
00079          return true;
00080       }
00081       else if(it->second == "0" ||
00082               isEqualNoCase(it->second, "false") || 
00083               isEqualNoCase(it->second, "off") || 
00084               isEqualNoCase(it->second, "disable"))
00085       {
00086          value = false;
00087          return true;
00088       }
00089       cerr << "Invalid boolean setting:  " << name << " = " << it->second << ": Valid values are: 1,true,on,enable,0,false,off or disable" << endl;
00090       return false;
00091    }
00092    // Not found
00093    return false;
00094 }
00095 
00096 bool 
00097 ConfigParse::getConfigBool(const resip::Data& name, bool defaultValue)
00098 {
00099    bool ret = defaultValue;
00100    getConfigValue(name, ret);
00101    return ret;
00102 }
00103 
00104 bool 
00105 ConfigParse::getConfigValue(const resip::Data& name, unsigned long &value)
00106 {
00107    Data lowerName(name);  lowerName.lowercase();
00108    ConfigValuesMap::iterator it = mConfigValues.find(lowerName);
00109    if(it != mConfigValues.end())
00110    {
00111       value = it->second.convertUnsignedLong();
00112       return true;
00113    }
00114    // Not found
00115    return false;
00116 }
00117 
00118 unsigned long 
00119 ConfigParse::getConfigUnsignedLong(const resip::Data& name, unsigned long defaultValue)
00120 {
00121    unsigned long ret = defaultValue;
00122    getConfigValue(name, ret);
00123    return ret;
00124 }
00125 
00126 bool 
00127 ConfigParse::getConfigValue(const resip::Data& name, int &value)
00128 {
00129    Data lowerName(name);  lowerName.lowercase();
00130    ConfigValuesMap::iterator it = mConfigValues.find(lowerName);
00131    if(it != mConfigValues.end())
00132    {
00133       value = it->second.convertInt();
00134       return true;
00135    }
00136    // Not found
00137    return false;
00138 }
00139 
00140 
00141 int 
00142 ConfigParse::getConfigInt(const resip::Data& name, int defaultValue)
00143 {
00144    int ret = defaultValue;
00145    getConfigValue(name, ret);
00146    return ret;
00147 }
00148 
00149 bool
00150 ConfigParse::getConfigValue(const resip::Data& name, unsigned short &value)
00151 {
00152    Data lowerName(name);  lowerName.lowercase();
00153    ConfigValuesMap::iterator it = mConfigValues.find(lowerName);
00154    if(it != mConfigValues.end())
00155    {
00156       value = it->second.convertInt();
00157       return true;
00158    }
00159    // Not found
00160    return false;
00161 }
00162 
00163 
00164 unsigned short
00165 ConfigParse::getConfigUnsignedShort(const resip::Data& name, int defaultValue)
00166 {
00167    int ret = defaultValue;
00168    getConfigValue(name, ret);
00169    return ret;
00170 }
00171 
00172 bool 
00173 ConfigParse::getConfigValue(const resip::Data& name, std::vector<resip::Data> &value)
00174 {
00175    Data lowerName(name);  lowerName.lowercase();
00176    std::pair<ConfigValuesMap::iterator,ConfigValuesMap::iterator> valuesIts = mConfigValues.equal_range(lowerName);
00177    bool found = false;
00178    for (ConfigValuesMap::iterator it=valuesIts.first; it!=valuesIts.second; ++it)
00179    {
00180       found = true;
00181       ParseBuffer pb(it->second);
00182       Data item;
00183       while(!it->second.empty() && !pb.eof())
00184       {
00185          pb.skipWhitespace();
00186          const char *start = pb.position();
00187          pb.skipToOneOf(ParseBuffer::Whitespace, ",");  // allow white space 
00188          pb.data(item, start);
00189          value.push_back(item);
00190          if(!pb.eof())
00191          {
00192             pb.skipChar();
00193          }
00194       }
00195    }
00196 
00197    return found;
00198 }
00199 
00200 void 
00201 ConfigParse::insertConfigValue(const resip::Data& name, const resip::Data& value)
00202 {
00203    resip::Data lowerName(name);
00204    lowerName.lowercase();
00205    mConfigValues.insert(ConfigValuesMap::value_type(lowerName, value));
00206 }
00207 
00208 void 
00209 ConfigParse::parseCommandLine(int argc, char** argv)
00210 {
00211    int startingArgForNameValuePairs = 1;
00212    // First argument is the configuration filename - it is optional and is never proceeded with a - or /
00213 #ifdef WIN32
00214    if(argc >= 2 && argv[1][0] != '-' && argv[1][0] != '/')
00215 #else
00216    if(argc >= 2 && argv[1][0] != '-')
00217 #endif
00218    {
00219       mCmdLineConfigFilename = argv[1];
00220       startingArgForNameValuePairs = 2;
00221    }
00222 
00223    // Loop through command line arguments and process them
00224    for(int i = startingArgForNameValuePairs; i < argc; i++)
00225    {
00226       Data argData(argv[i]);
00227 
00228       // Process all commandNames that don't take values
00229       if(isEqualNoCase(argData, "-?") || 
00230          isEqualNoCase(argData, "--?") ||
00231          isEqualNoCase(argData, "--help") ||
00232          isEqualNoCase(argData, "/?"))
00233       {
00234          printHelpText(argc, argv);
00235          exit(1);
00236       }
00237       else if(argData.at(0) == '-' || argData.at(0) == '/')
00238       {
00239          Data name;
00240          Data value;
00241          ParseBuffer pb(argData);
00242 
00243          try
00244          {
00245             pb.skipChars(Data::toBitset("-/"));  // Skip any leading -'s or /'s
00246             const char * anchor = pb.position();
00247             pb.skipToOneOf("=:");
00248             if(!pb.eof())
00249             {
00250                pb.data(name, anchor);
00251                pb.skipChar();
00252                anchor = pb.position();
00253                pb.skipToEnd();
00254                pb.data(value, anchor);
00255 
00256                //cout << "Command line Name='" << name << "' value='" << value << "'" << endl;
00257                insertConfigValue(name, value);
00258             }
00259             else
00260             {
00261                cerr << "Invalid command line parameters:"  << endl;
00262                cerr << " Name/Value pairs must contain an = or a : between the name and the value" << endl;
00263                exit(-1);
00264             }
00265          }
00266          catch(BaseException& ex)
00267          {
00268             cerr << "Invalid command line parameters:"  << endl;
00269             cerr << " Exception parsing Name/Value pairs: " << ex << endl;
00270             exit(-1);
00271          }
00272       }
00273       else
00274       {
00275          cerr << "Invalid command line parameters:"  << endl;
00276          cerr << " Name/Value pairs must be prefixed with either a -, --, or a /" << endl;
00277          exit(-1);
00278       }
00279    }
00280 }
00281 
00282 void
00283 ConfigParse::parseConfigFile(const Data& filename)
00284 {
00285    ifstream configFile(filename.c_str());
00286    
00287    if(!configFile)
00288    {
00289       throw Exception("Error opening/reading configuration file", __FILE__, __LINE__);
00290    }
00291 
00292    string sline;
00293    const char * anchor;
00294    while(getline(configFile, sline)) 
00295    {
00296       Data line(sline);
00297       Data name;
00298       Data value;
00299       ParseBuffer pb(line);
00300 
00301       pb.skipWhitespace();
00302       anchor = pb.position();
00303       if(pb.eof() || *anchor == '#') continue;  // if line is a comment or blank then skip it
00304 
00305       // Look for end of name
00306       pb.skipToOneOf("= \t");
00307       pb.data(name,anchor);
00308       if(*pb.position()!='=') 
00309       {
00310          pb.skipToChar('=');
00311       }
00312       pb.skipChar('=');
00313       pb.skipWhitespace();
00314       anchor = pb.position();
00315       if(!pb.eof())
00316       {
00317          pb.skipToOneOf("\r\n");
00318          pb.data(value, anchor);
00319       }
00320       //cout << "Config file Name='" << name << "' value='" << value << "'" << endl;
00321       insertConfigValue(name, value);
00322    }
00323 }
00324 
00325 EncodeStream& 
00326 operator<<(EncodeStream& strm, const ConfigParse& config)
00327 {
00328    ConfigParse::ConfigValuesMap::const_iterator it = config.mConfigValues.begin();
00329    for(; it != config.mConfigValues.end(); it++)
00330    {
00331       strm << it->first << " = " << it->second << endl;
00332    }
00333    return strm;
00334 }
00335 
00336 }
00337 
00338 /* ====================================================================
00339  * The Vovida Software License, Version 1.0 
00340  * 
00341  * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
00342  * 
00343  * Redistribution and use in source and binary forms, with or without
00344  * modification, are permitted provided that the following conditions
00345  * are met:
00346  * 
00347  * 1. Redistributions of source code must retain the above copyright
00348  *    notice, this list of conditions and the following disclaimer.
00349  * 
00350  * 2. Redistributions in binary form must reproduce the above copyright
00351  *    notice, this list of conditions and the following disclaimer in
00352  *    the documentation and/or other materials provided with the
00353  *    distribution.
00354  * 
00355  * 3. The names "VOCAL", "Vovida Open Communication Application Library",
00356  *    and "Vovida Open Communication Application Library (VOCAL)" must
00357  *    not be used to endorse or promote products derived from this
00358  *    software without prior written permission. For written
00359  *    permission, please contact vocal@vovida.org.
00360  *
00361  * 4. Products derived from this software may not be called "VOCAL", nor
00362  *    may "VOCAL" appear in their name, without prior written
00363  *    permission of Vovida Networks, Inc.
00364  * 
00365  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
00366  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00367  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
00368  * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
00369  * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
00370  * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
00371  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00372  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00373  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00374  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00375  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
00376  * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00377  * DAMAGE.
00378  * 
00379  * ====================================================================
00380  * 
00381  * This software consists of voluntary contributions made by Vovida
00382  * Networks, Inc. and many individuals on behalf of Vovida Networks,
00383  * Inc.  For more information on Vovida Networks, Inc., please see
00384  * <http://www.vovida.org/>.
00385  *
00386  */