วันจันทร์ที่ 25 ตุลาคม พ.ศ. 2553

xconfig - parsing x-config

xconfig is used to parse x-file file as a configuration file.


e.g.
# This is a comment line
# '#' or '!' must be the first of character of a line
OTFChart.backgroundColor: black
OTFChart.foregroundColor: white
OTFChart.width: 640
OTFChart.height: 480


//////////////////////////////////////////////////////////////////////////////////////////////
// xcnf.h
// Author: Seree Rakwong
// Date: 25-OCT-10
// Purpose: Read a X file
#ifndef xcnf_h
#define xcnf_h

#include <fstream>
#include <string>
#include <map>
#include <iostream>

class xconfig_parser;
class xconfig_interface {
private:
  friend class xconfig_parser;

protected:
  virtual void found_comment(const std::string& comment)=0;
  virtual int  found_key(const std::string& key)=0;
  virtual int  found_value(const std::string& value)=0;
  virtual void found_error(const std::string& filename, unsigned int errline, int errcode)=0;
};

class xconfig : public xconfig_interface {
  friend std::ostream& operator<<(std::ostream& os, const xconfig& xcnf);
private:
  std::map<std::string, std::string> keys_map_;
  std::string cur_key_;

protected:
  void found_comment(const std::string& comment);
  int  found_key(const std::string& key);
  int  found_value(const std::string& value);
  void found_error(const std::string& filename, unsigned int errline, int errcode);

public:
  int get_int(const std::string& key, int def_value = 0);
  double get_double(const std::string& key, double def_value = 0.0);
  std::string get_string(const std::string& key, const std::string& def_value = "");
};

// dump pairs of keys and values
std::ostream& operator<<(std::ostream& os, const xconfig& xcnf);

class xconfig_parser : public std::ifstream {
private:
  std::string filename_;

public:
  xconfig_parser();
  xconfig_parser(const std::string& filename);
  virtual ~xconfig_parser();

public:
  void open(const std::string& filename);
  void open(const char *filename, ios_base::openmode mode = ios_base::in);
  int  parse(xconfig_interface *xcnf_ptr);

public:
  enum xconfig_error { 
    none_error      = 0,
    missing_object  = 1,
    file_not_opened = 2,
    not_allowed_2_separators = 3,
    missing_key     = 1000,
    missing_value   = 1001
  };
};

#endif // xcnf_h

//////////////////////////////////////////////////////////////////////////////////////////////
//xcnf.cpp
// Author: Seree Rakwong
// Date: 25-OCT-10
// Purpose: Read a X file
#include "xcnf.h"
//#include <iostream>
#include <iomanip>

using std::string;
using std::ifstream;
using std::map;
using std::cout;
using std::cerr;
using std::endl;
using std::ostream;

/////////////////////////////////////////////////////////////////////////
// xconfig
void xconfig::found_comment(const string& comment) {
}

int xconfig::found_key(const string& key) {
  keys_map_[key] = ":invalid:";
  cur_key_ = key;
  return 0;
}

int xconfig::found_value(const string& value) {
  keys_map_[cur_key_] = value;
  return 0;
}

void xconfig::found_error(const std::string& filename, unsigned int errline, int errcode) {
  cerr << "Filename: " << filename
       << ", Line: "   << errline
       << ", Code: "   << errcode
       << endl;
}

int xconfig::get_int(const string& key, int def_value) {
  map<string, string>::iterator it;
  it = keys_map_.find(key);
  if(it != keys_map_.end()) 
    return atoi(it->second.c_str());
  return def_value;
}

double xconfig::get_double(const string& key, double def_value) {
  map<string, string>::iterator it;
  it = keys_map_.find(key);
  if(it != keys_map_.end()) 
    return atof(it->second.c_str());
  return def_value;
}

string xconfig::get_string(const string& key, const string& def_value) {
  map<string, string>::iterator it;
  it = keys_map_.find(key);
  if(it != keys_map_.end()) 
    return it->second;
  return def_value;
}

ostream& operator<<(ostream& os, const xconfig& xcnf) {
  map<string, string>::const_iterator it;
  // find the max of length of a key
  int len = 0;
  for(it=xcnf.keys_map_.begin(); it!=xcnf.keys_map_.end(); it++) {
    if(it->first.length() > len) len = it->first.length();
  }
  // print output
  for(it=xcnf.keys_map_.begin(); it!=xcnf.keys_map_.end(); it++) {
    string key("\"");
    key += it->first;
    key += "\"";
    os << "key[" << std::right << std::setw(len+2) << key << "] = " << std::left << it->second << endl;
  }
  return os;
}
/////////////////////////////////////////////////////////////////////////
// xconfig_parser
xconfig_parser::xconfig_parser() 
: filename_("") {
}

xconfig_parser::xconfig_parser(const string& filename)
: ifstream(filename.c_str()),
  filename_(filename) {
}

xconfig_parser::~xconfig_parser() {
  close();
}

void xconfig_parser::open(const string& filename) {
  if(is_open()) close();
  ifstream::open(filename.c_str());
  filename_ = filename;
}

void xconfig_parser::open(const char *filename, ios_base::openmode mode) {
  if(is_open()) close();
  ifstream::open(filename, mode);
  filename_ = filename;
}

int xconfig_parser::parse(xconfig_interface *xcnf_ptr) {
  if(!xcnf_ptr)  return missing_object; // no need parsing
  if(!is_open()) return file_not_opened; // cannot read a file

  unsigned int line_no = 1;
  bool reading_key   = false;
  bool reading_value = false;
  string key;
  string value;
  while(good()) {
    char ch = get();
    // comments
    switch(ch) {
      case '!':
      case '#': {
        // reset
        if(reading_value) {
          if(!value.empty()) { xcnf_ptr->found_value(value); }
          else if(!reading_key) {
            xcnf_ptr->found_error(filename_, line_no, missing_value);
            return missing_value;
          }
        }
        reading_value = false;
        string comment("");
        while(good()) {
          ch = get();
          if('\n' == ch) {
            xcnf_ptr->found_comment(comment);
            comment = "";
            line_no++;
            break;
          }
          else { comment += ch; }
        }
        if(comment.length() > 0) { xcnf_ptr->found_comment(comment); }
        reading_key = true;
        break;
      }
      case '\n': {
        if(reading_value && !value.empty()) {
          xcnf_ptr->found_value(value);
          reading_value = false;
          value = "";
        }
        else if((reading_key && !key.empty()) || (value.empty() && !reading_key)) {
          xcnf_ptr->found_error(filename_, line_no, missing_value);
          return missing_value;
        }
        line_no++;
        reading_key = true;
        break;
      }
      // separator between a key and a value
      case ':':
      case '=': {
        if(reading_key) {
          if(!key.empty()) {
            xcnf_ptr->found_key(key);
            reading_key = false;
            key = "";
            reading_value = true;
          }
          else {
            xcnf_ptr->found_error(filename_, line_no, missing_key);
            return missing_key;
          }
        }
        else {
          if(reading_value) { 
            xcnf_ptr->found_error(filename_, line_no, not_allowed_2_separators);
            return not_allowed_2_separators;
          }
          //else { reading_value = true; }
        }
        reading_value = true;
        value = "";
        break;
      }
      // white spaces
      case ' ':
      case '\t':
      case '\r': {
        //no action
        break;
      }
      default: {
        //a-z, A-Z, 0-9, ., _
        if(reading_key) { key += ch; }
        else if(reading_value) { value += ch; }
        break;
      }
    }
  }
  if(!value.empty()) { xcnf_ptr->found_value(value); }
  return 0;
}








/////////////////////////////////////////////////////////////////////////
#include "xcnf.h"
// Author: Seree Rakwong
// Date: 25-OCT-10
// Purpose: Read a X file
#include <iostream>

using namespace std;

class myconfig : public xconfig {
protected:
  void  found_comment(const string& comment) {
    cout << comment << endl;
  }
public:
  string get_service()        { return get_string("OTFChart.Service"); }
  int    get_downloadBuffer() { return get_int("OTFChart.downloadBuffer"); }
  double get_imageWidth()     { return get_double("OTFChart.imageWidth"); }
  double get_imageHeight()    { return get_double("OTFChart.imageHeight"); }
  string get_unknown()        { return get_string("OTFChart.unknown", ":invalid:"); }
};

int main(int argc, char *argv[]) {
  if(argc < 2) {
    cerr << "USAGE: " << argv[0] << " config_filename" << endl;
    return 1;
  }
  myconfig xcnf;
  xconfig_parser parser(argv[1]);

  int rc = 0;
  if((rc = parser.parse(&xcnf)) != 0) {
    switch(rc) {
      case xconfig_parser::missing_key: {
        cerr << "errcode(" << rc << ") - missing key" << endl;
        break;
      }
      case xconfig_parser::missing_value: {
        cerr << "errcode(" << rc << ") - missing value" << endl;
        break;
      }
    }
    return 1;
  }
  cout << "service : " << xcnf.get_service() << endl;
  cout << "buffer  : " << xcnf.get_downloadBuffer() << endl;
  cout << "width   : " << xcnf.get_imageWidth() << endl;
  cout << "height  : " << xcnf.get_imageHeight() << endl;
  cout << "unknown : " << xcnf.get_unknown() << endl;
  cout << "dump all keys and values" << endl;
  cout << xcnf << endl;
  return 0;
}






###########################################
# xtest.cnf
# this is a test comment line
OTFChart.Service: RI2APICHART




OTFChart.imageHeight: 50.01 # this is an image height
# test failed missing value
OTFChart.colorBG: blue
OTFChart.colorFG: yellow


# yep, this is a second comment line
# also this is too
OTFChart.downloadBuffer: 51200
OTFChart.imageWidth    :  101.23