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

InkXml.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
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 /***************************************************************************
00025  InkXml.cc
00026 
00027 
00028  ***************************************************************************/
00029 #include "libts.h"
00030 
00031 #include "InkXml.h"
00032 
00033 /*-------------------------------------------------------------------------
00034   InkXmlAttr
00035   -------------------------------------------------------------------------*/
00036 
00037 InkXmlAttr::InkXmlAttr(const char *tag, const char *value)
00038 {
00039   m_tag = ats_strdup(tag);
00040   m_value = ats_strdup(value);
00041 }
00042 
00043 InkXmlAttr::~InkXmlAttr()
00044 {
00045   ats_free(m_tag);
00046   ats_free(m_value);
00047 }
00048 
00049 void
00050 InkXmlAttr::display(FILE * fd)
00051 {
00052   fprintf(fd, "    <%s,%s>\n", m_tag, m_value);
00053 }
00054 
00055 /*-------------------------------------------------------------------------
00056   InkXmlObject
00057   -------------------------------------------------------------------------*/
00058 
00059 InkXmlObject::InkXmlObject(const char *object_name, bool dup_attrs_allowed)
00060 {
00061   m_object_name = ats_strdup(object_name);
00062   m_dup_attrs_allowed = dup_attrs_allowed;
00063 }
00064 
00065 InkXmlObject::~InkXmlObject()
00066 {
00067   ats_free(m_object_name);
00068   clear_tags();
00069 }
00070 
00071 void
00072 InkXmlObject::clear_tags()
00073 {
00074   InkXmlAttr *attr;
00075   while ((attr = m_tags.dequeue())) {
00076     delete attr;
00077   }
00078 }
00079 
00080 int
00081 InkXmlObject::add_tag(const char *tag, const char *value)
00082 {
00083   ink_assert(tag != NULL);
00084   ink_assert(value != NULL);
00085 
00086   InkXmlAttr *attr = new InkXmlAttr(tag, value);
00087   return add_attr(attr);
00088 }
00089 
00090 int
00091 InkXmlObject::add_attr(InkXmlAttr * attr)
00092 {
00093   ink_assert(attr != NULL);
00094 
00095   if (!m_dup_attrs_allowed) {
00096     for (InkXmlAttr * a = first(); a; a = next(a)) {
00097       if (!strcmp(a->tag(), attr->tag())) {
00098         Debug("xml", "tag %s already exists & dups not allowed", attr->tag());
00099         return -1;
00100       }
00101     }
00102   }
00103   m_tags.enqueue(attr);
00104   return 0;
00105 }
00106 
00107 char *
00108 InkXmlObject::tag_value(const char *tag_name)
00109 {
00110   ink_assert(tag_name != NULL);
00111 
00112   for (InkXmlAttr * a = first(); a; a = next(a)) {
00113     if (!strcmp(a->tag(), tag_name)) {
00114       return a->value();
00115     }
00116   }
00117   return NULL;
00118 }
00119 
00120 void
00121 InkXmlObject::display(FILE * fd)
00122 {
00123   fprintf(fd, "<%s>\n", m_object_name);
00124   for (InkXmlAttr * a = first(); a; a = next(a)) {
00125     a->display(fd);
00126   }
00127 }
00128 
00129 /*-------------------------------------------------------------------------
00130   InkXmlConfigFile
00131   -------------------------------------------------------------------------*/
00132 
00133 InkXmlConfigFile::InkXmlConfigFile(const char *config_file):
00134 m_line(0),
00135 m_col(0)
00136 {
00137   m_config_file = ats_strdup(config_file);
00138 }
00139 
00140 InkXmlConfigFile::~InkXmlConfigFile()
00141 {
00142   ats_free(m_config_file);
00143   clear_objects();
00144 }
00145 
00146 void
00147 InkXmlConfigFile::clear_objects()
00148 {
00149   InkXmlObject *obj;
00150   while ((obj = m_objects.dequeue())) {
00151     delete obj;
00152   }
00153 }
00154 
00155 /*                                                                  */
00156 int
00157 InkXmlConfigFile::parse(int fd)
00158 {
00159   ink_assert(fd >= 0);
00160   Debug("log", "Parsing XML config info from memory..");
00161 
00162   m_line = 1;
00163   m_col = 0;
00164 
00165   InkXmlObject *obj;
00166   while ((obj = get_next_xml_object(fd)) != NULL) {
00167     Debug("log", "Adding XML object <%s>", obj->object_name());
00168     add_object(obj);
00169   }
00170 
00171   return 0;
00172 }
00173 
00174 /*                                                                  */
00175 
00176 int
00177 InkXmlConfigFile::parse()
00178 {
00179   ink_assert(m_config_file != NULL);
00180   Debug("xml", "Parsing XML config file %s ...", m_config_file);
00181 
00182   int fd =::open(m_config_file, O_RDONLY);
00183   if (fd < 0) {
00184     Debug("xml", "Error opening %s: %d, %s", m_config_file, fd, strerror(errno));
00185     return -1;
00186   }
00187 
00188   m_line = 1;
00189   m_col = 0;
00190 
00191   InkXmlObject *obj;
00192   while ((obj = get_next_xml_object(fd)) != NULL) {
00193     Debug("xml", "Adding XML object <%s>", obj->object_name());
00194     add_object(obj);
00195   }
00196 
00197   ::close(fd);
00198   return 0;
00199 }
00200 
00201 InkXmlObject *
00202 InkXmlConfigFile::find_object(const char *object_name)
00203 {
00204   for (InkXmlObject * obj = first(); obj; obj = next(obj)) {
00205     if (!strcmp(object_name, obj->object_name())) {
00206       return obj;
00207     }
00208   }
00209   return NULL;
00210 }
00211 
00212 void
00213 InkXmlConfigFile::display(FILE * fd)
00214 {
00215   size_t i;
00216 
00217   fprintf(fd, "\n");
00218   for (i = 0; i < strlen(m_config_file) + 13; i++)
00219     fputc('-', fd);
00220   fprintf(fd, "\nConfig File: %s\n", m_config_file);
00221   for (i = 0; i < strlen(m_config_file) + 13; i++)
00222     fputc('-', fd);
00223   fprintf(fd, "\n");
00224   for (InkXmlObject * obj = first(); obj; obj = next(obj)) {
00225     obj->display(fd);
00226     fprintf(fd, "\n");
00227   }
00228 }
00229 
00230 void
00231 InkXmlConfigFile::add_object(InkXmlObject * object)
00232 {
00233   ink_assert(object != NULL);
00234   m_objects.enqueue(object);
00235 }
00236 
00237 /*-------------------------------------------------------------------------
00238   InkXmlConfigFile::get_next_xml_object()
00239 
00240   This routine (and its friends) does the real work of parsing the given
00241   open file for the next XML object.
00242   -------------------------------------------------------------------------*/
00243 
00244 InkXmlObject *
00245 InkXmlConfigFile::get_next_xml_object(int fd)
00246 {
00247   ink_assert(fd >= 0);
00248 
00249   char token;
00250   bool start_object = false;
00251 
00252   while ((token = next_token(fd)) != EOF) {
00253     switch (token) {
00254 
00255     case '<':
00256       start_object = true;
00257       break;
00258 
00259     case '!':
00260       if (!start_object)
00261         return parse_error();
00262       if ((token = scan_comment(fd)) == EOF) {
00263         return NULL;
00264       }
00265       Debug("xml", "comment scanned");
00266       start_object = false;
00267       break;
00268 
00269     default:
00270       if (!start_object)
00271         return parse_error();
00272       return scan_object(fd, token);
00273     }
00274   }
00275   return NULL;
00276 }
00277 
00278 InkXmlObject *
00279 InkXmlConfigFile::parse_error()
00280 {
00281   Debug("xml", "Invalid XML tag, line %u, col %u", m_line, m_col);
00282   return NULL;
00283 }
00284 
00285 #define BAD_ATTR ((InkXmlAttr*)1)
00286 
00287 InkXmlObject *
00288 InkXmlConfigFile::scan_object(int fd, char token)
00289 {
00290   // this routine is called just after the first '<' is read for a new
00291   // object.
00292 
00293   const int max_ident_len = 2048;
00294   char ident[max_ident_len];
00295   int ident_len = 0;
00296 
00297   while (token != '>' && ident_len < max_ident_len) {
00298     ident[ident_len++] = token;
00299     token = next_token(fd);
00300     if (token == EOF)
00301       return parse_error();
00302   }
00303   if (!ident_len || ident_len >= max_ident_len) {
00304     return parse_error();
00305   }
00306 
00307   ident[ident_len] = 0;
00308   InkXmlObject *obj = new InkXmlObject(ident);
00309   ink_assert(obj != NULL);
00310 
00311   InkXmlAttr *attr;
00312   while ((attr = scan_attr(fd, ident)) != NULL) {
00313     if (attr == BAD_ATTR) {
00314       delete obj;
00315       return parse_error();
00316     }
00317     obj->add_attr(attr);
00318   }
00319 
00320   return obj;
00321 }
00322 
00323 InkXmlAttr *
00324 InkXmlConfigFile::scan_attr(int fd, const char *id)
00325 {
00326   // this routine is called after the object identifier has been scannedm
00327   // and should attempt to scan for the next attribute set.  When we see
00328   // the end of the object (closing identifier), we scan it and return
00329   // NULL for the attribute, signalling that there are no more
00330   // attributes.
00331 
00332   char token, prev, next;
00333   const int buf_size = 2048;
00334   char name[buf_size];
00335   char value[buf_size];
00336   char ident[buf_size];
00337   char *write_to = NULL;
00338   int write_len = 0;
00339   bool start_attr = false;
00340   bool in_quotes = false;
00341   InkXmlAttr *attr = NULL;
00342 
00343   prev = next = 0;
00344   while ((token = next_token(fd, !in_quotes)) != EOF) {
00345     switch (token) {
00346     case '<':
00347       if (in_quotes && write_to) {
00348         if (write_len >= buf_size)
00349           return BAD_ATTR;
00350         write_to[write_len++] = token;
00351         break;
00352       }
00353       start_attr = true;
00354       write_to = name;
00355       write_len = 0;
00356       break;
00357 
00358     case '=':
00359       if (in_quotes && write_to) {
00360         if (write_len >= buf_size)
00361           return BAD_ATTR;
00362         write_to[write_len++] = token;
00363         break;
00364       }
00365       if (!start_attr)
00366         return BAD_ATTR;
00367       write_to[write_len] = 0;
00368       write_to = value;
00369       write_len = 0;
00370       break;
00371 
00372     case '"':
00373       if (in_quotes) {
00374         if (prev == '\\') {
00375           // escape the quote, just replace the backslash
00376           // with it
00377           write_to[write_len - 1] = token;
00378           break;
00379         }
00380       }
00381       in_quotes = !in_quotes;
00382       break;
00383 
00384     case '/':
00385       if (in_quotes && write_to) {
00386         if (write_len >= buf_size)
00387           return BAD_ATTR;
00388         write_to[write_len++] = token;
00389         break;
00390       }
00391       if (!start_attr)
00392         return BAD_ATTR;
00393       if (prev == '<') {
00394         write_len = 0;
00395         token = next_token(fd, !in_quotes);
00396         while (token != '>' && write_len < buf_size) {
00397           ident[write_len++] = token;
00398           token = next_token(fd, !in_quotes);
00399           if (token == EOF)
00400             return BAD_ATTR;
00401         }
00402         if (!write_len || write_len >= buf_size) {
00403           return BAD_ATTR;
00404         }
00405         ident[write_len] = 0;
00406         if (strcmp(ident, id) != 0)
00407           return BAD_ATTR;
00408         return NULL;
00409       }
00410 
00411       next = next_token(fd, !in_quotes);
00412       if (next != '>')
00413         return BAD_ATTR;
00414       write_to[write_len] = 0;
00415       attr = new InkXmlAttr(name, value);
00416       ink_assert(attr != NULL);
00417       return attr;
00418 
00419     case '>':
00420       if (in_quotes && write_to) {
00421         if (write_len >= buf_size)
00422           return BAD_ATTR;
00423         write_to[write_len++] = token;
00424         break;
00425       }
00426       // seen at this point, this is an error, probably becase
00427       // the person forgot the trailing '/'.
00428       return BAD_ATTR;
00429 
00430     default:
00431       if (!start_attr)
00432         return BAD_ATTR;
00433       if (write_len >= buf_size)
00434         return BAD_ATTR;
00435       write_to[write_len++] = token;
00436       break;
00437     }
00438     prev = token;
00439   }
00440   return BAD_ATTR;
00441 }
00442 
00443 char
00444 InkXmlConfigFile::next_token(int fd, bool eat_whitespace)
00445 {
00446   char ch;
00447   while (read(fd, &ch, 1) == 1) {
00448     if (ch == '\n') {
00449       m_line++;
00450       m_col = 0;
00451       continue;
00452     }
00453     m_col++;
00454     if (eat_whitespace && ParseRules::is_space(ch))
00455       continue;
00456     return ch;
00457   }
00458   return EOF;
00459 }
00460 
00461 char
00462 InkXmlConfigFile::scan_comment(int fd)
00463 {
00464   // this routine is called when we're just past a "<!" in the file.  we
00465   // need to skip until we find the matching '>'.
00466 
00467   int lt_stack = 1;             // we've already seen one '<' character
00468   char token;
00469   while ((token = next_token(fd)) != EOF) {
00470     switch (token) {
00471     case '<':
00472       lt_stack++;
00473       break;
00474     case '>':
00475       lt_stack--;
00476       if (lt_stack == 0) {
00477         return token;
00478       }
00479       break;
00480     default:
00481       break;
00482     }
00483   }
00484   return EOF;
00485 }

Generated by  doxygen 1.7.1