00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 # include "TsValue.h"
00025 # include "TsBuilder.h"
00026 # include "ts/ink_defs.h"
00027 
00028 # include <TsErrataUtil.h>
00029 # include <sys/stat.h>
00030 # include <stdio.h>
00031 # include <stdlib.h>
00032 
00033 # if !defined(_MSC_VER)
00034 # define _fileno fileno
00035 # endif
00036 
00037 
00038 namespace ts { namespace config {
00039 
00040 Buffer const detail::NULL_BUFFER;
00041 ConstBuffer const detail::NULL_CONST_BUFFER;
00042 detail::ValueItem detail::ValueTableImpl::NULL_ITEM(VoidValue);
00043 detail::PseudoBool::Type const detail::PseudoBool::FALSE = 0;
00044 detail::PseudoBool::Type const detail::PseudoBool::TRUE = &detail::PseudoBool::operator !;
00045 
00046 bool detail::PseudoBool::operator ! () const { return false; }
00047 
00048 unsigned int const detail::Type_Property[N_VALUE_TYPES] = {
00049     0, 
00050     detail::IS_VALID | detail::IS_CONTAINER, 
00051     detail::IS_VALID | detail::IS_CONTAINER, 
00052     detail::IS_VALID | detail::IS_LITERAL, 
00053     detail::IS_VALID | detail::IS_LITERAL, 
00054 };
00055 
00056 detail::ValueTableImpl::ValueTableImpl() : _generation(0) { }
00057 detail::ValueTableImpl::~ValueTableImpl() {
00058     for ( BufferGroup::iterator spot(_buffers.begin()), limit(_buffers.end()) ; spot != limit ; ++spot)
00059         free(spot->_ptr);
00060 }
00061 
00062 detail::ValueTable::ImplType*
00063 detail::ValueTable::instance() {
00064     if (! _ptr) _ptr.reset(new ImplType);
00065     return _ptr.get();
00066 }
00067 
00068 detail::ValueTable&
00069 detail::ValueTable::forceRootItem() {
00070     ImplType* imp = this->instance();
00071     if (0 == imp->_values.size())
00072         imp->_values.push_back(ValueItem(GroupValue));
00073     return *this;
00074 }
00075 
00076 Rv<detail::ValueIndex>
00077 detail::ValueTable::make(ValueIndex pidx, ValueType type, ConstBuffer const& name) {
00078   Rv<ValueIndex> zret = NULL_VALUE_INDEX;
00079   if (_ptr) {
00080     size_t n = _ptr->_values.size();
00081     
00082     if (pidx < n) {
00083       ValueItem* parent = &(_ptr->_values[pidx]);
00084       if (IS_CONTAINER & Type_Property[parent->_type]) {
00085         ValueItem* item;
00086 
00087         _ptr->_values.push_back(ValueItem(type));
00088         parent = &(_ptr->_values[pidx]); 
00089         item = &(_ptr->_values[n]);
00090         item->_parent = pidx;
00091         parent->_children.push_back(n);
00092         item->_local_index = parent->_children.size() - 1;
00093         
00094         if (GroupValue == parent->_type) item->_name = name;
00095         zret = n; 
00096       } else {
00097         msg::log(zret.errata(), msg::WARN, "Add child failed because parent is not a container.");
00098       }
00099     } else {
00100       msg::logf(zret.errata(), msg::WARN, "Add child failed because parent index (%ul) is out of range (%ul).", pidx.raw(), n);
00101     }
00102   } else {
00103     msg::log(zret.errata(), msg::WARN, "Add child failed because the configuration is null.");
00104   }
00105   return zret;
00106 }
00107 
00108 detail::ValueItem&
00109 detail::ValueTable::operator [] ( ValueIndex idx ) {
00110     assert(_ptr && idx < _ptr->_values.size());
00111     return _ptr->_values[idx];
00112 }
00113 Buffer
00114 detail::ValueTable::alloc(size_t n) {
00115     ImplType* imp = this->instance();
00116     Buffer zret(static_cast<char*>(malloc(n)), n);
00117     if (zret._ptr) imp->_buffers.push_back(zret);
00118     return zret;
00119 }
00120 
00121 
00122 Value
00123 Value::operator [] (size_t idx) const {
00124     Value zret;
00125     detail::ValueItem const* item = this->item();
00126     if (item && idx < item->_children.size()) {
00127         zret = Value(_config, item->_children[idx]);
00128         if (PathValue == zret.getType()) zret = _config.getRoot().find(_config._table[zret._vidx]._path);
00129     }
00130     return zret;
00131 }
00132 
00133 Value
00134 Value::operator [] (ConstBuffer const& name) const {
00135     Value zret;
00136     detail::ValueItem const* item = this->item();
00137     if (item) {
00138         for ( detail::ValueItem::ChildGroup::const_iterator spot = item->_children.begin(), limit = item->_children.end(); spot != limit; ++spot ) {
00139             if (_config._table[*spot]._name == name) {
00140                 zret = Value(_config, *spot);
00141                 if (PathValue == zret.getType()) zret = _config.getRoot().find(_config._table[zret._vidx]._path);
00142                 break;
00143             }
00144         }
00145     }
00146     return zret;
00147 }
00148 
00149 Value
00150 Value::find( ConstBuffer const& path ) {
00151     Value zret = *this;
00152     Path::Parser parser(path);
00153     Rv<Path::Parser::Result> x;
00154     ConstBuffer elt;
00155     for ( x = parser.parse(&elt) ; zret && Path::Parser::EOP != x && Path::Parser::ERROR != x ; x = parser.parse(&elt) ) {
00156         if (Path::Parser::TAG == x) zret = zret[elt];
00157         else if (Path::Parser::INDEX == x) zret = zret[elt._size];
00158         else zret.reset();
00159     }
00160     if (Path::Parser::EOP != x) zret.reset();
00161     return zret;
00162 }
00163 
00164 Value
00165 Value::find(Path const& path ) {
00166     Value zret = *this;
00167     for ( size_t i = 0, n = path.count() ; i < n && zret ; ++i ) {
00168         ConstBuffer const& elt = path[i];
00169         if (elt._ptr) zret = zret[elt];
00170         else zret = zret[elt._size];
00171     }
00172     return zret;
00173 }
00174 
00175 Rv<Value>
00176 Value::makeChild(ValueType type, ConstBuffer const& name) {
00177     Rv<Value> zret;
00178     Rv<detail::ValueIndex> vr = _config._table.make(this->_vidx, type, name);
00179     if (vr.isOK()) zret = Value(_config, vr.result());
00180     else zret.errata() = vr.errata();
00181     return zret;
00182 }
00183 
00184 Rv<Value>
00185 Value::makeGroup(ConstBuffer const& name) {
00186     return this->makeChild(GroupValue, name);
00187 }
00188 
00189 Rv<Value>
00190 Value::makeList(ConstBuffer const& name) {
00191     return this->makeChild(ListValue, name);
00192 }
00193 
00194 Rv<Value>
00195 Value::makeString(ConstBuffer const& text, ConstBuffer const& name) {
00196     Rv<Value> zret = this->makeChild(StringValue, name);
00197     if (zret.isOK()) zret.result().setText(text);
00198     return zret;
00199 }
00200 
00201 Rv<Value>
00202 Value::makeInteger(ConstBuffer const& text, ConstBuffer const& name) {
00203     Rv<Value> zret = this->makeChild(IntegerValue, name);
00204     if (zret.isOK()) zret.result().setText(text);
00205     return zret;
00206 }
00207 
00208 Rv<Value>
00209 Value::makePath(Path const& path, ConstBuffer const& name) {
00210     Rv<Value> zret = this->makeChild(PathValue, name);
00211     if (zret.isOK()) _config._table[zret.result()._vidx]._path = path;
00212     return zret;
00213 }
00214 
00215 Path& Path::reset() {
00216     if (_ptr) {
00217         
00218         if (_ptr.isShared()) _ptr = new ImplType;
00219         else { 
00220             _ptr->_elements.clear();
00221         }
00222     }
00223     return *this;
00224 }
00225 
00226 Rv<Path::Parser::Result>
00227 Path::Parser::parse(ConstBuffer *cbuff) {
00228     Rv<Result> zret = EOP;
00229     enum State {
00230         S_INIT, 
00231         S_INDEX, 
00232         S_TAG, 
00233         S_DASH, 
00234     } state = S_INIT;
00235 
00236     
00237     enum Bucket {
00238         C_INVALID, 
00239         C_DIGIT, 
00240         C_IDENT, 
00241         C_DASH, 
00242         C_DOT, 
00243     };
00244 
00245     if (cbuff) cbuff->reset();
00246     char const* start = _c; 
00247     size_t idx = 0; 
00248 
00249     bool final = false;
00250     while (! final && this->hasInput()) {
00251         Bucket cb;
00252         if (isdigit(*_c)) cb = C_DIGIT;
00253         else if ('_' == *_c || isalpha(*_c)) cb = C_IDENT;
00254         else if ('-' == *_c) cb = C_DASH;
00255         else if ('.' == *_c) cb = C_DOT;
00256         else cb = C_INVALID;
00257 
00258         if (C_INVALID == cb) {
00259             msg::logf(zret, msg::WARN, "Invalid character '%c' [%u] in path.", *_c, *_c);
00260         } else switch (state) {
00261             case S_INIT:
00262                 switch (cb) {
00263                     case C_DIGIT: state = S_INDEX; idx = *_c - '0'; break;
00264                     case C_IDENT: state = S_TAG; break;
00265                     case C_DASH: msg::logf(zret, msg::WARN, "Dash not allowed as leading character for tag."); final = true; break;
00266                     case C_DOT: msg::logf(zret, msg::WARN, "Separator without preceding element."); final = true; break;
00267                     default: msg::logf(zret, msg::WARN, "Internal error: unexpected character %u in INIT state.", *_c); final = true; break;
00268                 }
00269                 break;
00270             case S_INDEX: 
00271                 if (C_DIGIT == cb) idx = 10 * idx + *_c - '0';
00272                 else if (C_DOT == cb) { final = true; }
00273                 else {
00274                     msg::logf(zret, msg::WARN, "Invalid character '%c' [%u] in index element.", *_c, *_c);
00275                     final = true;
00276                 }
00277                 break;
00278             case S_TAG: 
00279                 if (C_IDENT == cb || C_DIGIT == cb) ; 
00280                 else if (C_DASH == cb) state = S_DASH;
00281                 else if (C_DOT == cb) { final = true; }
00282                 else { 
00283                     msg::logf(zret, msg::WARN, "Invalid character '%c' [%u] in index element.", *_c, *_c);
00284                     final = true;
00285                 }
00286                 break;
00287             case S_DASH: 
00288                 if (C_IDENT == cb || C_DIGIT == cb) state = S_TAG;
00289                 else if (C_DOT == cb) {
00290                     msg::log(zret, msg::WARN, "Trailing dash not allowed in tag element.");
00291                     final = true;
00292                 } else if (C_DASH != cb) { 
00293                     msg::logf(zret, msg::WARN, "Invalid character '%c' [%u] in index element.", *_c, *_c);
00294                     final = true;
00295                 }
00296                 break;
00297         }
00298         ++_c;
00299     }
00300     if (!zret.isOK()) {
00301         zret = ERROR;
00302         if (cbuff) cbuff->set(_c - 1, 1);
00303         _c = 0;
00304         _input.reset();
00305     } else if (S_INIT == state) {
00306         zret = EOP;
00307     } else if (S_TAG == state) {
00308         zret = TAG;
00309         if (cbuff) {
00310             cbuff->set(start, _c - start);
00311             
00312             
00313             if (final) cbuff->_size -= 1;
00314         }
00315     } else if (S_INDEX == state) {
00316         zret = INDEX;
00317         if (cbuff) cbuff->_size = idx;
00318     } else if (S_DASH == state) {
00319         zret = ERROR;
00320         msg::log(zret, msg::WARN, "Trailing dash not allowed in tag element.");
00321         if (cbuff) cbuff->set(start, _c - start);
00322     }
00323     return zret;
00324 }
00325 
00326 Value
00327 Configuration::getRoot() const {
00328   const_cast<self*>(this)->_table.forceRootItem();
00329   return Value(*this, 0);
00330 }
00331 
00332 Rv<Configuration>
00333 Configuration::loadFromPath(char const* path) {
00334     Rv<Configuration> zret;
00335     Buffer buffer;
00336     FILE* in = fopen(path, "r");
00337 
00338     if (in) {
00339         struct stat info;
00340         if (0 == fstat(_fileno(in), &info)) {
00341             
00342             buffer = zret.result().alloc(info.st_size + 2);
00343             if (buffer._ptr) {
00344                 size_t n;
00345                 if (0 < (n = fread(buffer._ptr, sizeof(char), info.st_size, in))) {
00346                     buffer._size = n+2;
00347                     memset(buffer._ptr + n, 0, 2); 
00348                     zret = Builder(zret.result()).build(buffer);
00349                 } else {
00350                     msg::logf_errno(zret, msg::WARN, "failed to read %" PRIu64 " bytes from configuration file '%s'", info.st_size, path);
00351                 }
00352             } else {
00353                 msg::logf_errno(zret, msg::WARN, "failed to allocate buffer for configuration file '%s' - needed %" PRIu64 " bytes.", path, info.st_size);
00354             }
00355         } else {
00356             msg::logf_errno(zret, msg::WARN, "failed to determine file information on '%s'", path);
00357         }
00358     } else {
00359         msg::logf_errno(zret, msg::WARN, "failed to open configuration file '%s'", path);
00360     }
00361     return zret;
00362 }
00363 
00364 }}