1 /*
2  *
3  *  D-Bus++ - C++ bindings for D-Bus
4  *
5  *  Copyright (C) 2005-2007  Paolo Durante <shackan@gmail.com>
6  *
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 
24 
25 #include "xml.h"
26 
27 #include <expat.h>
28 
operator >>(std::istream & in,DBus::Xml::Document & doc)29 std::istream &operator >> (std::istream &in, DBus::Xml::Document &doc)
30 {
31   std::stringbuf xmlbuf;
32   in.get(xmlbuf, '\0');
33   doc.from_xml(xmlbuf.str());
34 
35   return in;
36 }
37 
operator <<(std::ostream & out,const DBus::Xml::Document & doc)38 std::ostream &operator << (std::ostream &out, const DBus::Xml::Document &doc)
39 {
40   return out << doc.to_xml();
41 }
42 
43 using namespace DBus;
44 using namespace DBus::Xml;
45 
Error(const char * error,int line,int column)46 Error::Error(const char *error, int line, int column)
47 {
48   std::ostringstream estream;
49 
50   estream << "line " << line << ", column " << column << ": " << error;
51 
52   _error = estream.str();
53 }
54 
Node(const char * n,const char ** a)55 Node::Node(const char *n, const char **a)
56   : name(n)
57 {
58   if (a)
59     for (int i = 0; a[i]; i += 2)
60     {
61       _attrs[a[i]] = a[i + 1];
62 
63       //debug_log("xml:\t%s = %s", a[i], a[i+1]);
64     }
65 }
66 
operator [](const std::string & key)67 Nodes Nodes::operator[](const std::string &key)
68 {
69   Nodes result;
70 
71   for (iterator i = begin(); i != end(); ++i)
72   {
73     Nodes part = (**i)[key];
74 
75     result.insert(result.end(), part.begin(), part.end());
76   }
77   return result;
78 }
79 
select(const std::string & attr,const std::string & value)80 Nodes Nodes::select(const std::string &attr, const std::string &value)
81 {
82   Nodes result;
83 
84   for (iterator i = begin(); i != end(); ++i)
85   {
86     if ((*i)->get(attr) == value)
87       result.insert(result.end(), *i);
88   }
89   return result;
90 }
91 
operator [](const std::string & key)92 Nodes Node::operator[](const std::string &key)
93 {
94   Nodes result;
95 
96   if (key.length() == 0) return result;
97 
98   for (Children::iterator i = children.begin(); i != children.end(); ++i)
99   {
100     if (i->name == key)
101       result.push_back(&(*i));
102   }
103   return result;
104 }
105 
get(const std::string & attribute)106 std::string Node::get(const std::string &attribute)
107 {
108   if (_attrs.find(attribute) != _attrs.end())
109     return _attrs[attribute];
110   else
111     return "";
112 }
113 
set(const std::string & attribute,std::string value)114 void Node::set(const std::string &attribute, std::string value)
115 {
116   if (value.length())
117     _attrs[attribute] = value;
118   else
119     _attrs.erase(value);
120 }
121 
to_xml() const122 std::string Node::to_xml() const
123 {
124   std::string xml;
125   int depth = 0;
126 
127   _raw_xml(xml, depth);
128 
129   return xml;
130 }
131 
_raw_xml(std::string & xml,int & depth) const132 void Node::_raw_xml(std::string &xml, int &depth) const
133 {
134   xml.append(depth * 2, ' ');
135   xml.append("<" + name);
136 
137   for (Attributes::const_iterator i = _attrs.begin(); i != _attrs.end(); ++i)
138   {
139     xml.append(" " + i->first + "=\"" + i->second + "\"");
140   }
141 
142   if (cdata.length() == 0 && children.size() == 0)
143   {
144     xml.append("/>\n");
145   }
146   else
147   {
148     xml.append(">");
149 
150     if (cdata.length())
151     {
152       xml.append(cdata);
153     }
154 
155     if (children.size())
156     {
157       xml.append("\n");
158       depth++;
159 
160       for (Children::const_iterator i = children.begin(); i != children.end(); ++i)
161       {
162         i->_raw_xml(xml, depth);
163       }
164 
165       depth--;
166       xml.append(depth * 2, ' ');
167     }
168     xml.append("</" + name + ">\n");
169   }
170 }
171 
Document()172 Document::Document()
173   : root(0), _depth(0)
174 {
175 }
176 
Document(const std::string & xml)177 Document::Document(const std::string &xml)
178   : root(0), _depth(0)
179 {
180   from_xml(xml);
181 }
182 
~Document()183 Document::~Document()
184 {
185   delete root;
186 }
187 
188 struct Document::Expat
189 {
190   static void start_doctype_decl_handler(
191     void *data, const XML_Char *name, const XML_Char *sysid, const XML_Char *pubid, int has_internal_subset
192   );
193   static void end_doctype_decl_handler(void *data);
194   static void start_element_handler(void *data, const XML_Char *name, const XML_Char **atts);
195   static void character_data_handler(void *data, const XML_Char *chars, int len);
196   static void end_element_handler(void *data, const XML_Char *name);
197 };
198 
from_xml(const std::string & xml)199 void Document::from_xml(const std::string &xml)
200 {
201   _depth = 0;
202   delete root;
203   root = 0;
204 
205   XML_Parser parser = XML_ParserCreate("UTF-8");
206 
207   XML_SetUserData(parser, this);
208 
209   XML_SetDoctypeDeclHandler(
210     parser,
211     Document::Expat::start_doctype_decl_handler,
212     Document::Expat::end_doctype_decl_handler
213   );
214 
215   XML_SetElementHandler(
216     parser,
217     Document::Expat::start_element_handler,
218     Document::Expat::end_element_handler
219   );
220 
221   XML_SetCharacterDataHandler(
222     parser,
223     Document::Expat::character_data_handler
224   );
225 
226   XML_Status status = XML_Parse(parser, xml.c_str(), xml.length(), true);
227 
228   if (status == XML_STATUS_ERROR)
229   {
230     const char *error = XML_ErrorString(XML_GetErrorCode(parser));
231     int line = XML_GetCurrentLineNumber(parser);
232     int column = XML_GetCurrentColumnNumber(parser);
233 
234     XML_ParserFree(parser);
235 
236     throw Error(error, line, column);
237   }
238   else
239   {
240     XML_ParserFree(parser);
241   }
242 }
243 
to_xml() const244 std::string Document::to_xml() const
245 {
246   return root->to_xml();
247 }
248 
start_doctype_decl_handler(void * data,const XML_Char * name,const XML_Char * sysid,const XML_Char * pubid,int has_internal_subset)249 void Document::Expat::start_doctype_decl_handler(
250   void *data, const XML_Char *name, const XML_Char *sysid, const XML_Char *pubid, int has_internal_subset
251 )
252 {
253 }
254 
end_doctype_decl_handler(void * data)255 void Document::Expat::end_doctype_decl_handler(void *data)
256 {
257 }
258 
start_element_handler(void * data,const XML_Char * name,const XML_Char ** atts)259 void Document::Expat::start_element_handler(void *data, const XML_Char *name, const XML_Char **atts)
260 {
261   Document *doc = (Document *)data;
262 
263   //debug_log("xml:%d -> %s", doc->_depth, name);
264 
265   if (!doc->root)
266   {
267     doc->root = new Node(name, atts);
268   }
269   else
270   {
271     Node::Children *cld = &(doc->root->children);
272 
273     for (int i = 1; i < doc->_depth; ++i)
274     {
275       cld = &(cld->back().children);
276     }
277     cld->push_back(Node(name, atts));
278 
279     //std::cerr << doc->to_xml() << std::endl;
280   }
281   doc->_depth++;
282 }
283 
character_data_handler(void * data,const XML_Char * chars,int len)284 void Document::Expat::character_data_handler(void *data, const XML_Char *chars, int len)
285 {
286   Document *doc = (Document *)data;
287 
288   Node *nod = doc->root;
289 
290   for (int i = 1; i < doc->_depth; ++i)
291   {
292     nod = &(nod->children.back());
293   }
294   int x, y;
295 
296   x = 0;
297   y = len - 1;
298 
299   while (isspace(chars[y]) && y > 0) --y;
300   while (isspace(chars[x]) && x < y) ++x;
301 
302   nod->cdata = std::string(chars, x, y + 1);
303 }
304 
end_element_handler(void * data,const XML_Char * name)305 void Document::Expat::end_element_handler(void *data, const XML_Char *name)
306 {
307   Document *doc = (Document *)data;
308 
309   //debug_log("xml:%d <- %s", doc->_depth, name);
310 
311   doc->_depth--;
312 }
313 
314