1 /******************************************************************************
2 *
3 * utilxml.cpp - Implementaion of utility classes to handle
4 * XML processing
5 *
6 * $Id: utilxml.cpp 3439 2016-10-23 08:32:02Z scribe $
7 *
8 * Copyright 2003-2013 CrossWire Bible Society (http://www.crosswire.org)
9 * CrossWire Bible Society
10 * P. O. Box 2528
11 * Tempe, AZ 85280-2528
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation version 2.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 */
23
24 #include <utilxml.h>
25 #include <ctype.h>
26 #include <utilstr.h>
27
28
29 SWORD_NAMESPACE_START
30
31
parse() const32 void XMLTag::parse() const {
33 int i;
34 int start;
35 char *name = 0;
36 char *value = 0;
37 attributes.clear();
38
39 if (!buf)
40 return;
41 for (i = 0; ((buf[i]) && (!isalpha(buf[i]))); i++);
42 for (; buf[i]; i++) {
43 if (strchr("\t\r\n ", buf[i])) {
44 // Convert newlines, carriage returns and tabs to spaces
45 buf[i] = ' ';
46
47 for (; ((buf[i]) && (!isalpha(buf[i]))); i++);
48 if (buf[i]) { // we have an attribute name
49 start = i;
50 // Deprecated: check for following whitespacee
51 // Should be: for (; (buf[i] && buf[i] != '='; i++);
52 for (; ((buf[i]) && (!strchr(" =", buf[i]))); i++);
53
54 if (i-start) {
55 if (name)
56 delete [] name;
57 name = new char [ (i-start) + 1 ];
58 strncpy(name, buf+start, i-start);
59 name[i-start] = 0;
60 }
61
62 // The following does not allow for empty attributes
63 //for (; ((buf[i]) && (strchr(" =\"\'", buf[i]))); i++);
64
65 // skip space preceding the = sign
66 // Deprecated: this is not part of the xml spec
67 for (; buf[i] == ' '; i++) ;
68
69 // skip the = sign
70 if (buf[i])
71 i++;
72
73 // skip space following the = sign
74 // Deprecated: this is not part of the xml spec
75 for (; buf[i] == ' '; i++) ;
76
77 // remember and skip the quote sign
78 char quoteChar = buf[i];
79 if (quoteChar)
80 i++;
81
82 if (buf[i]) { // we have attribute value
83 start = i;
84 // Skip until matching quote character
85 for (; ((buf[i]) && (buf[i] != quoteChar)); i++);
86
87 // Allow for empty quotes
88 if (value)
89 delete [] value;
90 value = new char [ (i-start) + 1 ];
91 if (i-start) {
92 strncpy(value, buf+start, i-start);
93 }
94 value[i-start] = 0;
95 attributes[name] = value;
96 }
97 }
98 }
99
100 // if there are no more characters left then quit
101 if (!buf[i])
102 break;
103
104 }
105 for (;i;i--) {
106 if (buf[i] == '/')
107 empty = true;
108 if (!strchr(" \t\r\n>\t", buf[i]))
109 break;
110 }
111
112 parsed = true;
113 if (name) delete [] name;
114 if (value) delete [] value;
115 }
116
117
XMLTag(const char * tagString)118 XMLTag::XMLTag(const char *tagString) {
119
120 name = 0;
121 buf = 0;
122 setText(tagString);
123 }
124
125
XMLTag(const XMLTag & t)126 XMLTag::XMLTag(const XMLTag& t) : attributes(t.attributes) {
127 parsed = t.parsed;
128 empty = t.empty;
129 endTag = t.endTag;
130 if (t.buf) {
131 int len = (int)strlen(t.buf);
132 buf = new char[len + 1];
133 memcpy(buf, t.buf, len + 1);
134 }
135 if (t.name) {
136 int len = (int)strlen(t.name);
137 name = new char[len + 1];
138 memcpy(name, t.name, len + 1);
139 }
140 }
141
142
setText(const char * tagString)143 void XMLTag::setText(const char *tagString) {
144 parsed = false;
145 empty = false;
146 endTag = false;
147
148 if (buf) {
149 delete [] buf;
150 buf = 0;
151 }
152
153 if (!tagString) // assert tagString before proceeding
154 return;
155
156 stdstr(&buf, tagString);
157
158 int start = 0;
159 int i;
160
161 // skip beginning silliness
162 for (i = 0; ((tagString[i]) && (!isalpha(tagString[i]))); i++) {
163 if (tagString[i] == '/')
164 endTag = true;
165 }
166 start = i;
167 for (; ((tagString[i]) && (!strchr("\t\r\n />", tagString[i]))); i++);
168 if (i-start) {
169 if (name)
170 delete [] name;
171 name = new char [ (i-start) + 1 ];
172 strncpy(name, tagString+start, i-start);
173 name[i-start] = 0;
174 if (tagString[i] == '/')
175 empty = true;
176 }
177 }
178
179
~XMLTag()180 XMLTag::~XMLTag() {
181 if (buf)
182 delete [] buf;
183 if (name)
184 delete [] name;
185 }
186
187
getAttributeNames() const188 const StringList XMLTag::getAttributeNames() const {
189 StringList retVal;
190
191 if (!parsed)
192 parse();
193
194 for (StringPairMap::const_iterator it = attributes.begin(); it != attributes.end(); it++)
195 retVal.push_back(it->first.c_str());
196
197 return retVal;
198 }
199
200
getPart(const char * buf,int partNum,char partSplit) const201 const char *XMLTag::getPart(const char *buf, int partNum, char partSplit) const {
202 for (; (buf && partNum); partNum--) {
203 buf = strchr(buf, partSplit);
204 if (buf)
205 buf++;
206 }
207 if (buf) {
208 const char *end = strchr(buf, partSplit);
209 junkBuf = buf;
210 if (end)
211 junkBuf.setSize(end - buf);
212 return junkBuf.c_str();
213 }
214 return 0;
215 }
216
217
getAttributePartCount(const char * attribName,char partSplit) const218 int XMLTag::getAttributePartCount(const char *attribName, char partSplit) const {
219 int count;
220 const char *buf = getAttribute(attribName);
221 for (count = 0; buf; count++) {
222 buf = strchr(buf, partSplit);
223 if (buf)
224 buf++;
225 }
226 return count;
227 }
228
229
getAttribute(const char * attribName,int partNum,char partSplit) const230 const char *XMLTag::getAttribute(const char *attribName, int partNum, char partSplit) const {
231
232 if (!parsed)
233 parse();
234
235 StringPairMap::const_iterator it = attributes.find(attribName);
236
237 const char *retVal = 0;
238 if (it != attributes.end())
239 retVal = it->second.c_str();
240
241 if ((retVal) && (partNum > -1))
242 retVal = getPart(retVal, partNum, partSplit);
243
244 return retVal;
245 }
246
247
setAttribute(const char * attribName,const char * attribValue,int partNum,char partSplit)248 const char *XMLTag::setAttribute(const char *attribName, const char *attribValue, int partNum, char partSplit) {
249 if (!parsed)
250 parse();
251
252 SWBuf newVal = "";
253 // set part of an attribute
254 if (partNum > -1) {
255 const char *wholeAttr = getAttribute(attribName);
256 int attrCount = getAttributePartCount(attribName, partSplit);
257 for (int i = 0; i < attrCount; i++) {
258 if (i == partNum) {
259 if (attribValue) {
260 newVal += attribValue;
261 newVal += partSplit;
262 }
263 else {
264 // discard this part per null attribValue
265 }
266 }
267 else {
268 newVal += getPart(wholeAttr, i, partSplit);
269 newVal += partSplit;
270 }
271 }
272 if (newVal.length()) newVal--; // discard the last partSplit
273 attribValue = (!attribValue && !newVal.length()) ? 0 : newVal.c_str();
274 }
275
276 // perform the actual set
277 if (attribValue)
278 attributes[attribName] = attribValue;
279 else attributes.erase(attribName);
280
281 return attribValue;
282 }
283
284
toString() const285 const char *XMLTag::toString() const {
286 SWBuf tag = "<";
287 if (!parsed)
288 parse();
289
290 if (isEndTag())
291 tag.append('/');
292
293 tag.append(getName());
294 for (StringPairMap::iterator it = attributes.begin(); it != attributes.end(); it++) {
295 //tag.appendFormatted(" %s=\"%s\"", it->first.c_str(), it->second.c_str());
296 tag.append(' ');
297 tag.append(it->first.c_str());
298 tag.append((strchr(it->second.c_str(), '\"')) ? "=\'" : "=\"");
299 tag.append(it->second.c_str());
300 tag.append((strchr(it->second.c_str(), '\"'))? '\'' : '\"');
301 }
302
303 if (isEmpty())
304 tag.append('/');
305
306 tag.append('>');
307
308
309 if (buf)
310 delete [] buf;
311 buf = new char [ tag.length() + 1 ];
312 strcpy(buf, tag.c_str());
313
314 return buf;
315 }
316
317
318 // if an eID is provided, then we check to be sure we have an attribute <tag eID="xxx"/> value xxx equiv to what is given us
319 // otherwise, we return if we're a simple XML end </tag>.
isEndTag(const char * eID) const320 bool XMLTag::isEndTag(const char *eID) const {
321 if (eID) {
322 return (SWBuf(eID) == getAttribute("eID"));
323 }
324 return endTag;
325 }
326
327
328 SWORD_NAMESPACE_END
329
330