Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

OptionAgent.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004 Ivo Danihelka (ivo@danihelka.net)
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008  */
00009 #include "OptionAgent.h"
00010 
00011 #include "Environ.h"
00012 
00013 #include "Log.h"
00014 #include "Path.h"
00015 #include "FsPath.h"
00016 #include "ScriptAgent.h"
00017 #include "StringTool.h"
00018 #include "HelpException.h"
00019 #include "LogicException.h"
00020 #include "ScriptException.h"
00021 #include "OptionParams.h"
00022 #include "StringMsg.h"
00023 #include "UnknownMsgException.h"
00024 #include "minmax.h"
00025 
00026 #include <string.h> //strlen
00027 #include <locale.h> //setlocale
00028 
00029 #ifndef LC_MESSAGES
00030 #define LC_MESSAGES LC_CTYPE
00031 #endif
00032 
00033 //NOTE: SYSTEM_DATA_DIR is set to "$(datadir)/games/@PACKAGE@"
00034 #ifndef SYSTEM_DATA_DIR
00035 #define SYSTEM_DATA_DIR ""
00036 #endif
00037 
00038 //NOTE: userdir = $HOME + USER_DATA_DIR
00039 #ifndef USER_DATA_DIR
00040 #define USER_DATA_DIR ".fillets-ng"
00041 #endif
00042 
00043 const char *OptionAgent::CONFIG_FILE = "script/options.lua";
00044 //-----------------------------------------------------------------
00045 /**
00046  * Set user and sytem dir
00047  * and process "script/options.lua" - this will set user and system paths
00048  * and process "script/init.lua".
00049  */
00050     void
00051 OptionAgent::own_init()
00052 {
00053     m_environ = new Environ();
00054     prepareVersion();
00055     prepareDataPaths();
00056     prepareLang();
00057 }
00058 //-----------------------------------------------------------------
00059 /**
00060  * Save user config.
00061  * Delete left messages.
00062  */
00063     void
00064 OptionAgent::own_shutdown()
00065 {
00066     delete m_environ;
00067 }
00068 //-----------------------------------------------------------------
00069 /**
00070  * Set program version.
00071  */
00072     void
00073 OptionAgent::prepareVersion()
00074 {
00075 #ifdef VERSION
00076     setParam("version", VERSION);
00077 #else
00078     setParam("version", "0.0.1");
00079 #endif
00080 #ifdef PACKAGE
00081     setParam("package", PACKAGE);
00082 #else
00083     setParam("package", "A game");
00084 #endif
00085 }
00086 //-----------------------------------------------------------------
00087 /**
00088  * Set user and sytem dir options.
00089  * Userdir="$HOME/.fillets-ng" or ""
00090  */
00091     void
00092 OptionAgent::prepareDataPaths()
00093 {
00094     registerWatcher("systemdir");
00095     registerWatcher("userdir");
00096     OptionAgent::agent()->setParam("systemdir", SYSTEM_DATA_DIR);
00097 
00098     std::string userdir = "";
00099     const char *home = getenv("HOME");
00100     if (home) {
00101         userdir = FsPath::join(home, USER_DATA_DIR);
00102         OptionAgent::agent()->setParam("userdir", userdir);
00103     }
00104     else {
00105         readUserConfig();
00106     }
00107 }
00108 //-----------------------------------------------------------------
00109 /**
00110  * Prepare user lang option.
00111  * For 2-letter lang codes
00112  * see http://www.w3.org/WAI/ER/IG/ert/iso639.htm
00113  */
00114     void
00115 OptionAgent::prepareLang()
00116 {
00117     setlocale(LC_ALL, "");
00118     //NOTE: '.' will be decimal point for Lua
00119     setlocale(LC_NUMERIC, "C");
00120     if (getParam("lang").empty()) {
00121         char *form = setlocale(LC_MESSAGES, NULL);
00122         int size = min(5, strlen(form));
00123         if (form && size >= 2) {
00124             setParam("lang", std::string(form, size));
00125         }
00126     }
00127 }
00128 //-----------------------------------------------------------------
00129 /**
00130  * Parse command line options.
00131  * Format: $0 [name=value ...]
00132  *
00133  * @throws LogicException when format is wrong
00134  */
00135     void
00136 OptionAgent::parseCmdOpt(int argc, char *argv[], const OptionParams &params)
00137 {
00138     if (argc >= 1) {
00139         setParam("program", argv[0]);
00140     }
00141 
00142     for (int i = 1; i < argc; ++i) {
00143         if (argv[i][0] == '-') {
00144             parseDashOpt(argv[i], params);
00145         }
00146         else {
00147             parseParamOpt(argv[i], params);
00148         }
00149     }
00150 }
00151 //-----------------------------------------------------------------
00152 /**
00153  * Supported options are '-h', '-v'.
00154  * @throws HelpException when only help is need
00155  * @throws LogicException when used option is unknown
00156  */
00157     void
00158 OptionAgent::parseDashOpt(const std::string &arg,
00159         const OptionParams &params)
00160 {
00161     if ("-h" == arg || "--help" == arg) {
00162         throw HelpException(ExInfo(getHelpInfo(params)));
00163     }
00164     else if ("-v" == arg || "--version" == arg) {
00165         throw HelpException(ExInfo(getVersionInfo()));
00166     }
00167     else if ("-c" == arg || "--config" == arg) {
00168         throw HelpException(ExInfo(params.getConfig(m_environ)));
00169     }
00170     else {
00171         throw LogicException(ExInfo("unknown option")
00172                 .addInfo("arg", arg)
00173                 .addInfo("use",
00174                     getParam("program") + " --help"));
00175     }
00176 }
00177 //-----------------------------------------------------------------
00178     void
00179 OptionAgent::parseParamOpt(const std::string &arg,
00180                 const OptionParams &/*params*/)
00181 {
00182     //TODO: check params type
00183     std::string name;
00184     std::string value;
00185     if (splitOpt(arg, &name, &value)) {
00186         setParam(name, value);
00187     }
00188     else {
00189         throw LogicException(ExInfo("unknown option")
00190                 .addInfo("arg", arg)
00191                 .addInfo("use",
00192                     getParam("program") + " --help"));
00193     }
00194 }
00195 //-----------------------------------------------------------------
00196 /**
00197  * Split "name=value".
00198  * @return true for success
00199  */
00200     bool
00201 OptionAgent::splitOpt(const std::string &option,
00202         std::string *out_name, std::string *out_value)
00203 {
00204     bool result = false;
00205     std::string::size_type pos = option.find('=');
00206     if (pos != std::string::npos) {
00207         if (pos + 1  < option.size()) {
00208             *out_name = option.substr(0, pos);
00209             *out_value = option.substr(pos + 1, std::string::npos);
00210             result = true;
00211         }
00212     }
00213 
00214     return result;
00215 }
00216 //-----------------------------------------------------------------
00217 /**
00218  * Set param.
00219  * Notice watchers.
00220  * When watcher is not available, it will be removed.
00221  *
00222  * @param name param name
00223  * @param value param value
00224  */
00225     void
00226 OptionAgent::setParam(const std::string &name, const std::string &value)
00227 {
00228     m_environ->setParam(name, value);
00229 }
00230 //-----------------------------------------------------------------
00231 /**
00232  * Store this integer value like string param.
00233  */
00234     void
00235 OptionAgent::setParam(const std::string &name, long value)
00236 {
00237     m_environ->setParam(name, value);
00238 }
00239 //-----------------------------------------------------------------
00240 /**
00241  * Return value.
00242  * Implicit value is "".
00243  *
00244  * @param name param name
00245  * @param implicit default value = ""
00246  * @return value or implicit value
00247  */
00248     std::string
00249 OptionAgent::getParam(const std::string &name,
00250         const std::string &implicit) const
00251 {
00252     return m_environ->getParam(name, implicit);
00253 }
00254 //-----------------------------------------------------------------
00255 /**
00256  * Returns number value.
00257  * Implicit value is zero.
00258  *
00259  * @param name param name
00260  * @param implicit default value = 0
00261  * @return number or implicit
00262  */
00263     int
00264 OptionAgent::getAsInt(const std::string &name,
00265         int implicit) const
00266 {
00267     return m_environ->getAsInt(name, implicit);
00268 }
00269 //-----------------------------------------------------------------
00270 /**
00271  * Returns boolean value.
00272  * Implicit value is false.
00273  *
00274  * @param name param name
00275  * @param implicit default value = false
00276  * @return stored boolean value or implicit value
00277  */
00278     bool
00279 OptionAgent::getAsBool(const std::string &name,
00280         bool implicit) const
00281 {
00282     return m_environ->getAsBool(name, implicit);
00283 }
00284 //-----------------------------------------------------------------
00285 /**
00286  * Set param also on disk.
00287  * Preserve all current params in options file.
00288  */
00289     void
00290 OptionAgent::setPersistent(const std::string &name, const std::string &value)
00291 {
00292     //NOTE: path must be created before change of environ
00293     Path config = Path::dataWritePath(CONFIG_FILE);
00294 
00295     Environ *swap_env = m_environ;
00296     m_environ = new Environ();
00297 
00298     try {
00299         if (config.exists()) {
00300             ScriptAgent::agent()->doFile(config);
00301         }
00302     }
00303     catch (ScriptException &e) {
00304         LOG_WARNING(e.info());
00305     }
00306     setParam(name, value);
00307     m_environ->store(config);
00308 
00309     delete m_environ;
00310     m_environ = swap_env;
00311     setParam(name, value);
00312 }
00313 //-----------------------------------------------------------------
00314     void
00315 OptionAgent::setPersistent(const std::string &name, long value)
00316 {
00317     setPersistent(name, StringTool::toString(value));
00318 }
00319 //-----------------------------------------------------------------
00320 /**
00321  * Set value when it is empty.
00322  */
00323 void
00324 OptionAgent::setDefault(const std::string &name, const std::string &value)
00325 {
00326     m_environ->setParam(name, m_environ->getParam(name, value));
00327 }
00328 //-----------------------------------------------------------------
00329 void
00330 OptionAgent::setDefault(const std::string &name, int value)
00331 {
00332     m_environ->setParam(name, m_environ->getAsInt(name, value));
00333 }
00334 //-----------------------------------------------------------------
00335     void
00336 OptionAgent::addWatcher(const std::string &name, BaseMsg *msg)
00337 {
00338     m_environ->addWatcher(name, msg);
00339 }
00340 //-----------------------------------------------------------------
00341 void
00342 OptionAgent::removeWatchers(const std::string &listenerName)
00343 {
00344     m_environ->removeWatchers(listenerName);
00345 }
00346 //-----------------------------------------------------------------
00347 /**
00348  * Get help text.
00349  */
00350 std::string
00351 OptionAgent::getHelpInfo(const OptionParams &params) const
00352 {
00353     std::string help = "Usage: "
00354         + getParam("program") + " [options] [name=value ...]\n";
00355     help += "  -h, --help               Show this help\n";
00356     help += "  -v, --version            Show version\n";
00357     help += "  -c, --config             Show config\n";
00358     help += "\n";
00359     help += "Config variables:\n";
00360     help += params.getHelp(m_environ);
00361     return help;
00362 }
00363 //-----------------------------------------------------------------
00364 std::string
00365 OptionAgent::getVersionInfo() const
00366 {
00367     return getParam("package") + " " + getParam("version");
00368 }
00369 //-----------------------------------------------------------------
00370 /**
00371  * Handle incoming message.
00372  * Messages:
00373  * - param_changed(systemdir) ... reread system options
00374  * - param_changed(userdir) ... reread user options
00375  *
00376  * @throws UnknownMsgException
00377  */
00378     void
00379 OptionAgent::receiveString(const StringMsg *msg)
00380 {
00381     if (msg->equalsName("param_changed")) {
00382         std::string param = msg->getValue();
00383         if ("systemdir" == param) {
00384             readSystemConfig();
00385         }
00386         else if ("userdir" == param) {
00387             readUserConfig();
00388         }
00389         else {
00390             throw UnknownMsgException(msg);
00391         }
00392     }
00393     else {
00394         throw UnknownMsgException(msg);
00395     }
00396 }
00397 //-----------------------------------------------------------------
00398 void
00399 OptionAgent::readSystemConfig()
00400 {
00401     try {
00402         Path systemConfig = Path::dataSystemPath(CONFIG_FILE);
00403         if (systemConfig.exists()) {
00404             ScriptAgent::agent()->doFile(systemConfig);
00405         }
00406     }
00407     catch (ScriptException &e) {
00408         LOG_WARNING(e.info());
00409     }
00410 }
00411 //-----------------------------------------------------------------
00412 void
00413 OptionAgent::readUserConfig()
00414 {
00415     try {
00416         Path userConfig = Path::dataUserPath(CONFIG_FILE);
00417         if (userConfig.exists()) {
00418             ScriptAgent::agent()->doFile(userConfig);
00419         }
00420     }
00421     catch (ScriptException &e) {
00422         LOG_WARNING(e.info());
00423     }
00424 }
00425 

Generated on Wed Jun 1 09:54:31 2005 for Fish Fillets - Next Generation by  doxygen 1.4.2