src/app/GApplicationPars.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *               GApplicationPars.cpp - Application parameters             *
00003  * ----------------------------------------------------------------------- *
00004  *  copyright (C) 2010-2016 by Juergen Knoedlseder                         *
00005  * ----------------------------------------------------------------------- *
00006  *                                                                         *
00007  *  This program is free software: you can redistribute it and/or modify   *
00008  *  it under the terms of the GNU General Public License as published by   *
00009  *  the Free Software Foundation, either version 3 of the License, or      *
00010  *  (at your option) any later version.                                    *
00011  *                                                                         *
00012  *  This program is distributed in the hope that it will be useful,        *
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
00015  *  GNU General Public License for more details.                           *
00016  *                                                                         *
00017  *  You should have received a copy of the GNU General Public License      *
00018  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
00019  *                                                                         *
00020  ***************************************************************************/
00021 /**
00022  * @file GApplicationPars.cpp
00023  * @brief Application parameter container class implementation
00024  * @author Juergen Knoedlseder
00025  */
00026 
00027 /* __ Includes ___________________________________________________________ */
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 #include <pwd.h>           // user/passwd function
00032 #include <fcntl.h>         // for file locking
00033 #include <unistd.h>        // access() function
00034 #include <sys/stat.h>      // mkdir() function
00035 #include <cstdlib>         // std::getenv() function
00036 #include <cstdio>          // std::fopen(), etc. functions
00037 #include "GTools.hpp"
00038 #include "GException.hpp"
00039 #include "GFilename.hpp"
00040 #include "GApplicationPars.hpp"
00041 
00042 /* __ Method name definitions ____________________________________________ */
00043 #define G_ACCESS                 "GApplicationPars::operator[](std::string&)"
00044 #define G_AT                    "GApplicationPar& GApplicationPars::at(int&)"
00045 #define G_APPEND                 "GApplicationPars::append(GApplicationPar&)"
00046 #define G_INSERT1          "GApplicationPar& GApplicationPars::insert(int&, "\
00047                                                           "GApplicationPar&)"
00048 #define G_INSERT2  "GApplicationPar& GApplicationPars::insert(std::string&, "\
00049                                                           "GApplicationPar&)"
00050 #define G_REMOVE1                            "GApplicationPars::remove(int&)"
00051 #define G_REMOVE2                    "GApplicationPars::remove(std::string&)"
00052 #define G_EXTEND                "GApplicationPars::extend(GApplicationPars&)"
00053 #define G_LOAD1                          "GApplicationPars::load(GFilename&)"
00054 #define G_LOAD2                         "GApplicationPars::load(GFilename&, "\
00055                                                  "std::vector<std::string>&)"
00056 #define G_SAVE                           "GApplicationPars::save(GFilename&)"
00057 #define G_OUTPATH                   "GApplicationPars::outpath(std::string&)"
00058 #define G_READ                         "GApplicationPars::read(std::string&)"
00059 #define G_WRITE                       "GApplicationPars::write(std::string&)"
00060 #define G_PARSE                                   "GApplicationPars::parse()"
00061 
00062 /* __ Macros _____________________________________________________________ */
00063 
00064 /* __ Coding definitions _________________________________________________ */
00065 #define G_LOCK_PARFILE                //!< Enables parfile locking
00066 //#define G_CHECK_LOCK_PARFILE          //!< Enables check of parfile locking
00067 
00068 /* __ Debug definitions __________________________________________________ */
00069 
00070 
00071 /*==========================================================================
00072  =                                                                         =
00073  =                         Constructors/destructors                        =
00074  =                                                                         =
00075  ==========================================================================*/
00076 
00077 /***********************************************************************//**
00078  * @brief Void constructor
00079  ***************************************************************************/
00080 GApplicationPars::GApplicationPars(void)
00081 {
00082     // Initialise private members for clean destruction
00083     init_members();
00084 
00085     // Return
00086     return;
00087 }
00088 
00089 
00090 /***********************************************************************//**
00091  * @brief Parameter file constructor
00092  *
00093  * @param[in] filename Parameter filename. 
00094  ***************************************************************************/
00095 GApplicationPars::GApplicationPars(const GFilename& filename)
00096 {
00097     // Initialise private members for clean destruction
00098     init_members();
00099 
00100     // Load parameters
00101     load(filename);
00102 
00103     // Return
00104     return;
00105 }
00106 
00107 /***********************************************************************//**
00108  * @brief Parameter constructor
00109  *
00110  * @param[in] filename Parameter filename. 
00111  * @param[in] args Command line arguments. 
00112  ***************************************************************************/
00113 GApplicationPars::GApplicationPars(const GFilename&                filename,
00114                                    const std::vector<std::string>& args)
00115 {
00116     // Initialise private members for clean destruction
00117     init_members();
00118 
00119     // Load parameters
00120     load(filename, args);
00121 
00122     // Return
00123     return;
00124 }
00125 
00126 
00127 /***********************************************************************//**
00128  * @brief Copy constructor
00129  *
00130  * @param[in] pars Parameter container.
00131  ***************************************************************************/
00132 GApplicationPars::GApplicationPars(const GApplicationPars& pars)
00133 {
00134     // Initialise private members for clean destruction
00135     init_members();
00136 
00137     // Copy members
00138     copy_members(pars);
00139 
00140     // Return
00141     return;
00142 }
00143 
00144 
00145 /***********************************************************************//**
00146  * @brief Destructor
00147  ***************************************************************************/
00148 GApplicationPars::~GApplicationPars(void)
00149 {
00150     // Free members
00151     free_members();
00152 
00153     // Return
00154     return;
00155 }
00156 
00157 
00158 /*==========================================================================
00159  =                                                                         =
00160  =                                Operators                                =
00161  =                                                                         =
00162  ==========================================================================*/
00163 
00164 /***********************************************************************//**
00165  * @brief Assignment operator
00166  *
00167  * @param[in] pars Parameter container.
00168  * @return Parameter container.
00169  ***************************************************************************/
00170 GApplicationPars& GApplicationPars::operator=(const GApplicationPars& pars)
00171 {
00172     // Execute only if object is not identical
00173     if (this != &pars) {
00174 
00175         // Free members
00176         free_members();
00177 
00178         // Initialise private members for clean destruction
00179         init_members();
00180 
00181         // Copy members
00182         copy_members(pars);
00183 
00184     } // endif: object was not identical
00185 
00186     // Return
00187     return *this;
00188 }
00189 
00190 
00191 /***********************************************************************//**
00192  * @brief Returns reference to parameter
00193  *
00194  * @param[in] name Parameter name.
00195  * @return Parameter.
00196  *
00197  * @exception GException::invalid_argument
00198  *            Parameter with specified name not found in container.
00199  ***************************************************************************/
00200 GApplicationPar& GApplicationPars::operator[](const std::string& name)
00201 {
00202     // Get parameter index
00203     int index = get_index(name);
00204 
00205     // Throw exception if parameter name has not been found
00206     if (index == -1) {
00207         std::string msg = "Parameter \""+name+"\" has not been found in "
00208                           "parameter file. Please specify a valid parameter "
00209                           "name.";
00210         throw GException::invalid_argument(G_ACCESS, msg);
00211     }
00212 
00213     // Return reference
00214     return (m_pars[index]);
00215 }
00216 
00217 
00218 /***********************************************************************//**
00219  * @brief Returns reference to parameter (const version)
00220  *
00221  * @param[in] name Parameter name.
00222  * @return Parameter.
00223  *
00224  * @exception GException::invalid_argument
00225  *            Parameter with specified name not found in container.
00226  ***************************************************************************/
00227 const GApplicationPar& GApplicationPars::operator[](const std::string& name) const
00228 {
00229     // Get parameter index
00230     int index = get_index(name);
00231 
00232     // Throw exception if parameter name has not been found
00233     if (index == -1) {
00234         std::string msg = "Parameter \""+name+"\" has not been found in "
00235                           "parameter file. Please specify a valid parameter "
00236                           "name.";
00237         throw GException::invalid_argument(G_ACCESS, msg);
00238     }
00239 
00240     // Return reference
00241     return (m_pars[index]);
00242 }
00243 
00244 
00245 /*==========================================================================
00246  =                                                                         =
00247  =                             Public methods                              =
00248  =                                                                         =
00249  ==========================================================================*/
00250 
00251 /***********************************************************************//**
00252  * @brief Clear parameter container
00253  ***************************************************************************/
00254 void GApplicationPars::clear(void)
00255 {
00256     // Free members
00257     free_members();
00258 
00259     // Init members
00260     init_members();
00261 
00262     // Return
00263     return;
00264 }
00265 
00266 
00267 /***********************************************************************//**
00268  * @brief Clone parameter container
00269  *
00270  * @return Pointer to deep copy of parameter container.
00271  ***************************************************************************/
00272 GApplicationPars* GApplicationPars::clone(void) const
00273 {
00274     return (new GApplicationPars(*this));
00275 }
00276 
00277 
00278 /***********************************************************************//**
00279  * @brief Returns reference to parameter
00280  *
00281  * @param[in] index Parameter index [0,...,size()-1].
00282  *
00283  * @exception GException::out_of_range
00284  *            Parameter index is out of range.
00285  ***************************************************************************/
00286 GApplicationPar& GApplicationPars::at(const int& index)
00287 {
00288     // Compile option: raise an exception if index is out of range
00289     #if defined(G_RANGE_CHECK)
00290     if (index < 0 || index >= size()) {
00291         throw GException::out_of_range(G_AT, "Parameter index", index, size());
00292     }
00293     #endif
00294 
00295     // Return reference
00296     return (m_pars[index]);
00297 }
00298 
00299 
00300 /***********************************************************************//**
00301  * @brief Returns reference to parameter (const version)
00302  *
00303  * @param[in] index Parameter index [0,...,size()-1].
00304  *
00305  * @exception GException::out_of_range
00306  *            Parameter index is out of range.
00307  ***************************************************************************/
00308 const GApplicationPar& GApplicationPars::at(const int& index) const
00309 {
00310     // Compile option: raise an exception if index is out of range
00311     #if defined(G_RANGE_CHECK)
00312     if (index < 0 || index >= size()) {
00313         throw GException::out_of_range(G_AT, "Parameter index", index, size());
00314     }
00315     #endif
00316 
00317     // Return reference
00318     return (m_pars[index]);
00319 }
00320 
00321 
00322 /***********************************************************************//**
00323  * @brief Append parameter to container
00324  *
00325  * @param[in] par Parameter.
00326  * @return Reference to deep copy of appended parameter.
00327  *
00328  * @exception GException::invalid_value
00329  *            Parameter with same name exists already in container.
00330  *
00331  * This method appends one parameter to the parameter container. The
00332  * parameter provided to the method can be deleted after calling this method.
00333  ***************************************************************************/
00334 GApplicationPar& GApplicationPars::append(const GApplicationPar& par)
00335 {
00336     // Check if a parameter with specified name does not yet exist
00337     int inx = get_index(par.name());
00338     if (inx != -1) {
00339         std::string msg =
00340             "Attempt to append parameter with name \""+par.name()+"\" in"
00341             " parameter container, but a parameter with the same name exists"
00342             " already at index "+gammalib::str(inx)+" in the container.\n"
00343             "Every parameter in the parameter container needs a unique name.";
00344         throw GException::invalid_value(G_APPEND, msg);
00345     }
00346     
00347     // Append parameter
00348     m_pars.push_back(par);
00349 
00350     // Get reference of appended parameter
00351     GApplicationPar& parameter = m_pars[m_pars.size()-1];
00352     
00353     // Build parameter file line
00354     size_t      start = 0;
00355     size_t      stop  = 0;
00356     std::string line  = parline(parameter, &start, &stop);
00357     
00358     // Append parameter file line and attributes
00359     m_line.push_back(m_parfile.size());
00360     m_parfile.push_back(line);
00361     m_vstart.push_back(start);
00362     m_vstop.push_back(stop);
00363 
00364     // Return parameter reference
00365     return parameter;
00366 }
00367 
00368 
00369 /***********************************************************************//**
00370  * @brief Append standard parameters to container
00371  *
00372  * This method appends the standard parameters to the parameter container.
00373  * Standard parameters are: "chatter", "clobber", "debug" and "mode".
00374  ***************************************************************************/
00375 void GApplicationPars::append_standard(void)
00376 {
00377     // Append standard parameters
00378     append(GApplicationPar("chatter","i","h","2","0","4","Chattiness of output"));
00379     append(GApplicationPar("clobber","b","h","yes","","","Overwrite existing output files with new output files?"));
00380     append(GApplicationPar("debug","b","h","no","","","Debugging mode activated"));
00381     append(GApplicationPar("mode","s","h","ql","","","Mode of automatic parameters"));
00382 
00383     // Return
00384     return;
00385 }
00386 
00387 
00388 /***********************************************************************//**
00389  * @brief Insert parameter into container
00390  *
00391  * @param[in] index Parameter index [0,...,size()-1].
00392  * @param[in] par Parameter.
00393  * @return Reference to deep copy of inserted parameter.
00394  *
00395  * Inserts a parameter into the container before the parameter with the
00396  * specified @p index.
00397  ***************************************************************************/
00398 GApplicationPar& GApplicationPars::insert(const int& index, const GApplicationPar& par)
00399 {
00400     // Compile option: raise exception if index is out of range
00401     #if defined(G_RANGE_CHECK)
00402     if (is_empty()) {
00403         if (index > 0) {
00404             throw GException::out_of_range(G_INSERT1, "Parameter index", index, size());
00405         }
00406     }
00407     else {
00408         if (index < 0 || index >= size()) {
00409             throw GException::out_of_range(G_INSERT1, "Parameter index", index, size());
00410         }
00411     }
00412     #endif
00413 
00414     // Check if a parameter with specified name does not yet exist
00415     int inx = get_index(par.name());
00416     if (inx != -1) {
00417         std::string msg =
00418             "Attempt to insert parameter with name \""+par.name()+"\" in"
00419             " parameter container before index "+gammalib::str(index)+
00420             ", but a parameter with the same name exists already at index "+
00421             gammalib::str(inx)+" in the container.\n"
00422             "Every parameter in the parameter container needs a unique name.";
00423         throw GException::invalid_value(G_INSERT1, msg);
00424     }
00425 
00426     // Inserts parameter
00427     m_pars.insert(m_pars.begin()+index, par);
00428 
00429     // Get reference of appended parameter
00430     GApplicationPar& parameter = m_pars[m_pars.size()-1];
00431 
00432     // Build parameter file line
00433     size_t      start = 0;
00434     size_t      stop  = 0;
00435     std::string line  = parline(parameter, &start, &stop);
00436 
00437     // Determine at which line number of the parameter file the parameter
00438     // should be inserted
00439     int line_number = m_line[index];
00440 
00441     // Insert parameter file line and parameter attributes
00442     m_parfile.insert(m_parfile.begin()+line_number, line);
00443     m_line.insert(m_line.begin()+index, line_number);
00444     m_vstart.insert(m_vstart.begin()+index, start);
00445     m_vstop.insert(m_vstop.begin()+index, stop);
00446 
00447     // Increment the line numbers for all parameters after the inserted one
00448     for (int i = index+1; i < size(); ++i) {
00449         m_line[i]++;
00450     }
00451 
00452     // Return parameter reference
00453     return parameter;
00454 }
00455 
00456 
00457 /***********************************************************************//**
00458  * @brief Insert parameter into container
00459  *
00460  * @param[in] name Parameter name.
00461  * @param[in] par Parameter.
00462  * @return Reference to deep copy of inserted parameter.
00463  *
00464  * @exception GException::invalid_argument
00465  *            Parameter with specified name not found in container.
00466  * @exception GException::invalid_value
00467  *            Name of parameter exists already in container.
00468  *
00469  * Inserts a parameter into the container before the parameter with the
00470  * specified @p name.
00471  ***************************************************************************/
00472 GApplicationPar& GApplicationPars::insert(const std::string& name, const GApplicationPar& par)
00473 {
00474     // Get parameter index
00475     int index = get_index(name);
00476 
00477     // Throw exception if parameter name was not found
00478     if (index == -1) {
00479         std::string msg = "Parameter \""+name+"\" has not been found in"
00480                           " parameter file.\n"
00481                           "Please specify a valid parameter name.";
00482         throw GException::invalid_argument(G_INSERT2, msg);
00483     }
00484 
00485     // Insert by index and return parameter reference
00486     return (insert(index, par));
00487 }
00488 
00489 
00490 /***********************************************************************//**
00491  * @brief Remove parameter from container
00492  *
00493  * @param[in] index Parameter index [0,...,size()-1].
00494  *
00495  * @exception GException::out_of_range
00496  *            Parameter index is out of range.
00497  *
00498  * Remove parameter with specified @p index from container.
00499  ***************************************************************************/
00500 void GApplicationPars::remove(const int& index)
00501 {
00502     // Compile option: raise exception if index is out of range
00503     #if defined(G_RANGE_CHECK)
00504     if (index < 0 || index >= size()) {
00505         throw GException::out_of_range(G_REMOVE1, "Parameter index", index, size());
00506     }
00507     #endif
00508 
00509     // Erase parameter from container
00510     m_pars.erase(m_pars.begin() + index);
00511 
00512     // Remove parameter file line and parameter attributes
00513     m_parfile.erase(m_parfile.begin() + m_line[index]);
00514     m_line.erase(m_line.begin() + index);
00515     m_vstart.erase(m_vstart.begin() + index);
00516     m_vstop.erase(m_vstop.begin() + index);
00517 
00518     // Decrement the line numbers for all parameters after the removed one
00519     for (int i = index; i < size(); ++i) {
00520         m_line[i]--;
00521     }
00522     
00523     // Return
00524     return;
00525 }
00526 
00527 
00528 /***********************************************************************//**
00529  * @brief Remove parameter from container
00530  *
00531  * @param[in] name Parameter name.
00532  *
00533  * @exception GException::invalid_argument
00534  *            Parameter with specified name not found in container.
00535  *
00536  * Remove parameter with specified @p name from container.
00537  ***************************************************************************/
00538 void GApplicationPars::remove(const std::string& name)
00539 {
00540     // Get parameter index
00541     int index = get_index(name);
00542 
00543     // Throw exception if parameter name was not found
00544     if (index == -1) {
00545         std::string msg = "Parameter \""+name+"\" has not been found in"
00546                           " parameter file.\n"
00547                           "Please specify a valid parameter name.";
00548         throw GException::invalid_argument(G_REMOVE2, msg);
00549     }
00550 
00551     // Remove by index
00552     remove(index);
00553 
00554     // Return
00555     return;
00556 }
00557 
00558 
00559 /***********************************************************************//**
00560  * @brief Append parameter container
00561  *
00562  * @param[in] pars Parameter container.
00563  *
00564  * Append parameter container to the container.
00565  ***************************************************************************/
00566 void GApplicationPars::extend(const GApplicationPars& pars)
00567 {
00568     // Do nothing if parameter container is empty
00569     if (!pars.is_empty()) {
00570 
00571         // Get size. Note that we extract the size first to avoid an
00572         // endless loop that arises when a container is appended to
00573         // itself.
00574         int num = pars.size();
00575 
00576         // Reserve enough space
00577         reserve(size() + num);
00578 
00579         // Loop over all parameters and append copies 
00580         for (int i = 0; i < num; ++i) {
00581 
00582             // Check if parameter name does not yet exist
00583             int inx = get_index(pars[i].name());
00584             if (inx != -1) {
00585                 std::string msg =
00586                     "Attempt to append parameter with name \""+pars[i].name()+
00587                     "\" to parameter container, but a parameter with the same name"
00588                     " exists already at index "+gammalib::str(inx)+" in the"
00589                     " container.\n"
00590                     "Every parameter in the parameter container needs a unique"
00591                     " name.";
00592                 throw GException::invalid_value(G_EXTEND, msg);
00593             }
00594 
00595             // Append parameter to container
00596             append(pars[i]);
00597 
00598         } // endfor: looped over all parameters
00599 
00600     } // endif: parameter container was not empty
00601     
00602     // Return
00603     return;
00604 }
00605 
00606 
00607 /***********************************************************************//**
00608  * @brief Check parameter exists
00609  *
00610  * @param[in] name Parameter name.
00611  * @return True if parameter with specified name exists, false otherwise.
00612  *
00613  * Determines whether a parameter with the specified name exists already in
00614  * the parameter container.
00615  ***************************************************************************/
00616 bool GApplicationPars::contains(const std::string& name) const
00617 {
00618     // Get parameter index
00619     int inx = get_index(name);
00620     
00621     // Return test result
00622     return (inx != -1);
00623 }
00624 
00625 
00626 /***********************************************************************//**
00627  * @brief Load parameters
00628  *
00629  * @param[in] filename Parameter filename.
00630  *
00631  * @exception GException::par_file_not_found
00632  *            Parameter file not found.
00633  *
00634  * Loads all parameters from parameter file.
00635  ***************************************************************************/
00636 void GApplicationPars::load(const GFilename& filename)
00637 {
00638     // Reset parameters
00639     m_parfile.clear();
00640 
00641     // Get path to parameter file for input
00642     std::string path = inpath(filename.url());
00643     if (path.length() == 0) {
00644         throw GException::par_file_not_found(G_LOAD1, filename.url());
00645     }
00646 
00647     // Read parfile
00648     read(path);
00649 
00650     // Parse parfile
00651     parse();
00652 
00653     // Return
00654     return;
00655 }
00656 
00657 
00658 /***********************************************************************//**
00659  * @brief Load parameters
00660  *
00661  * @param[in] filename Parameter filename.
00662  * @param[in] args Command line arguments. 
00663  *
00664  * @exception GException::par_file_not_found
00665  *            Parameter file not found.
00666  * @exception GException::bad_cmdline_argument
00667  *            Invalid command line argument encountered.
00668  *
00669  * Loads all parameters from parameter file. Parameters are overwritten by
00670  * the values specified in the command line arguments.
00671  ***************************************************************************/
00672 void GApplicationPars::load(const GFilename&                filename,
00673                             const std::vector<std::string>& args)
00674 {
00675     // Reset parameters
00676     m_parfile.clear();
00677 
00678     // Get path to parameter file for input
00679     std::string path = inpath(filename.url());
00680     if (path.length() == 0) {
00681         throw GException::par_file_not_found(G_LOAD2, filename.url());
00682     }
00683 
00684     // Read parfile
00685     read(path);
00686 
00687     // Parse parfile
00688     parse();
00689 
00690     // Overwrite parameter values that are specified in the command line
00691     for (int i = 1; i < args.size(); ++i) {
00692 
00693         // Extract parameter name and value (empty values are permitted)
00694         size_t pos = args[i].find("=");
00695         if (pos == std::string::npos) {
00696             throw GException::bad_cmdline_argument(G_LOAD2, args[i],
00697                                                    "no \"=\" specified");
00698         }
00699         std::string name  = args[i].substr(0, pos);
00700         std::string value = args[i].substr(pos+1);
00701         if (name.length() < 1) {
00702             throw GException::bad_cmdline_argument(G_LOAD2, args[i],
00703                                        "no parameter name before \"=\"");
00704         }
00705         
00706         // Check if parameter exists
00707         if (!contains(name)) {
00708             throw GException::bad_cmdline_argument(G_LOAD2, args[i],
00709                                   "invalid parameter name \""+name+"\"");
00710         }
00711 
00712         // Assign value
00713         try {
00714             (*this)[name].value(value);
00715         }
00716         catch (GException::par_error &e) {
00717             throw GException::bad_cmdline_argument(G_LOAD2, args[i]);
00718         }
00719 
00720         // Set mode to hidden to prevent querying the parameter
00721         if ((*this)[name].mode() == "q") {
00722             (*this)[name].mode("h");
00723         }
00724         else if ((*this)[name].mode() == "ql") {
00725             (*this)[name].mode("hl");
00726         }
00727         else if ((*this)[name].mode() == "lq") {
00728             (*this)[name].mode("lh");
00729         }
00730 
00731     } // endfor: looped over all parameters
00732 
00733     // Return
00734     return;
00735 }
00736 
00737 
00738 /***********************************************************************//**
00739  * @brief Save parameters
00740  *
00741  * @param[in] filename Parameter filename.
00742  *
00743  * @exception GException::par_file_not_found
00744  *            No valid directory to write the parameter file has been found.
00745  ***************************************************************************/
00746 void GApplicationPars::save(const GFilename& filename)
00747 {
00748     // Get path to parameter file for output
00749     std::string path = outpath(filename.url());
00750     if (path.size() == 0) {
00751         throw GException::par_file_not_found(G_SAVE, filename.url());
00752     }
00753 
00754     // Update parameter file
00755     update();
00756 
00757     // Write parfile
00758     write(path);
00759 
00760     // Return
00761     return;
00762 }
00763 
00764 
00765 /***********************************************************************//**
00766  * @brief Print parameters
00767  *
00768  * @param[in] chatter Chattiness (defaults to NORMAL).
00769  * @return String containing parameter information.
00770  ***************************************************************************/
00771 std::string GApplicationPars::print(const GChatter& chatter) const
00772 {
00773     // Initialise result string
00774     std::string result;
00775 
00776     // Continue only if chatter is not silent
00777     if (chatter != SILENT) {
00778 
00779         // Append header
00780         result.append("=== GApplicationPars ===");
00781 
00782         // Append parameters
00783         for (int i = 0; i < size(); ++i) {
00784             result.append("\n"+m_pars[i].print(chatter));
00785         }
00786 
00787     } // endif: chatter was not silent
00788 
00789     // Return result
00790     return result;
00791 }
00792 
00793 
00794 /*==========================================================================
00795  =                                                                         =
00796  =                             Private methods                             =
00797  =                                                                         =
00798  ==========================================================================*/
00799 
00800 /***********************************************************************//**
00801  * @brief Initialise class members
00802  ***************************************************************************/
00803 void GApplicationPars::init_members(void)
00804 {
00805     // Initialise members
00806     m_parfile.clear();
00807     m_pars.clear();
00808     m_line.clear();
00809     m_vstart.clear();
00810     m_vstop.clear();
00811     m_mode = "h";
00812 
00813     // Return
00814     return;
00815 }
00816 
00817 
00818 /***********************************************************************//**
00819  * @brief Copy class members
00820  *
00821  * @param[in] pars Object from which members which should be copied.
00822  ***************************************************************************/
00823 void GApplicationPars::copy_members(const GApplicationPars& pars)
00824 {
00825     // Copy attributes
00826     m_parfile = pars.m_parfile;
00827     m_pars    = pars.m_pars;
00828     m_line    = pars.m_line;
00829     m_vstart  = pars.m_vstart;
00830     m_vstop   = pars.m_vstop;
00831     m_mode    = pars.m_mode;
00832 
00833     // Return
00834     return;
00835 }
00836 
00837 
00838 /***********************************************************************//**
00839  * @brief Delete class members
00840  ***************************************************************************/
00841 void GApplicationPars::free_members(void)
00842 {
00843     // Return
00844     return;
00845 }
00846 
00847 
00848 /***********************************************************************//**
00849  * @brief Determine filepath for parameter file input
00850  *
00851  * @param[in] filename Parameter filename to search for.
00852  *
00853  * Locates parameter file for input. 
00854  * The parameter file is first searched in the directories that are listed
00855  * in the PFILES environment variable. Directories may be separated by : or
00856  * by ; in PFILES.
00857  *
00858  * If the PFILES environment variable is not set the parameter file is
00859  * searched in the users pfiles directory.
00860  *
00861  * If still no parameter file is found, the parameter file is searched (in
00862  * the given order) in ${GAMMALIB}/syspfiles and in ${prefix}/syspfiles,
00863  * where ${prefix} is the path to the GammaLib installation.
00864  ***************************************************************************/
00865 std::string GApplicationPars::inpath(const std::string& filename) const
00866 {
00867     // Allocate result path
00868     std::string path;
00869 
00870     // Search for parameter file in PFILES directories if the PFILES
00871     // environment variable has been set
00872     char* ptr = std::getenv("PFILES");
00873     if (ptr != NULL) {
00874 
00875         // Extract directories from PFILES environment variable
00876         std::string              pfiles = ptr;
00877         std::vector<std::string> dirs   = gammalib::split(pfiles, ":;");
00878 
00879         // Search for first occurence of parameter file
00880         for (int i = 0; i < dirs.size(); ++i) {
00881 
00882             // Build filename
00883             std::string fname = dirs[i] + "/" + filename;
00884 
00885             // If file is accessible for reading then exit loop
00886             if (access(fname.c_str(), R_OK) == 0) {
00887                 path = fname;
00888                 break;
00889             }
00890 
00891         } // endfor: searched all directories given in PFILES
00892 
00893     } // endif: PFILES environment variable has been set
00894 
00895     // ... otherwise, if no PFILES environment variable has been set
00896     // then search in users pfiles directory
00897     else {
00898         uid_t uid         = geteuid();
00899         struct passwd* pw = getpwuid(uid);
00900         if (pw != NULL) {
00901             std::string fname = std::string(pw->pw_dir) + "/pfiles/" + filename;
00902             if (access(fname.c_str(), R_OK) == 0) {
00903                 path = fname;
00904             }
00905         }    
00906     } // endif: searched in users pfiles directory
00907 
00908     // If we have no valid path so far then search file within GAMMALIB
00909     // repository (${GAMMALIB}/syspfiles)
00910     if (path.size() == 0) {
00911         ptr = std::getenv("GAMMALIB");
00912         if (ptr != NULL) {
00913             std::string fname = std::string(ptr) + "/syspfiles/" + filename;
00914             if (access(fname.c_str(), R_OK) == 0) {
00915                 path = fname;
00916             }
00917         }
00918     }
00919 
00920     // If we have no valid path so far then search file within GammaLib
00921     // package (${prefix}/syspfiles)
00922     #ifdef PACKAGE_PREFIX
00923     if (path.size() == 0) {
00924         std::string fname = std::string(PACKAGE_PREFIX) + "/syspfiles/" +
00925                             filename;
00926         if (access(fname.c_str(), R_OK) == 0) {
00927             path = fname;
00928         }
00929     }
00930     #endif
00931 
00932     //printf("parfile=%s\n", path.c_str());
00933 
00934     // Return path
00935     return path;
00936 }
00937 
00938 
00939 /***********************************************************************//**
00940  * @brief Determine filepath for parameter file output
00941  *
00942  * @param[in] filename Parameter filename.
00943  *
00944  * @exception GException::home_not_found
00945  *            Unable to determine users home directory.
00946  * @exception GException::could_not_create_pfiles
00947  *            Unable to create pfiles directory.
00948  * @exception GException::pfiles_not_accessible
00949  *            Unable to make pfiles directory accessible to user.
00950  *
00951  * Searchs for first writable directory listed in PFILES environment
00952  * variable. If PFILES is not set then use pfiles directory in users
00953  * home directory. If pfiles directory does not exist then create it.
00954  * If directory exists but is not writable then make it writable.
00955  ***************************************************************************/
00956 std::string GApplicationPars::outpath(const std::string& filename) const
00957 {
00958    // Allocate result path
00959     std::string path;
00960 
00961     // Search for writeable PFILES directories
00962     char* ptr = std::getenv("PFILES");
00963     if (ptr != NULL) {
00964 
00965         // Extract directories from PFILES environment variable
00966         std::string              pfiles = ptr;
00967         std::vector<std::string> dirs   = gammalib::split(pfiles, ":;");
00968 
00969         // Search for first writeable
00970         for (int i = 0; i < dirs.size(); ++i) {
00971 
00972             // If directory is accessible for writing then exit loop
00973             if (access(dirs[i].c_str(), W_OK) == 0) {
00974                 path = dirs[i] + "/" + filename;
00975                 break;
00976             }
00977 
00978         }
00979     } // endif: PFILES environment variable exists
00980 
00981     // If no valid directory is found in PFILES environment variable then 
00982     // use pfiles directory in users home directory.
00983     if (path.size() == 0) {
00984 
00985         // Get users home directory
00986         uid_t uid         = geteuid();
00987         gid_t gid         = getegid();
00988         struct passwd* pw = getpwuid(uid);
00989         if (pw == NULL) {
00990             throw GException::home_not_found(G_OUTPATH);
00991         }
00992 
00993         // Set path
00994         path = std::string(pw->pw_dir) + "/pfiles";
00995 
00996         // If directory does not exist then create it
00997         if (access(path.c_str(), F_OK) != 0) {
00998             if (mkdir(path.c_str(), 
00999                 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
01000                 throw GException::could_not_create_pfiles(G_OUTPATH, path);
01001             }
01002         }
01003 
01004         // If directory exists but is not writable then make it writable
01005         else if (access(path.c_str(), W_OK) != 0) {
01006             if (chown(path.c_str(), uid, gid) != 0 ||
01007                 chmod(path.c_str(), 
01008                       S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
01009                 throw GException::pfiles_not_accessible(G_OUTPATH, path);
01010             }
01011         }
01012 
01013         // Append filename
01014         path = path + "/" + filename;
01015 
01016     } // endif: no valid directory found in PFILES
01017 
01018     // Return path
01019     return path;
01020 }
01021 
01022 
01023 /***********************************************************************//**
01024  * @brief Read parameter file
01025  *
01026  * @param[in] filename Parameter filename (absolut path).
01027  *
01028  * @exception GException::par_file_open_error
01029  *            Unable to open parameter file (read access requested).
01030  *
01031  * Read all lines of the parameter file. Each line is terminated by a newline
01032  * character. The file will be locked to avoid simultaneous access by another
01033  * process.
01034  ***************************************************************************/
01035 void GApplicationPars::read(const std::string& filename)
01036 {
01037     // Allocate line buffer
01038     const int n = 1000; 
01039     char  line[n];
01040 
01041     // Trying to get file lock
01042     #if defined(G_LOCK_PARFILE)
01043     struct flock lock;
01044     lock.l_type   = F_RDLCK;  // Want a read lock
01045     lock.l_whence = SEEK_SET; // Want beginning of file
01046     lock.l_start  = 0;        // No offset, lock entire file ...
01047     lock.l_len    = 0;        // ... to the end
01048     lock.l_pid    = getpid(); // Current process ID
01049     int fd;
01050     if ((fd = open(filename.c_str(), O_RDONLY)) == -1) {
01051         throw GException::par_file_open_error(G_READ, filename,
01052               "Could not open file for locking.");
01053     }
01054     #if defined(G_CHECK_LOCK_PARFILE)
01055     if (fcntl(fd, F_SETLKW, &lock) == -1) { // F_SETLKW: wait until unlocked
01056         throw GException::par_file_open_error(G_READ, filename,
01057               "Could not get a lock on the file.");
01058     }
01059     #else
01060     fcntl(fd, F_SETLKW, &lock);
01061     #endif
01062     #endif
01063 
01064     // Open parameter file
01065     FILE* fptr = fopen(filename.c_str(), "r");
01066     if (fptr == NULL) {
01067         throw GException::par_file_open_error(G_READ, filename);
01068     }
01069 
01070     // Read lines
01071     while (fgets(line, n, fptr) != NULL) {
01072         m_parfile.push_back(std::string(line));
01073     }
01074 
01075     // Close file
01076     fclose(fptr);
01077 
01078     // Unlock file
01079     #if defined(G_LOCK_PARFILE)
01080     if (fd != -1) {
01081         lock.l_type = F_UNLCK;
01082         #if defined(G_CHECK_LOCK_PARFILE)
01083         if (fcntl(fd, F_SETLK, &lock) == -1) {
01084             throw GException::par_file_open_error(G_READ, filename,
01085                   "Could not unlock the file.");
01086         }
01087         #else
01088         fcntl(fd, F_SETLK, &lock);
01089         #endif
01090         close(fd);
01091     }
01092     #endif
01093 
01094     // Return
01095     return;
01096 }
01097 
01098 
01099 /***********************************************************************//**
01100  * @brief Write parameter file
01101  *
01102  * @param[in] filename Parameter filename (absolut path).
01103  *
01104  * @exception GException::par_file_open_error
01105  *            Unable to open parameter file (write access requested).
01106  *
01107  * Writes all lines of the parameter file. The file will be locked to avoid
01108  * simultaneous access by another process.
01109  ***************************************************************************/
01110 void GApplicationPars::write(const std::string& filename) const
01111 {
01112     // Trying to get file lock. We have to do this after opening the file
01113     // using the fopen function, as the file may not exist, hence it needs
01114     // to be created first.
01115     #if defined(G_LOCK_PARFILE)
01116     struct flock lock;
01117     lock.l_type   = F_WRLCK;  // Want a write lock
01118     lock.l_whence = SEEK_SET; // Want beginning of file
01119     lock.l_start  = 0;        // No offset, lock entire file ...
01120     lock.l_len    = 0;        // ... to the end
01121     lock.l_pid    = getpid(); // Current process ID
01122     int  fd;
01123     if ((fd = open(filename.c_str(), O_WRONLY)) != -1) {
01124         #if defined(G_CHECK_LOCK_PARFILE)
01125         if (fcntl(fd, F_SETLKW, &lock) == -1) { // F_SETLKW: wait until unlocked
01126             throw GException::par_file_open_error(G_WRITE, filename,
01127                   "Could not get a lock on the file.");
01128         }
01129         #else
01130         fcntl(fd, F_SETLKW, &lock);
01131         #endif
01132     }
01133     #endif
01134 
01135     // Open parameter file.
01136     FILE* fptr = fopen(filename.c_str(), "w");
01137     if (fptr == NULL) {
01138         throw GException::par_file_open_error(G_WRITE, filename);
01139     }
01140 
01141     // If file is not locked then lock it now.
01142     #if defined(G_LOCK_PARFILE)
01143     if (fd == -1) {
01144         if ((fd = open(filename.c_str(), O_WRONLY)) != -1) {
01145             #if defined(G_CHECK_LOCK_PARFILE)
01146             if (fcntl(fd, F_SETLKW, &lock) == -1) { // F_SETLKW: wait until unlocked
01147                 fclose(fptr);
01148                 throw GException::par_file_open_error(G_WRITE, filename,
01149                       "Could not get a lock on the file.");
01150             }
01151             #else
01152             fcntl(fd, F_SETLKW, &lock);
01153             #endif
01154         }
01155     }
01156     #endif
01157 
01158     // Write lines
01159     for (int i = 0; i < m_parfile.size(); ++i) {
01160         fprintf(fptr, "%s", m_parfile[i].c_str());
01161     }
01162 
01163     // Close file
01164     fclose(fptr);
01165 
01166     // Unlock file
01167     #if defined(G_LOCK_PARFILE)
01168     if (fd != -1) {
01169         lock.l_type = F_UNLCK;
01170         #if defined(G_CHECK_LOCK_PARFILE)
01171         if (fcntl(fd, F_SETLK, &lock) == -1) {
01172             throw GException::par_file_open_error(G_WRITE, filename,
01173                   "Could not unlock the file.");
01174         }
01175         #else
01176         fcntl(fd, F_SETLK, &lock);
01177         #endif
01178         close(fd);
01179     }
01180     #endif
01181 
01182     // Return
01183     return;
01184 }
01185 
01186 
01187 /***********************************************************************//**
01188  * @brief Parse parameter file
01189  *
01190  * @exception GException::par_file_syntax_error
01191  *            Syntax error encountered in parameter file.
01192  *
01193  * The parameter type has to be one b,i,r,s,f,fr,fw,fe,fn. The fr,fw,fe,fn
01194  * types test for read access, write access, file existence, and file
01195  * absence, respectively.
01196  * The parameter mode has to be one of a,h,l,q,hl,ql,lh,lq. For mode 'a' the
01197  * effective mode equals to the value given by the mode parameter, if it
01198  * exists. Without the presence of the mode parameter the effective mode
01199  * will be 'h'.
01200  ***************************************************************************/
01201 void GApplicationPars::parse(void)
01202 {
01203     // Preset effective mode to 'hidden'
01204     m_mode = "h";
01205 
01206     // Parse all lines
01207     for (int i = 0; i < m_parfile.size(); ++i) {
01208 
01209         // Get line without any leading and trailing whitespace
01210         std::string line = gammalib::strip_whitespace(m_parfile[i]);
01211 
01212         // If line contains a single linefeed then skip the line
01213         if (line.length() == 1 && line[0] == '\n')
01214             continue;
01215 
01216         // If line is empty or if line starts with # then skip the line
01217         if (line.length() == 0 || line[0] == '#') {
01218             continue;
01219         }
01220 
01221         // Get the 7 text fields of valid a parameter line
01222         std::string fields[7];
01223         int         quotes = 0;
01224         size_t      start  = 0;
01225         size_t      end    = line.length() - 1;
01226         int         index  = 0;
01227         size_t      vstart = 0;
01228         size_t      vstop  = 0;
01229         for (size_t pos = 0; pos < line.length(); ++pos) {
01230 
01231             // Toggle quotes
01232             if (line[pos] == '"') {
01233                 quotes = 1-quotes;
01234             }
01235 
01236             // Search for comma only if we are outside quotes. If comma is
01237             // found or end of line is reached then extract a field and start
01238             // searching again from the position following the comma. Strip
01239             // leading and trailing quotes from the fields.
01240             if (quotes == 0) {
01241                 if (line[pos] == ',' || pos == end) {
01242                     if (index < 7) {
01243                         fields[index] = 
01244                           gammalib::strip_chars(gammalib::strip_whitespace(line.substr(start, 
01245                                                                  pos-start)), "\"");
01246                         if (index == 3) {
01247                             vstart = start;
01248                             vstop  = pos;
01249                         }
01250                         start = pos + 1;
01251                     }
01252                     index++;
01253                 }
01254             }
01255 
01256         } // endfor: looped over line
01257 
01258         // Throw an error if quotes are not balanced
01259         if (quotes != 0) {
01260             throw GException::par_file_syntax_error(G_PARSE, 
01261                                                     gammalib::strip_chars(line,"\n"),
01262                                                     "quotes are not balanced");
01263         }
01264 
01265         // Throw an error if line has not 7 fields
01266         if (index != 7) {
01267             throw GException::par_file_syntax_error(G_PARSE, 
01268                                                     gammalib::strip_chars(line,"\n"),
01269                                  "found "+gammalib::str(index)+" fields, require 7");
01270         }
01271 
01272         // Verify if parameter name does not yet exist
01273         if (contains(fields[0])) {
01274             throw GException::par_file_syntax_error(G_PARSE, 
01275                                                     gammalib::strip_chars(line,"\n"),
01276                           "redefinition of parameter name \""+fields[0]+"\"");
01277         }
01278 
01279         // Add parameter
01280         try {
01281             m_pars.push_back(GApplicationPar(fields[0], fields[1], fields[2],
01282                                              fields[3], fields[4], fields[5],
01283                                              fields[6]));
01284             m_line.push_back(i);
01285             m_vstart.push_back(vstart);
01286             m_vstop.push_back(vstop);
01287         }
01288         catch (GException::par_error &e) {
01289             throw GException::par_file_syntax_error(G_PARSE, 
01290                                                     gammalib::strip_chars(line,"\n"),
01291                                                                  e.what());
01292         }
01293 
01294         // If parameter name is mode then store the effective mode
01295         if (fields[0] == "mode") {
01296             if (fields[3] != "h"  && fields[3] != "q" &&
01297                 fields[3] != "hl" && fields[3] != "ql" &&
01298                 fields[3] != "lh" && fields[3] != "lq") {
01299                 throw GException::par_file_syntax_error(G_PARSE, 
01300                                                     gammalib::strip_chars(line,"\n"),
01301                        "mode parameter has invalid value \""+fields[3]+"\"");
01302             }
01303             m_mode = fields[3];
01304         }
01305 
01306     } // endfor: looped over lines
01307 
01308     // Set effective mode for all parameters that have mode 'auto'
01309     for (int i = 0; i < m_pars.size(); ++i) {
01310         if (m_pars[i].mode() == "a") {
01311             m_pars[i].mode(m_mode);
01312         }
01313     }
01314 
01315     // Return
01316     return;
01317 }
01318 
01319 
01320 /***********************************************************************//**
01321  * @brief Update parameter file
01322  *
01323  * Update lines of parameter file according to the parameter values. This
01324  * method handles correctly formatted parameter files by replacing the value
01325  * at its original location within the line (preserving additional
01326  * whitespace).
01327  * Updating is only done of the parameter mode is 'learn'.
01328  ***************************************************************************/
01329 void GApplicationPars::update(void)
01330 {
01331     // Loop over all parameters
01332     for (int i = 0; i < m_pars.size(); ++i) {
01333 
01334         // Update only if requested and allowed
01335         if (m_pars[i].m_update && m_pars[i].is_learn()) {
01336             m_parfile[m_line[i]] = m_parfile[m_line[i]].substr(0, m_vstart[i]) +
01337                                    m_pars[i].m_value +
01338                                    m_parfile[m_line[i]].substr(m_vstop[i]);
01339             m_vstop[i] = m_vstart[i] + m_pars[i].m_value.length();
01340         }
01341 
01342     } // endfor: looped over all parameters
01343 
01344     // Return
01345     return;
01346 }
01347 
01348 
01349 /***********************************************************************//**
01350  * @brief Return parameter index by name
01351  *
01352  * @param[in] name Parameter name.
01353  * @return Parameter index (-1 if parameter name has not been found)
01354  *
01355  * Returns parameter index based on the specified @p name. If no parameter
01356  * with the specified @p name is found the method returns -1.
01357  ***************************************************************************/
01358 int GApplicationPars::get_index(const std::string& name) const
01359 {
01360     // Initialise index
01361     int index = -1;
01362 
01363     // Search parameter with specified name
01364     for (int i = 0; i < size(); ++i) {
01365         if (m_pars[i].name() == name) {
01366             index = i;
01367             break;
01368         }
01369     }
01370 
01371     // Return index
01372     return index;
01373 }
01374 
01375 
01376 /***********************************************************************//**
01377  * @brief Return parameter file line for a specific parameter
01378  *
01379  * @param[in] par Parameter.
01380  * @param[out] start Column of value start.
01381  * @param[out] stop Column of value stop.
01382  * @return Parameter file line (termined by \n).
01383  *
01384  * Constructs the parameter file line for a specific parameter and returns
01385  * the line as a string. The line is terminated by a \n character.
01386  ***************************************************************************/
01387 std::string GApplicationPars::parline(GApplicationPar& par, size_t* start, size_t* stop) const
01388 {
01389     // Declate line
01390     std::string line;
01391 
01392     // Build parameter file line
01393     line.append(par.name()+", ");
01394     line.append(par.type()+ ", ");
01395     line.append(par.mode()+ ",");
01396     *start = line.length();
01397     line.append(par.value()+ ",");
01398     *stop = line.length();
01399     line.append(par.min()+ ",");
01400     line.append(par.max()+ ",");
01401     line.append("\""+par.prompt()+"\"\n");
01402 
01403     // Return line
01404     return line;
01405 }

Generated on Tue Jan 24 12:37:21 2017 for GammaLib by  doxygen 1.4.7