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 }}