• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //  tiny XML sub-set tools implementation  -----------------------------------//
2 
3 //  (C) Copyright Beman Dawes 2002.  Distributed under the Boost
4 //  Software License, Version 1.0. (See accompanying file
5 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 
7 #include "tiny_xml.hpp"
8 #include <cassert>
9 #include <cstring>
10 
11 namespace
12 {
13 
eat_whitespace(char & c,std::istream & in)14    inline void eat_whitespace( char & c, std::istream & in )
15    {
16       while ( c == ' ' || c == '\r' || c == '\n' || c == '\t' )
17          in.get( c );
18    }
19 
eat_comment(char & c,std::istream & in)20    void eat_comment( char & c, std::istream & in )
21    {
22       in.get(c);
23       if(c != '-')
24          throw std::string("Invalid comment in XML");
25       in.get(c);
26       if(c != '-')
27          throw std::string("Invalid comment in XML");
28       do{
29          while(in.get(c) && (c != '-'));
30          in.get(c);
31          if(c != '-')
32             continue;
33          in.get(c);
34          if(c != '>')
35             continue;
36          else
37             break;
38       }
39       while(true);
40    }
41 
get_name(char & c,std::istream & in)42    std::string get_name( char & c, std::istream & in )
43    {
44       std::string result;
45       eat_whitespace( c, in );
46       while ( std::strchr(
47          "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.:", c )
48          != 0 )
49       {
50          result += c;
51          if(!in.get( c ))
52             throw std::string("xml: unexpected eof");
53       }
54       return result;
55    }
56 
eat_delim(char & c,std::istream & in,char delim,const std::string & msg)57    void eat_delim( char & c, std::istream & in,
58       char delim, const std::string & msg )
59    {
60       eat_whitespace( c, in );
61       if ( c != delim )
62          throw std::string("xml syntax error, expected ") + delim
63          + " (" + msg + ")";
64       in.get( c );
65    }
66 
get_value(char & c,std::istream & in)67    std::string get_value( char & c, std::istream & in )
68    {
69       std::string result;
70       while ( c != '\"' )
71       {
72          result += c;
73          in.get( c );
74       }
75       in.get( c );
76       return result;
77    }
78 
79 }
80 
81 namespace boost
82 {
83    namespace tiny_xml
84    {
85 
86       //  parse  -----------------------------------------------------------------//
87 
parse(std::istream & in,const std::string & msg)88       element_ptr parse( std::istream & in, const std::string & msg )
89       {
90          char c = 0;  // current character
91          element_ptr e( new element );
92 
93          if(!in.get( c ))
94             throw std::string("xml: unexpected eof");
95          if ( c == '<' )
96             if(!in.get( c ))
97                throw std::string("xml: unexpected eof");
98 
99          if(c == '!')
100          {
101             eat_comment(c, in);
102             return e;
103          }
104          if(c == '?')
105          {
106             // XML processing instruction.
107             e->name += c;
108             if(!in.get( c )) // next char
109                throw std::string("xml: unexpected eof");
110             e->name += get_name(c, in);
111             in >> std::ws;
112             if(!in.get( c )) // next char
113                throw std::string("xml: unexpected eof");
114             while(c != '?')
115             {
116                e->content += c;
117                if(!in.get( c )) // next char
118                   throw std::string("xml: unexpected eof");
119             }
120             if(!in.get( c )) // next char
121                throw std::string("xml: unexpected eof");
122             if(c != '>')
123                throw std::string("Invalid XML processing instruction.");
124             return e;
125          }
126 
127          e->name = get_name( c, in );
128          eat_whitespace( c, in );
129 
130          // attributes
131          while ( (c != '>') && (c != '/') )
132          {
133             attribute a;
134             a.name = get_name( c, in );
135 
136             eat_delim( c, in, '=', msg );
137             eat_delim( c, in, '\"', msg );
138 
139             a.value = get_value( c, in );
140 
141             e->attributes.push_back( a );
142             eat_whitespace( c, in );
143          }
144          if(c == '/')
145          {
146             if(!in.get( c )) // next after '/'
147                throw std::string("xml: unexpected eof");
148             eat_whitespace( c, in );
149             if(c != '>')
150                throw std::string("xml: unexpected /");
151             return e;
152          }
153          if(!in.get( c )) // next after '>'
154             throw std::string("xml: unexpected eof");
155 
156          //eat_whitespace( c, in );
157 
158          do{
159             // sub-elements
160             while ( c == '<' )
161             {
162                if ( in.peek() == '/' )
163                   break;
164                element_ptr child(parse( in, msg ));
165                child->parent = e;
166                e->elements.push_back(child);
167                in.get( c ); // next after '>'
168                //eat_whitespace( c, in );
169             }
170             if (( in.peek() == '/' ) && (c == '<'))
171                break;
172 
173             // content
174             if ( (c != '<') )
175             {
176                element_ptr sub( new element );
177                while ( c != '<' )
178                {
179                   sub->content += c;
180                   if(!in.get( c ))
181                      throw std::string("xml: unexpected eof");
182                }
183                sub->parent = e;
184                e->elements.push_back( sub );
185             }
186 
187             assert( c == '<' );
188             if( in.peek() == '/' )
189                break;
190          }while(true);
191 
192          in.get(c);
193          eat_delim( c, in, '/', msg );
194          std::string end_name( get_name( c, in ) );
195          if ( e->name != end_name )
196             throw std::string("xml syntax error: beginning name ")
197             + e->name + " did not match end name " + end_name
198             + " (" + msg + ")";
199 
200          eat_delim( c, in, '>', msg );
201          if(c != '>')
202          {
203             // we've eaten one character past the >, put it back:
204             if(!in.putback(c))
205                throw std::string("Unable to put back character");
206          }
207          return e;
208       }
209 
210       //  write  ---------------------------------------------------------------//
211 
write(const element & e,std::ostream & out)212       void write( const element & e, std::ostream & out )
213       {
214          if(e.name.size())
215          {
216             out << "<" << e.name;
217             if ( !e.attributes.empty() )
218             {
219                for( attribute_list::const_iterator itr = e.attributes.begin();
220                   itr != e.attributes.end(); ++itr )
221                {
222                   out << " " << itr->name << "=\"" << itr->value << "\"";
223                }
224             }
225             if(e.name[0] == '?')
226             {
227                out << " " << e.content << "?>";
228                return;
229             }
230             if(e.elements.empty() && e.content.empty())
231             {
232                out << "/>";
233                return;
234             }
235             out << ">";
236          }
237          if ( !e.elements.empty() )
238          {
239             for( element_list::const_iterator itr = e.elements.begin();
240                itr != e.elements.end(); ++itr )
241             {
242                write( **itr, out );
243             }
244          }
245          if ( !e.content.empty() )
246          {
247             out << e.content;
248          }
249          if(e.name.size() && (e.name[0] != '?'))
250          {
251             out << "</" << e.name << ">";
252          }
253       }
254 
255    } // namespace tiny_xml
256 } // namespace boost
257 
258