• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

TsValue.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003     TS Configuration API implementation.
00004 
00005     @section license License
00006 
00007     Licensed to the Apache Software Foundation (ASF) under one
00008     or more contributor license agreements.  See the NOTICE file
00009     distributed with this work for additional information
00010     regarding copyright ownership.  The ASF licenses this file
00011     to you under the Apache License, Version 2.0 (the
00012     "License"); you may not use this file except in compliance
00013     with the License.  You may obtain a copy of the License at
00014 
00015     http://www.apache.org/licenses/LICENSE-2.0
00016 
00017     Unless required by applicable law or agreed to in writing, software
00018     distributed under the License is distributed on an "AS IS" BASIS,
00019     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020     See the License for the specific language governing permissions and
00021     limitations under the License.
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 // This should not be called, it is used only as a pointer value.
00046 bool detail::PseudoBool::operator ! () const { return false; }
00047 // ---------------------------------------------------------------------------
00048 unsigned int const detail::Type_Property[N_VALUE_TYPES] = {
00049     0, // Void
00050     detail::IS_VALID | detail::IS_CONTAINER, // List
00051     detail::IS_VALID | detail::IS_CONTAINER, // Group
00052     detail::IS_VALID | detail::IS_LITERAL, // String
00053     detail::IS_VALID | detail::IS_LITERAL, // Integer
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     // Check the parent
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]); // possibly stale, refresh.
00089         item = &(_ptr->_values[n]);
00090         item->_parent = pidx;
00091         parent->_children.push_back(n);
00092         item->_local_index = parent->_children.size() - 1;
00093         // Only use the name if the parent is a group.
00094         if (GroupValue == parent->_type) item->_name = name;
00095         zret = n; // mark for return to caller.
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         // If we're sharing the instance, make a new one for us.
00218         if (_ptr.isShared()) _ptr = new ImplType;
00219         else { // clear out the existing instance.
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, // initial state
00231         S_INDEX, // reading index.
00232         S_TAG, // reading tag.
00233         S_DASH, // reading dashes in tag.
00234     } state = S_INIT;
00235 
00236     // Character bucket
00237     enum Bucket {
00238         C_INVALID, // Invalid input.
00239         C_DIGIT, // digit.
00240         C_IDENT, // Identifier character.
00241         C_DASH, // A dash
00242         C_DOT, // A dot (period).
00243     };
00244 
00245     if (cbuff) cbuff->reset();
00246     char const* start = _c; // save starting character location.
00247     size_t idx = 0; // accumulator for index value.
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: // reading an 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: // reading a tag.
00279                 if (C_IDENT == cb || C_DIGIT == cb) ; // continue
00280                 else if (C_DASH == cb) state = S_DASH;
00281                 else if (C_DOT == cb) { final = true; }
00282                 else { // should never happen, but be safe.
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: // dashes inside tag.
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) { // should never happen, but be safe.
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             // if @a final is set, then we parsed a dot separator.
00312             // don't include it in the returned tag.
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             // Must reserve 2 bytes at the end for FLEX terminator.
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); // required by FLEX
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 }} // namespace ts::config

Generated by  doxygen 1.7.1