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