Logo Search packages:      
Sourcecode: launchtool version File versions  Download package

Config.cc

#include "Config.h"

#include <Exception.h>
//#include <Logger.h>
#include <Regex.h>
#include <string>
#include <stack>
#include <vector>
#include <map>
#include <stdio.h>
#include <unistd.h>     // access
#include <errno.h>      // errno

using namespace std;
using namespace stringf;

//#undef LOGTAG
//#define LOGTAG "conffile"

#define log_pedant(a...) do { } while(0)
#define log_info(a...) do { } while(0)
#define log_error(a...) do { } while(0)

/*
#define log_pedant(a...) do { printf(##a); putchar('\n'); } while (0)
#define log_info(a...) do { printf(##a); putchar('\n'); } while (0)
#define log_error(a...) do { printf(##a); putchar('\n'); } while (0)
*/

static string substr(const string& s, const regmatch_t& r) throw()
{
      return s.substr(r.rm_so, r.rm_eo - r.rm_so);
}

template class map<string, ConfigNode*>;
template class map<string, string>;

class ConfigNode
{
protected:
      /// Reference count to this node
      int _ref;
      
      /// Type for the child map
      typedef map<string, ConfigNode*> cmap_t;
      
      /// Type for the values map
      typedef map<string, string> vmap_t;
      
      /// Child map
      cmap_t cmap;
      
      /// Values map
      vmap_t vmap;

      /// Increase the reference count
      void ref() throw () { ++_ref; }

      /// Decrease the reference count, returning true if it gets to 0
      bool unref() throw () { return (--_ref == 0); }
      
      /// Set the value of node `key' to `val'
      void set(const string& key, ConfigNode* val) throw ()
      {
            if (val)
                  val->ref();  // Do it early to correctly handle the case of x = x;
            ConfigNode*& dest = cmap[key];
            if (dest && dest->unref())
                  delete dest;
            dest = val;
      }
      
      /// Set the value of key `key' to `val'
      void set(const string& key, const string& val) throw ()
      {
            vmap[key] = val;
      }
      
      /// Get the value of key, throwing an exception if not found
      ConfigNode* node(const string& key) const
            throw (ConfigKeyNotFoundException)
      {
            cmap_t::const_iterator i = cmap.find(key);
            if (i == cmap.end())
                  return 0;
            return i->second;
      }

      /// Get the value of key, creating it if it does not exist
      bool value(const string& key, string& dest) const
            throw (ConfigKeyNotFoundException)
      {
            vmap_t::const_iterator i = vmap.find(key);
            if (i == vmap.end())
                  return false;
            dest = i->second;
            return true;
      }

      ConfigNode* findNode(const string& path) const throw ()
      {
            size_t sep = path.find('/');
            if (sep == string::npos)
            {
                  log_pedant("Local %.*s", PFSTR(path));
                  return node(path);
            } else {
                  log_pedant("Sub %.*s / %.*s", PFSTR(path.substr(0, sep)), PFSTR(path.substr(sep + 1)));
                  //for (cmap_t::const_iterator i = cmap.begin(); i != cmap.end(); ++i)
                        //log_pedant(" Avl: %.*s.", PFSTR(i->first));
                  ConfigNode* sub = node(path.substr(0, sep));
                  if (sub)
                        return sub->findNode(path.substr(sep + 1));
                  else
                        return 0;
            }
      }

      /// Get the Config present at `path'/`path1` in dest.  Return true if the
      /// value was found, else false.  If false is returned, dest is left
      /// untouched.
      bool findValue(const string& path, string& dest) const
            throw ()
      {
            size_t sep = path.find('/');
            if (sep == string::npos)
                  return value(path, dest);
            else
            {
                  ConfigNode* sub = findNode(path.substr(0, sep));
                  if (sub)
                        return sub->value(path.substr(sep + 1), dest);
                  else
                        return false;
            }
      }

      ConfigNode() throw () : _ref(0) {}
      virtual ~ConfigNode()
      {
            for (cmap_t::iterator i = cmap.begin();
                        i != cmap.end(); i++)
                  if (i->second->unref())
                        delete i->second;
      }

      friend class Config;
      friend class ConfFileParser;
};

//
// Config
//

Config::Config() throw () : node(new ConfigNode) { node->ref(); }
Config::Config(ConfigNode* n) throw () : node(n) { node->ref(); }
Config::Config(const Config& c) throw ()
{
      if (c.node)
            c.node->ref();
      node = c.node;
}
Config::~Config() throw ()
{
      if (node->unref())
            delete node;
}
Config& Config::operator=(const Config& c)
{
      if (c.node)
            c.node->ref();    // Do it early to correctly handle the case of x = x;
      if (node && node->unref())
            delete node;
      node = c.node;
      return *this;
}

Config Config::getSection(const string& path) const
      throw (ConfigKeyNotFoundException)
{
      ConfigNode* sub = node->findNode(path);
      if (sub)
            return sub;
      else
            throw ConfigKeyNotFoundException(path);
}

string Config::getString(const string& path) const
      throw (ConfigKeyNotFoundException)
{
      string res;
      if (node->findValue(path, res))
            return res;
      else
            throw ConfigKeyNotFoundException(path);
}

string Config::getString(const string& path, const string& dflt) const throw ()
{
      string res;
      if (node->findValue(path, res))
            return res;
      else
            return dflt;
}

string Config::getString(const string& path, const string& path1,
                                    const string& dflt) const throw ()
{
      ConfigNode* sub = node->findNode(path);
      if (sub)
      {
            string res;
            if (sub->value(path1, res))
                  return res;
            else
                  return dflt;
      } else
            return dflt;
}

int Config::getInt(const string& path) const
      throw (ConfigKeyNotFoundException)
{
      return atoi(getString(path).c_str());
}

int Config::getInt(const string& path, int dflt) const throw ()
{
      string res;
      if (node->findValue(path, res))
            return atoi(res.c_str());
      else
            return dflt;
}

int Config::getInt(const string& path, const string& path1, int dflt) const throw ()
{
      ConfigNode* sub = node->findNode(path);
      if (sub)
      {
            string res;
            if (sub->value(path1, res))
                  return atoi(res.c_str());
            else
                  return dflt;
      } else
            return dflt;
}

00251 void Config::visitNodes(NodeVisitor& v) const throw (Exception)
{
      for (ConfigNode::cmap_t::iterator i = node->cmap.begin();
                  i != node->cmap.end(); i++)
            if (!v.visitNode(i->first, i->second))
                  break;
}

00259 void Config::visitValues(ValueVisitor& v) const throw (Exception)
{
      for (ConfigNode::vmap_t::iterator i = node->vmap.begin();
                  i != node->vmap.end(); i++)
            if (!v.visitValue(i->first, i->second))
                  break;
}

class Printer : public Config::NodeVisitor, public Config::ValueVisitor
{
protected:
      string root;
      
public:
      Printer(const string& root) throw () : root(root) {}
      virtual ~Printer() throw () {}

      virtual bool visitNode(const string& str, const Config& node) throw (Exception)
      {
            Printer p(root + '/' + str);
            node.visitNodes(p);
            node.visitValues(p);
            return true;
      }

      virtual bool visitValue(const string& str, const string& val) throw (Exception)
      {
            printf("%.*s/%.*s = %.*s\n", PFSTR(root), PFSTR(str), PFSTR(val));
            return true;
      }
};

void Config::dump(const string& base) const throw ()
{
      Printer p(base);
      visitNodes(p);
      visitValues(p);
}


//
// ConfFileParser
//

class ConfFileParser
{
protected:
      struct input
      {
            string name;
            string base_path;
            FILE* stream;
      };
      string line;
      stack<input> in;

      Regex empty_re;
      Regex include_re;
      Regex alias_re;
      Regex bopen_re;
      Regex bclose_re;
      Regex assign_re;
      Regex assign1_re;

      bool eof;

      string nextLine() throw ();
      bool pushFile(const string& file) throw (FileException);
      bool popFile() throw ();
      ConfigNode* parse_node() throw (RegexException);
      
public:
      ConfFileParser() throw(RegexException) : line(), in(),
            empty_re("^[[:blank:]]*(#.+)?$", REG_EXTENDED | REG_NOSUB),
            include_re("^[[:blank:]]*include[[:blank:]]+([^[:blank:]]+)[[:blank:]]*(#.+)?$", REG_EXTENDED),
            alias_re(  "^[[:blank:]]*alias[[:blank:]]+([^[:blank:]]+)[[:blank:]]+([^[:blank:]]+)[[:blank:]]*(#.+)?$", REG_EXTENDED),
            bopen_re(  "^[[:blank:]]*([^{=]+[^[:blank:]{=])[[:blank:]]*\\{[[:blank:]]*(#.+)?$", REG_EXTENDED),
            bclose_re( "^[[:blank:]]*\\}[[:blank:]]*(#.+)?$", REG_EXTENDED | REG_NOSUB),
            assign_re( "^[[:blank:]]*([^=]+[^[:blank:]=])[[:blank:]]*=[[:blank:]]*\"((\\\\.|[^\"])*)\"[[:blank:]]*(#.+)?$", REG_EXTENDED),
            assign1_re( "^[[:blank:]]*([^=]+[^[:blank:]=])[[:blank:]]*=[[:blank:]]*([^#]*[^[:blank:]#])[[:blank:]]*(#.+)?$", REG_EXTENDED), eof(false) {}

      Config parse(const string& file)
            throw (FileException, RegexException);
};

string ConfFileParser::nextLine() throw ()
{
      if (!in.size())
      {
            eof = true;
            return string();
      }

      FILE* stm = in.top().stream;
      if (feof(stm))
      {
            if (popFile())
                  return nextLine();
            eof = true;
            return string();
      }

      line = "";
      int c;
      while ((c = getc(stm)) != '\n' && c != EOF)
            line += c;

      regmatch_t matches[2];
      if (include_re.match(line.c_str(), 2, matches)
                  && matches[1].rm_so != -1)
      {
            string fname = in.top().base_path;
            fname += "/";
            fname += line.substr(matches[1].rm_so, matches[1].rm_eo);
            //fprintf(stderr, "File: %s, base: %s, result: %s\n",
            //          line.substr(matches[1].rm_so, matches[1].rm_eo).c_str(),
            //          in.top().base_path.c_str(),
            //          fname.c_str());
            pushFile(fname.c_str());
            return nextLine();
      }
      
      return line;
}

bool ConfFileParser::pushFile(const string& file) throw (FileException)
{
      // Must not alter t.stream if the file can't be open due to lack of
      // permissions
      FILE* nstream = fopen(file.c_str(), "rt");

      //if (access(file, R_OK) == -1)
      if (nstream == 0)
            if (errno == EACCES)
            {
                  log_info("Skipped inclusion of unreadable file \"%.*s\"", PFSTR(file));
                  return false;
            } else
                  throw FileException(errno, "opening " + file);

      input t;
      //t.stream = fopen(file, "rt");
      t.stream = nstream;
      log_info("Including file %.*s", PFSTR(file));

      t.name = file;
      unsigned int bend = t.name.rfind('/');
      if (bend == string::npos)
            t.base_path = ".";
      else
            t.base_path = t.name.substr(0, bend);
      in.push(t);
      return true;
}

bool ConfFileParser::popFile() throw ()
{
      if (!in.size())
            return false;

      fclose(in.top().stream);
      in.pop();
      return true;
}

struct alias
{
      string a;
      string b;
      alias (const string& sa, const string& sb) : a(sa), b(sb) {}
};

template class deque<alias*>;

ConfigNode* ConfFileParser::parse_node() throw (RegexException)
{
      deque<alias*> aqueue;

      ConfigNode* node = new ConfigNode();

      while (!eof)
      {
            string s = nextLine();
            regmatch_t matches[4];
            if (empty_re.match(s.c_str()))
                  continue;
            if (alias_re.match(s.c_str(), 4, matches))
            {
                  aqueue.push_back(new alias(substr(s, matches[1]),
                                            substr(s, matches[2])));
                  log_pedant("Alias: %.*s (%.*s -> %.*s)", PFSTR(s),
                                                      PFSTR(aqueue.back()->a),
                                                      PFSTR(aqueue.back()->b));
            } else if (assign_re.match(s.c_str(), 4, matches)) {
                  log_pedant("Assign: %.*s", PFSTR(s));
                  node->set(substr(s, matches[1]),
                                substr(s, matches[2]));
            } else if (assign1_re.match(s.c_str(), 4, matches)) {
                  log_pedant("Assign unquoted: %.*s", PFSTR(s));
                  node->set(substr(s, matches[1]),
                                substr(s, matches[2]));
            } else if (bopen_re.match(s.c_str(), 4, matches)) {
                  log_pedant("Bopen: %.*s", PFSTR(s));
                  string path = substr(s, matches[1]);
                  node->set(path, parse_node());
            } else if (bclose_re.match(s.c_str())) {
                  log_pedant("Bclose: %.*s", PFSTR(s));
                  break;
            } else {
                  fprintf(stderr, "Parse error on line \"%.*s\": ignored\n", PFSTR(s));
            }
      }
      
      // Apply path aliases
      while (aqueue.size())
      {
            alias* anode = aqueue.front();
            log_pedant("Applying alias `%.*s' -> `%.*s'",
                                    PFSTR(anode->a),
                                    PFSTR(anode->b));
            ConfigNode* dest = node->findNode(anode->b);
            if (dest)
                  node->set(anode->a, dest);
            else {
                  string val;
                  if (node->findValue(anode->b, val))
                        node->set(anode->a, val);
                  else
                        fprintf(stderr, "Missing target for mapping %.*s -> %.*s\n",
                                    PFSTR(anode->a), PFSTR(anode->b));
            }
            aqueue.pop_front();
      }

      return node;
}
      
Config ConfFileParser::parse(const string& file)
      throw (FileException, RegexException)
{
      pushFile(file);
      return parse_node();
}

//
// Config
//

Config Config::parse(const string& file) throw (FileException)
{
      try {
            ConfFileParser parser;
            return parser.parse(file);
      } catch (RegexException& e) {
            fprintf(stderr, "%s: %.*s", e.type(), PFSTR(e.desc()));
            return Config();
      }
}

// vim:set ts=4 sw=4:

Generated by  Doxygen 1.6.0   Back to index