/*************************************************************************** * GApplicationPars.cpp - Application parameters * * ----------------------------------------------------------------------- * * copyright (C) 2010-2014 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file GApplicationPars.cpp * @brief Application parameter container class implementation * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include // user/passwd function #include // for file locking #include // access() function #include // mkdir() function #include // std::getenv() function #include // std::fopen(), etc. functions #include "GApplicationPars.hpp" #include "GTools.hpp" #include "GException.hpp" /* __ Method name definitions ____________________________________________ */ #define G_ACCESS "GApplicationPars::operator[](std::string&)" #define G_AT "GApplicationPar& GApplicationPars::at(int&)" #define G_APPEND "GApplicationPars::append(GApplicationPar&)" #define G_INSERT1 "GApplicationPar& GApplicationPars::insert(int&, "\ "GApplicationPar&)" #define G_INSERT2 "GApplicationPar& GApplicationPars::insert(std::string&, "\ "GApplicationPar&)" #define G_REMOVE1 "GApplicationPars::remove(int&)" #define G_REMOVE2 "GApplicationPars::remove(std::string&)" #define G_EXTEND "GApplicationPars::extend(GApplicationPars&)" #define G_LOAD1 "GApplicationPars::load(std::string&)" #define G_LOAD2 "GApplicationPars::load(std::string&, "\ "std::vector&)" #define G_SAVE "GApplicationPars::save(std::string&)" #define G_OUTPATH "GApplicationPars::outpath(std::string&)" #define G_READ "GApplicationPars::read(std::string&)" #define G_WRITE "GApplicationPars::write(std::string&)" #define G_PARSE "GApplicationPars::parse()" /* __ Macros _____________________________________________________________ */ /* __ Coding definitions _________________________________________________ */ #define G_LOCK_PARFILE //!< Enables parfile locking //#define G_CHECK_LOCK_PARFILE //!< Enables check of parfile locking /* __ Debug definitions __________________________________________________ */ /*========================================================================== = = = Constructors/destructors = = = ==========================================================================*/ /***********************************************************************//** * @brief Void constructor ***************************************************************************/ GApplicationPars::GApplicationPars(void) { // Initialise private members for clean destruction init_members(); // Return return; } /***********************************************************************//** * @brief Parameter file constructor * * @param[in] filename Parameter filename. ***************************************************************************/ GApplicationPars::GApplicationPars(const std::string& filename) { // Initialise private members for clean destruction init_members(); // Load parameters load(filename); // Return return; } /***********************************************************************//** * @brief Parameter constructor * * @param[in] filename Parameter filename. * @param[in] args Command line arguments. ***************************************************************************/ GApplicationPars::GApplicationPars(const std::string& filename, const std::vector& args) { // Initialise private members for clean destruction init_members(); // Load parameters load(filename, args); // Return return; } /***********************************************************************//** * @brief Copy constructor * * @param[in] pars Parameter container. ***************************************************************************/ GApplicationPars::GApplicationPars(const GApplicationPars& pars) { // Initialise private members for clean destruction init_members(); // Copy members copy_members(pars); // Return return; } /***********************************************************************//** * @brief Destructor ***************************************************************************/ GApplicationPars::~GApplicationPars(void) { // Free members free_members(); // Return return; } /*========================================================================== = = = Operators = = = ==========================================================================*/ /***********************************************************************//** * @brief Assignment operator * * @param[in] pars Parameter container. * @return Parameter container. ***************************************************************************/ GApplicationPars& GApplicationPars::operator=(const GApplicationPars& pars) { // Execute only if object is not identical if (this != &pars) { // Free members free_members(); // Initialise private members for clean destruction init_members(); // Copy members copy_members(pars); } // endif: object was not identical // Return return *this; } /***********************************************************************//** * @brief Returns reference to parameter * * @param[in] name Parameter name. * @return Parameter. * * @exception GException::invalid_argument * Parameter with specified name not found in container. ***************************************************************************/ GApplicationPar& GApplicationPars::operator[](const std::string& name) { // Get parameter index int index = get_index(name); // Throw exception if parameter name has not been found if (index == -1) { std::string msg = "Parameter \""+name+"\" has not been found in" " parameter file.\n" "Please specify a valid parameter name."; throw GException::invalid_argument(G_ACCESS, msg); } // Return reference return (m_pars[index]); } /***********************************************************************//** * @brief Returns reference to parameter (const version) * * @param[in] name Parameter name. * @return Parameter. * * @exception GException::invalid_argument * Parameter with specified name not found in container. ***************************************************************************/ const GApplicationPar& GApplicationPars::operator[](const std::string& name) const { // Get parameter index int index = get_index(name); // Throw exception if parameter name has not been found if (index == -1) { std::string msg = "Parameter \""+name+"\" has not been found in" " parameter file.\n" "Please specify a valid parameter name."; throw GException::invalid_argument(G_ACCESS, msg); } // Return reference return (m_pars[index]); } /*========================================================================== = = = Public methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Clear parameter container ***************************************************************************/ void GApplicationPars::clear(void) { // Free members free_members(); // Init members init_members(); // Return return; } /***********************************************************************//** * @brief Clone parameter container * * @return Pointer to deep copy of parameter container. ***************************************************************************/ GApplicationPars* GApplicationPars::clone(void) const { return (new GApplicationPars(*this)); } /***********************************************************************//** * @brief Returns reference to parameter * * @param[in] index Parameter index [0,...,size()-1]. * * @exception GException::out_of_range * Parameter index is out of range. ***************************************************************************/ GApplicationPar& GApplicationPars::at(const int& index) { // Compile option: raise an exception if index is out of range #if defined(G_RANGE_CHECK) if (index < 0 || index >= size()) { throw GException::out_of_range(G_AT, "Parameter index", index, size()); } #endif // Return reference return (m_pars[index]); } /***********************************************************************//** * @brief Returns reference to parameter (const version) * * @param[in] index Parameter index [0,...,size()-1]. * * @exception GException::out_of_range * Parameter index is out of range. ***************************************************************************/ const GApplicationPar& GApplicationPars::at(const int& index) const { // Compile option: raise an exception if index is out of range #if defined(G_RANGE_CHECK) if (index < 0 || index >= size()) { throw GException::out_of_range(G_AT, "Parameter index", index, size()); } #endif // Return reference return (m_pars[index]); } /***********************************************************************//** * @brief Append parameter to container * * @param[in] par Parameter. * @return Reference to deep copy of appended parameter. * * @exception GException::invalid_value * Parameter with same name exists already in container. * * This method appends one parameter to the parameter container. The * parameter provided to the method can be deleted after calling this method. ***************************************************************************/ GApplicationPar& GApplicationPars::append(const GApplicationPar& par) { // Check if a parameter with specified name does not yet exist int inx = get_index(par.name()); if (inx != -1) { std::string msg = "Attempt to append parameter with name \""+par.name()+"\" in" " parameter container, but a parameter with the same name exists" " already at index "+gammalib::str(inx)+" in the container.\n" "Every parameter in the parameter container needs a unique name."; throw GException::invalid_value(G_APPEND, msg); } // Append parameter m_pars.push_back(par); // Get reference of appended parameter GApplicationPar& parameter = m_pars[m_pars.size()-1]; // Build parameter file line size_t start = 0; size_t stop = 0; std::string line = parline(parameter, &start, &stop); // Append parameter file line and attributes m_line.push_back(m_parfile.size()); m_parfile.push_back(line); m_vstart.push_back(start); m_vstop.push_back(stop); // Return parameter reference return parameter; } /***********************************************************************//** * @brief Append standard parameters to container * * This method appends the standard parameters to the parameter container. * Standard parameters are: "chatter", "clobber", "debug" and "mode". ***************************************************************************/ void GApplicationPars::append_standard(void) { // Append standard parameters append(GApplicationPar("chatter","i","h","2","0","4","Chattiness of output")); append(GApplicationPar("clobber","b","h","yes","","","Overwrite existing output files with new output files?")); append(GApplicationPar("debug","b","h","no","","","Debugging mode activated")); append(GApplicationPar("mode","s","h","ql","","","Mode of automatic parameters")); // Return return; } /***********************************************************************//** * @brief Insert parameter into container * * @param[in] index Parameter index [0,...,size()-1]. * @param[in] par Parameter. * @return Reference to deep copy of inserted parameter. * * Inserts a parameter into the container before the parameter with the * specified @p index. ***************************************************************************/ GApplicationPar& GApplicationPars::insert(const int& index, const GApplicationPar& par) { // Compile option: raise exception if index is out of range #if defined(G_RANGE_CHECK) if (is_empty()) { if (index > 0) { throw GException::out_of_range(G_INSERT1, "Parameter index", index, size()); } } else { if (index < 0 || index >= size()) { throw GException::out_of_range(G_INSERT1, "Parameter index", index, size()); } } #endif // Check if a parameter with specified name does not yet exist int inx = get_index(par.name()); if (inx != -1) { std::string msg = "Attempt to insert parameter with name \""+par.name()+"\" in" " parameter container before index "+gammalib::str(index)+ ", but a parameter with the same name exists already at index "+ gammalib::str(inx)+" in the container.\n" "Every parameter in the parameter container needs a unique name."; throw GException::invalid_value(G_INSERT1, msg); } // Inserts parameter m_pars.insert(m_pars.begin()+index, par); // Get reference of appended parameter GApplicationPar& parameter = m_pars[m_pars.size()-1]; // Build parameter file line size_t start = 0; size_t stop = 0; std::string line = parline(parameter, &start, &stop); // Determine at which line number of the parameter file the parameter // should be inserted int line_number = m_line[index]; // Insert parameter file line and parameter attributes m_parfile.insert(m_parfile.begin()+line_number, line); m_line.insert(m_line.begin()+index, line_number); m_vstart.insert(m_vstart.begin()+index, start); m_vstop.insert(m_vstop.begin()+index, stop); // Increment the line numbers for all parameters after the inserted one for (int i = index+1; i < size(); ++i) { m_line[i]++; } // Return parameter reference return parameter; } /***********************************************************************//** * @brief Insert parameter into container * * @param[in] name Parameter name. * @param[in] par Parameter. * @return Reference to deep copy of inserted parameter. * * @exception GException::invalid_argument * Parameter with specified name not found in container. * @exception GException::invalid_value * Name of parameter exists already in container. * * Inserts a parameter into the container before the parameter with the * specified @p name. ***************************************************************************/ GApplicationPar& GApplicationPars::insert(const std::string& name, const GApplicationPar& par) { // Get parameter index int index = get_index(name); // Throw exception if parameter name was not found if (index == -1) { std::string msg = "Parameter \""+name+"\" has not been found in" " parameter file.\n" "Please specify a valid parameter name."; throw GException::invalid_argument(G_INSERT2, msg); } // Insert by index and return parameter reference return (insert(index, par)); } /***********************************************************************//** * @brief Remove parameter from container * * @param[in] index Parameter index [0,...,size()-1]. * * @exception GException::out_of_range * Parameter index is out of range. * * Remove parameter with specified @p index from container. ***************************************************************************/ void GApplicationPars::remove(const int& index) { // Compile option: raise exception if index is out of range #if defined(G_RANGE_CHECK) if (index < 0 || index >= size()) { throw GException::out_of_range(G_REMOVE1, "Parameter index", index, size()); } #endif // Erase parameter from container m_pars.erase(m_pars.begin() + index); // Remove parameter file line and parameter attributes m_parfile.erase(m_parfile.begin() + m_line[index]); m_line.erase(m_line.begin() + index); m_vstart.erase(m_vstart.begin() + index); m_vstop.erase(m_vstop.begin() + index); // Decrement the line numbers for all parameters after the removed one for (int i = index; i < size(); ++i) { m_line[i]--; } // Return return; } /***********************************************************************//** * @brief Remove parameter from container * * @param[in] name Parameter name. * * @exception GException::invalid_argument * Parameter with specified name not found in container. * * Remove parameter with specified @p name from container. ***************************************************************************/ void GApplicationPars::remove(const std::string& name) { // Get parameter index int index = get_index(name); // Throw exception if parameter name was not found if (index == -1) { std::string msg = "Parameter \""+name+"\" has not been found in" " parameter file.\n" "Please specify a valid parameter name."; throw GException::invalid_argument(G_REMOVE2, msg); } // Remove by index remove(index); // Return return; } /***********************************************************************//** * @brief Append parameter container * * @param[in] pars Parameter container. * * Append parameter container to the container. ***************************************************************************/ void GApplicationPars::extend(const GApplicationPars& pars) { // Do nothing if parameter container is empty if (!pars.is_empty()) { // Get size. Note that we extract the size first to avoid an // endless loop that arises when a container is appended to // itself. int num = pars.size(); // Reserve enough space reserve(size() + num); // Loop over all parameters and append copies for (int i = 0; i < num; ++i) { // Check if parameter name does not yet exist int inx = get_index(pars[i].name()); if (inx != -1) { std::string msg = "Attempt to append parameter with name \""+pars[i].name()+ "\" to parameter container, but a parameter with the same name" " exists already at index "+gammalib::str(inx)+" in the" " container.\n" "Every parameter in the parameter container needs a unique" " name."; throw GException::invalid_value(G_EXTEND, msg); } // Append parameter to container append(pars[i]); } // endfor: looped over all parameters } // endif: parameter container was not empty // Return return; } /***********************************************************************//** * @brief Check parameter exists * * @param[in] name Parameter name. * @return True if parameter with specified name exists, false otherwise. * * Determines whether a parameter with the specified name exists already in * the parameter container. ***************************************************************************/ bool GApplicationPars::contains(const std::string& name) const { // Get parameter index int inx = get_index(name); // Return test result return (inx != -1); } /***********************************************************************//** * @brief Load parameters * * @param[in] filename Parameter filename. * * @exception GException::par_file_not_found * Parameter file not found. * * Loads all parameters from parameter file. ***************************************************************************/ void GApplicationPars::load(const std::string& filename) { // Reset parameters m_parfile.clear(); // Get path to parameter file for input std::string path = inpath(filename); if (path.length() == 0) { throw GException::par_file_not_found(G_LOAD1, filename); } // Read parfile read(path); // Parse parfile parse(); // Return return; } /***********************************************************************//** * @brief Load parameters * * @param[in] filename Parameter filename. * @param[in] args Command line arguments. * * @exception GException::par_file_not_found * Parameter file not found. * @exception GException::bad_cmdline_argument * Invalid command line argument encountered. * * Loads all parameters from parameter file. Parameters are overwritten by * the values specified in the command line arguments. ***************************************************************************/ void GApplicationPars::load(const std::string& filename, const std::vector& args) { // Reset parameters m_parfile.clear(); // Get path to parameter file for input std::string path = inpath(filename); if (path.length() == 0) { throw GException::par_file_not_found(G_LOAD2, filename); } // Read parfile read(path); // Parse parfile parse(); // Overwrite parameter values that are specified in the command line for (int i = 1; i < args.size(); ++i) { // Extract parameter name and value (empty values are permitted) size_t pos = args[i].find("="); if (pos == std::string::npos) { throw GException::bad_cmdline_argument(G_LOAD2, args[i], "no \"=\" specified"); } std::string name = args[i].substr(0, pos); std::string value = args[i].substr(pos+1); if (name.length() < 1) { throw GException::bad_cmdline_argument(G_LOAD2, args[i], "no parameter name before \"=\""); } // Check if parameter exists if (!contains(name)) { throw GException::bad_cmdline_argument(G_LOAD2, args[i], "invalid parameter name \""+name+"\""); } // Assign value try { (*this)[name].value(value); } catch (GException::par_error &e) { throw GException::bad_cmdline_argument(G_LOAD2, args[i]); } // Set mode to hidden to prevent querying the parameter if ((*this)[name].mode() == "q") { (*this)[name].mode("h"); } else if ((*this)[name].mode() == "ql") { (*this)[name].mode("hl"); } else if ((*this)[name].mode() == "lq") { (*this)[name].mode("lh"); } } // endfor: looped over all parameters // Return return; } /***********************************************************************//** * @brief Save parameters * * @param[in] filename Parameter filename. * * @exception GException::par_file_not_found * No valid directory to write the parameter file has been found. ***************************************************************************/ void GApplicationPars::save(const std::string& filename) { // Get path to parameter file for output std::string path = outpath(filename); if (path.size() == 0) { throw GException::par_file_not_found(G_SAVE, filename); } // Update parameter file update(); // Write parfile write(path); // Return return; } /***********************************************************************//** * @brief Print parameters * * @param[in] chatter Chattiness (defaults to NORMAL). * @return String containing parameter information. ***************************************************************************/ std::string GApplicationPars::print(const GChatter& chatter) const { // Initialise result string std::string result; // Continue only if chatter is not silent if (chatter != SILENT) { // Append header result.append("=== GApplicationPars ==="); // Append parameters for (int i = 0; i < size(); ++i) { result.append("\n"+m_pars[i].print(chatter)); } } // endif: chatter was not silent // Return result return result; } /*========================================================================== = = = Private methods = = = ==========================================================================*/ /***********************************************************************//** * @brief Initialise class members ***************************************************************************/ void GApplicationPars::init_members(void) { // Initialise members m_parfile.clear(); m_pars.clear(); m_line.clear(); m_vstart.clear(); m_vstop.clear(); m_mode = "h"; // Return return; } /***********************************************************************//** * @brief Copy class members * * @param[in] pars Object from which members which should be copied. ***************************************************************************/ void GApplicationPars::copy_members(const GApplicationPars& pars) { // Copy attributes m_parfile = pars.m_parfile; m_pars = pars.m_pars; m_line = pars.m_line; m_vstart = pars.m_vstart; m_vstop = pars.m_vstop; m_mode = pars.m_mode; // Return return; } /***********************************************************************//** * @brief Delete class members ***************************************************************************/ void GApplicationPars::free_members(void) { // Return return; } /***********************************************************************//** * @brief Determine filepath for parameter file input * * @param[in] filename Parameter filename to search for. * * Locates parameter file for input. * The parameter file is first searched in the directories that are listed * in the PFILES environment variable. Directories may be separated by : or * by ; in PFILES. * If the PFILES environment variable is not set or no parameter file was * found, the parameter file is searched (in the given order) in the users * pfiles directory, in ${GAMMALIB}/syspfiles and in ${prefix}/syspfiles, * where ${prefix} is the path to the GammaLib installation. ***************************************************************************/ std::string GApplicationPars::inpath(const std::string& filename) const { // Allocate result path std::string path; // Search for parameter file in PFILES directories char* ptr = std::getenv("PFILES"); if (ptr != NULL) { // Extract directories from PFILES environment variable std::string pfiles = ptr; std::vector dirs = gammalib::split(pfiles, ":;"); // Search for first occurence of parameter file for (int i = 0; i < dirs.size(); ++i) { // Build filename std::string fname = dirs[i] + "/" + filename; // If file is accessible for reading then exit loop if (access(fname.c_str(), R_OK) == 0) { path = fname; break; } } // endfor: searched all directories given in PFILES } // endif: PFILES directory has been found // If we have no valid path so far then search in users pfiles // directory if (path.size() == 0) { uid_t uid = geteuid(); struct passwd* pw = getpwuid(uid); if (pw != NULL) { std::string fname = std::string(pw->pw_dir) + "/pfiles/" + filename; if (access(fname.c_str(), R_OK) == 0) { path = fname; } } } // If we have no valid path so far then search file within GAMMALIB // repository (${GAMMALIB}/syspfiles) if (path.size() == 0) { ptr = std::getenv("GAMMALIB"); if (ptr != NULL) { std::string fname = std::string(ptr) + "/syspfiles/" + filename; if (access(fname.c_str(), R_OK) == 0) { path = fname; } } } // If we have no valid path so far then search file within GammaLib // package (${prefix}/syspfiles) #ifdef PACKAGE_PREFIX if (path.size() == 0) { std::string fname = std::string(PACKAGE_PREFIX) + "/syspfiles/" + filename; if (access(fname.c_str(), R_OK) == 0) { path = fname; } } #endif //printf("parfile=%s\n", path.c_str()); // Return path return path; } /***********************************************************************//** * @brief Determine filepath for parameter file output * * @param[in] filename Parameter filename. * * @exception GException::home_not_found * Unable to determine users home directory. * @exception GException::could_not_create_pfiles * Unable to create pfiles directory. * @exception GException::pfiles_not_accessible * Unable to make pfiles directory accessible to user. * * Searchs for first writable directory listed in PFILES environment * variable. If PFILES is not set then use pfiles directory in users * home directory. If pfiles directory does not exist then create it. * If directory exists but is not writable then make it writable. ***************************************************************************/ std::string GApplicationPars::outpath(const std::string& filename) const { // Allocate result path std::string path; // Search for writeable PFILES directories char* ptr = std::getenv("PFILES"); if (ptr != NULL) { // Extract directories from PFILES environment variable std::string pfiles = ptr; std::vector dirs = gammalib::split(pfiles, ":;"); // Search for first writeable for (int i = 0; i < dirs.size(); ++i) { // If directory is accessible for writing then exit loop if (access(dirs[i].c_str(), W_OK) == 0) { path = dirs[i] + "/" + filename; break; } } } // endif: PFILES environment variable exists // If no valid directory is found in PFILES environment variable then // use pfiles directory in users home directory. if (path.size() == 0) { // Get users home directory uid_t uid = geteuid(); gid_t gid = getegid(); struct passwd* pw = getpwuid(uid); if (pw == NULL) { throw GException::home_not_found(G_OUTPATH); } // Set path path = std::string(pw->pw_dir) + "/pfiles"; // If directory does not exist then create it if (access(path.c_str(), F_OK) != 0) { if (mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) { throw GException::could_not_create_pfiles(G_OUTPATH, path); } } // If directory exists but is not writable then make it writable else if (access(path.c_str(), W_OK) != 0) { if (chown(path.c_str(), uid, gid) != 0 || chmod(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) { throw GException::pfiles_not_accessible(G_OUTPATH, path); } } // Append filename path = path + "/" + filename; } // endif: no valid directory found in PFILES // Return path return path; } /***********************************************************************//** * @brief Read parameter file * * @param[in] filename Parameter filename (absolut path). * * @exception GException::par_file_open_error * Unable to open parameter file (read access requested). * * Read all lines of the parameter file. Each line is terminated by a newline * character. The file will be locked to avoid simultaneous access by another * process. ***************************************************************************/ void GApplicationPars::read(const std::string& filename) { // Allocate line buffer const int n = 1000; char line[n]; // Trying to get file lock #if defined(G_LOCK_PARFILE) struct flock lock; lock.l_type = F_RDLCK; // Want a read lock lock.l_whence = SEEK_SET; // Want beginning of file lock.l_start = 0; // No offset, lock entire file ... lock.l_len = 0; // ... to the end lock.l_pid = getpid(); // Current process ID int fd; if ((fd = open(filename.c_str(), O_RDONLY)) == -1) { throw GException::par_file_open_error(G_READ, filename, "Could not open file for locking."); } #if defined(G_CHECK_LOCK_PARFILE) if (fcntl(fd, F_SETLKW, &lock) == -1) { // F_SETLKW: wait until unlocked throw GException::par_file_open_error(G_READ, filename, "Could not get a lock on the file."); } #else fcntl(fd, F_SETLKW, &lock); #endif #endif // Open parameter file FILE* fptr = fopen(filename.c_str(), "r"); if (fptr == NULL) { throw GException::par_file_open_error(G_READ, filename); } // Read lines while (fgets(line, n, fptr) != NULL) { m_parfile.push_back(std::string(line)); } // Close file fclose(fptr); // Unlock file #if defined(G_LOCK_PARFILE) if (fd != -1) { lock.l_type = F_UNLCK; #if defined(G_CHECK_LOCK_PARFILE) if (fcntl(fd, F_SETLK, &lock) == -1) { throw GException::par_file_open_error(G_READ, filename, "Could not unlock the file."); } #else fcntl(fd, F_SETLK, &lock); #endif close(fd); } #endif // Return return; } /***********************************************************************//** * @brief Write parameter file * * @param[in] filename Parameter filename (absolut path). * * @exception GException::par_file_open_error * Unable to open parameter file (write access requested). * * Writes all lines of the parameter file. The file will be locked to avoid * simultaneous access by another process. ***************************************************************************/ void GApplicationPars::write(const std::string& filename) const { // Trying to get file lock. We have to do this after opening the file // using the fopen function, as the file may not exist, hence it needs // to be created first. #if defined(G_LOCK_PARFILE) struct flock lock; lock.l_type = F_WRLCK; // Want a write lock lock.l_whence = SEEK_SET; // Want beginning of file lock.l_start = 0; // No offset, lock entire file ... lock.l_len = 0; // ... to the end lock.l_pid = getpid(); // Current process ID int fd; if ((fd = open(filename.c_str(), O_WRONLY)) != -1) { #if defined(G_CHECK_LOCK_PARFILE) if (fcntl(fd, F_SETLKW, &lock) == -1) { // F_SETLKW: wait until unlocked throw GException::par_file_open_error(G_WRITE, filename, "Could not get a lock on the file."); } #else fcntl(fd, F_SETLKW, &lock); #endif } #endif // Open parameter file. FILE* fptr = fopen(filename.c_str(), "w"); if (fptr == NULL) { throw GException::par_file_open_error(G_WRITE, filename); } // If file is not locked then lock it now. #if defined(G_LOCK_PARFILE) if (fd == -1) { if ((fd = open(filename.c_str(), O_WRONLY)) != -1) { #if defined(G_CHECK_LOCK_PARFILE) if (fcntl(fd, F_SETLKW, &lock) == -1) { // F_SETLKW: wait until unlocked fclose(fptr); throw GException::par_file_open_error(G_WRITE, filename, "Could not get a lock on the file."); } #else fcntl(fd, F_SETLKW, &lock); #endif } } #endif // Write lines for (int i = 0; i < m_parfile.size(); ++i) { fprintf(fptr, "%s", m_parfile[i].c_str()); } // Close file fclose(fptr); // Unlock file #if defined(G_LOCK_PARFILE) if (fd != -1) { lock.l_type = F_UNLCK; #if defined(G_CHECK_LOCK_PARFILE) if (fcntl(fd, F_SETLK, &lock) == -1) { throw GException::par_file_open_error(G_WRITE, filename, "Could not unlock the file."); } #else fcntl(fd, F_SETLK, &lock); #endif close(fd); } #endif // Return return; } /***********************************************************************//** * @brief Parse parameter file * * @exception GException::par_file_syntax_error * Syntax error encountered in parameter file. * * The parameter type has to be one b,i,r,s,f,fr,fw,fe,fn. The fr,fw,fe,fn * types test for read access, write access, file existence, and file * absence, respectively. * The parameter mode has to be one of a,h,l,q,hl,ql,lh,lq. For mode 'a' the * effective mode equals to the value given by the mode parameter, if it * exists. Without the presence of the mode parameter the effective mode * will be 'h'. ***************************************************************************/ void GApplicationPars::parse(void) { // Preset effective mode to 'hidden' m_mode = "h"; // Parse all lines for (int i = 0; i < m_parfile.size(); ++i) { // Get line without any leading and trailing whitespace std::string line = gammalib::strip_whitespace(m_parfile[i]); // If line contains a single linefeed then skip the line if (line.length() == 1 && line[0] == '\n') continue; // If line is empty or if line starts with # then skip the line if (line.length() == 0 || line[0] == '#') { continue; } // Get the 7 text fields of valid a parameter line std::string fields[7]; int quotes = 0; size_t start = 0; size_t end = line.length() - 1; int index = 0; size_t vstart = 0; size_t vstop = 0; for (size_t pos = 0; pos < line.length(); ++pos) { // Toggle quotes if (line[pos] == '"') { quotes = 1-quotes; } // Search for comma only if we are outside quotes. If comma is // found or end of line is reached then extract a field and start // searching again from the position following the comma. Strip // leading and trailing quotes from the fields. if (quotes == 0) { if (line[pos] == ',' || pos == end) { if (index < 7) { fields[index] = gammalib::strip_chars(gammalib::strip_whitespace(line.substr(start, pos-start)), "\""); if (index == 3) { vstart = start; vstop = pos; } start = pos + 1; } index++; } } } // endfor: looped over line // Throw an error if quotes are not balanced if (quotes != 0) { throw GException::par_file_syntax_error(G_PARSE, gammalib::strip_chars(line,"\n"), "quotes are not balanced"); } // Throw an error if line has not 7 fields if (index != 7) { throw GException::par_file_syntax_error(G_PARSE, gammalib::strip_chars(line,"\n"), "found "+gammalib::str(index)+" fields, require 7"); } // Verify if parameter name does not yet exist if (contains(fields[0])) { throw GException::par_file_syntax_error(G_PARSE, gammalib::strip_chars(line,"\n"), "redefinition of parameter name \""+fields[0]+"\""); } // Add parameter try { m_pars.push_back(GApplicationPar(fields[0], fields[1], fields[2], fields[3], fields[4], fields[5], fields[6])); m_line.push_back(i); m_vstart.push_back(vstart); m_vstop.push_back(vstop); } catch (GException::par_error &e) { throw GException::par_file_syntax_error(G_PARSE, gammalib::strip_chars(line,"\n"), e.what()); } // If parameter name is mode then store the effective mode if (fields[0] == "mode") { if (fields[3] != "h" && fields[3] != "q" && fields[3] != "hl" && fields[3] != "ql" && fields[3] != "lh" && fields[3] != "lq") { throw GException::par_file_syntax_error(G_PARSE, gammalib::strip_chars(line,"\n"), "mode parameter has invalid value \""+fields[3]+"\""); } m_mode = fields[3]; } } // endfor: looped over lines // Set effective mode for all parameters that have mode 'auto' for (int i = 0; i < m_pars.size(); ++i) { if (m_pars[i].mode() == "a") { m_pars[i].mode(m_mode); } } // Return return; } /***********************************************************************//** * @brief Update parameter file * * Update lines of parameter file according to the parameter values. This * method handles correctly formatted parameter files by replacing the value * at its original location within the line (preserving additional * whitespace). * Updating is only done of the parameter mode is 'learn'. ***************************************************************************/ void GApplicationPars::update(void) { // Loop over all parameters for (int i = 0; i < m_pars.size(); ++i) { // Update only if requested and allowed if (m_pars[i].m_update && m_pars[i].is_learn()) { m_parfile[m_line[i]] = m_parfile[m_line[i]].substr(0, m_vstart[i]) + m_pars[i].m_value + m_parfile[m_line[i]].substr(m_vstop[i]); m_vstop[i] = m_vstart[i] + m_pars[i].m_value.length(); } } // endfor: looped over all parameters // Return return; } /***********************************************************************//** * @brief Return parameter index by name * * @param[in] name Parameter name. * @return Parameter index (-1 if parameter name has not been found) * * Returns parameter index based on the specified @p name. If no parameter * with the specified @p name is found the method returns -1. ***************************************************************************/ int GApplicationPars::get_index(const std::string& name) const { // Initialise index int index = -1; // Search parameter with specified name for (int i = 0; i < size(); ++i) { if (m_pars[i].name() == name) { index = i; break; } } // Return index return index; } /***********************************************************************//** * @brief Return parameter file line for a specific parameter * * @param[in] par Parameter. * @param[out] start Column of value start. * @param[out] stop Column of value stop. * @return Parameter file line (termined by \n). * * Constructs the parameter file line for a specific parameter and returns * the line as a string. The line is terminated by a \n character. ***************************************************************************/ std::string GApplicationPars::parline(GApplicationPar& par, size_t* start, size_t* stop) const { // Declate line std::string line; // Build parameter file line line.append(par.name()+", "); line.append(par.type()+ ", "); line.append(par.mode()+ ","); *start = line.length(); line.append(par.value()+ ","); *stop = line.length(); line.append(par.min()+ ","); line.append(par.max()+ ","); line.append("\""+par.prompt()+"\"\n"); // Return line return line; }