1 #include "compare.hh"
2 #include "compareimpl.hh"
3 #include <sstream>
4 #include <iostream>
5 #include <assert.h>
6 #include <string.h>
7 
8 using std::stringstream;
9 
10 static int compare_elem(xmlNodePtr m, xmlNodePtr n, bool deep);
11 static int compare_content(xmlNodePtr m, xmlNodePtr n);
12 static int compare_pi(xmlNodePtr m, xmlNodePtr n);
13 static int compare_children(xmlNodePtr m, xmlNodePtr n);
14 
15 // A wrapper around xmlGetProp/xmlGetNsProp. Must be passed a valid
16 // attribute (belonging to some node). Throws on error (never returns
17 // NULL); the returned value must be freed (by xmlFree).
18 static xmlChar *get_value(xmlAttrPtr a);
19 
20 #if 0
21 #define TRACE(trace_arg) std::cerr << trace_arg << std::endl
22 #else
23 #define TRACE(trace_arg)
24 #endif
25 
compare(xmlNodePtr m,xmlNodePtr n,bool deep)26 int compare(xmlNodePtr m, xmlNodePtr n, bool deep)
27 {
28     TRACE("enter compare");
29 
30     assert(m);
31     assert(n);
32 
33     int type = m->type - n->type;
34     if (type) {
35 	return type;
36     }
37 
38     switch (m->type) {
39     case XML_ELEMENT_NODE:
40 	return compare_elem(m, n, deep);
41     case XML_TEXT_NODE:
42     case XML_COMMENT_NODE:
43     case XML_CDATA_SECTION_NODE:
44 	return compare_content(m, n);
45     case XML_PI_NODE:
46 	return compare_pi(m, n);
47     }
48 
49     stringstream s;
50     s << "unsupported node type " << m->type;
51     throw s.str();
52 }
53 
have_nulls(const void * p,const void * q,int & res)54 bool compareimpl::have_nulls(const void *p, const void *q, int &res)
55 {
56     if (!p) {
57 	res = q ? -1 : 0;
58 	return true;
59     }
60 
61     if (!q) {
62 	res = 1;
63 	return true;
64     }
65 
66     return false;
67 }
68 
compare_ns(xmlNsPtr a,xmlNsPtr b)69 int compareimpl::compare_ns(xmlNsPtr a, xmlNsPtr b)
70 {
71     assert(a);
72     assert(b);
73     assert(a->href);
74     assert(b->href);
75 
76     int href = strcmp(reinterpret_cast<const char *>(a->href),
77 	reinterpret_cast<const char *>(b->href));
78     if (href) {
79 	return href;
80     }
81 
82     int res;
83     if (have_nulls(a->prefix, b->prefix, res)) {
84 	return res;
85     } else {
86 	return strcmp(reinterpret_cast<const char *>(a->prefix),
87 	    reinterpret_cast<const char *>(b->prefix));
88     }
89 }
90 
compare_attr(xmlAttrPtr a,xmlAttrPtr b)91 int compareimpl::compare_attr(xmlAttrPtr a, xmlAttrPtr b)
92 {
93     int name = compareimpl::compare_name<xmlAttrPtr>(a, b);
94     if (name) {
95 	return name;
96     }
97 
98     xmlChar *v = get_value(a);
99     xmlChar *w = get_value(b);
100 
101     int rv = strcmp(reinterpret_cast<char *>(v),
102 	reinterpret_cast<char *>(w));
103 
104     xmlFree(w);
105     xmlFree(v);
106 
107     return rv;
108 }
109 
compare_elem(xmlNodePtr m,xmlNodePtr n,bool deep)110 static int compare_elem(xmlNodePtr m, xmlNodePtr n, bool deep)
111 {
112     int name = compareimpl::compare_name<xmlNodePtr>(m, n);
113     if (name) {
114 	return name;
115     }
116 
117     int ns_def = compareimpl::compare_set<xmlNsPtr>(m->nsDef,
118 	n->nsDef);
119     if (ns_def) {
120 	return ns_def;
121     }
122 
123     int properties = compareimpl::compare_set<xmlAttrPtr>(
124 	m->properties,
125 	n->properties);
126     if (properties) {
127 	return properties;
128     }
129 
130     return deep ? compare_children(m, n) : 0;
131 }
132 
get_value(xmlAttrPtr a)133 static xmlChar *get_value(xmlAttrPtr a)
134 {
135     assert(a);
136     assert(a->parent);
137 
138     xmlChar *out;
139 
140     if (a->ns) {
141 	assert(a->ns->href);
142 	out = xmlGetNsProp(a->parent, a->name, a->ns->href);
143     } else {
144 	out = xmlGetProp(a->parent, a->name);
145     }
146 
147     if (!out) {
148 	// should be pretty rare, but let's say an allocation might
149 	// have failed
150 	throw std::string("cannot get attribute value");
151     }
152 
153     return out;
154 }
155 
compare_pi(xmlNodePtr p,xmlNodePtr q)156 static int compare_pi(xmlNodePtr p, xmlNodePtr q)
157 {
158     assert(p->name);
159     assert(q->name);
160 
161     int name = strcmp(reinterpret_cast<const char *>(p->name),
162 	reinterpret_cast<const char *>(q->name));
163     if (name) {
164 	return name;
165     }
166 
167     return compare_content(p, q);
168 }
169 
compare_content(xmlNodePtr m,xmlNodePtr n)170 static int compare_content(xmlNodePtr m, xmlNodePtr n)
171 {
172     int res;
173     if (compareimpl::have_nulls(m->content, n->content, res)) {
174 	return res;
175     }
176 
177     return strcmp(reinterpret_cast<char *>(m->content),
178 	reinterpret_cast<char *>(n->content));
179 }
180 
compare_children(xmlNodePtr m,xmlNodePtr n)181 static int compare_children(xmlNodePtr m, xmlNodePtr n)
182 {
183     xmlNodePtr mch = m->children;
184     xmlNodePtr nch = n->children;
185 
186     while (mch && nch) {
187 	int res = compare(mch, nch, true);
188 	if (res) {
189 	    return res;
190 	}
191 
192 	mch = mch->next;
193 	nch = nch->next;
194     }
195 
196     if (!mch) {
197 	return nch ? -1 : 0;
198     } else {
199 	assert(!nch);
200 	return 1;
201     }
202 }
203 
204