The SWORD Project  1.9.0.svnversion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
utilxml.cpp
Go to the documentation of this file.
1 /******************************************************************************
2  *
3  * utilxml.cpp - Implementaion of utility classes to handle
4  * XML processing
5  *
6  * $Id: utilxml.cpp 3719 2020-04-19 03:19:53Z 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 
30 
31 
32 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 
118 XMLTag::XMLTag(const char *tagString) {
119 
120  name = 0;
121  buf = 0;
122  setText(tagString);
123 }
124 
125 
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 
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 
181  if (buf)
182  delete [] buf;
183  if (name)
184  delete [] name;
185 }
186 
187 
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 
201 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 
218 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 
230 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 
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 
285 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 
295  if (!isEndTag()) {
296  for (StringPairMap::iterator it = attributes.begin(); it != attributes.end(); it++) {
297  //tag.appendFormatted(" %s=\"%s\"", it->first.c_str(), it->second.c_str());
298  tag.append(' ');
299  tag.append(it->first.c_str());
300  tag.append((strchr(it->second.c_str(), '\"')) ? "=\'" : "=\"");
301  tag.append(it->second.c_str());
302  tag.append((strchr(it->second.c_str(), '\"'))? '\'' : '\"');
303  }
304  }
305 
306  if (isEmpty())
307  tag.append('/');
308 
309  tag.append('>');
310 
311 
312  if (buf)
313  delete [] buf;
314  buf = new char [ tag.length() + 1 ];
315  strcpy(buf, tag.c_str());
316 
317  return buf;
318 }
319 
320 
321 // 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
322 // otherwise, we return if we're a simple XML end </tag>.
323 bool XMLTag::isEndTag(const char *eID) const {
324  if (eID) {
325  return (SWBuf(eID) == getAttribute("eID"));
326  }
327  return endTag;
328 }
329 
330 
332 
#define SWORD_NAMESPACE_START
Definition: defs.h:39
const char * getPart(const char *buf, int partNum=0, char partSplit= '|') const
Definition: utilxml.cpp:201
Definition: swbuf.h:47
unsigned long length() const
Definition: swbuf.h:197
const char * setAttribute(const char *attribName, const char *attribValue, int partNum=-1, char partSplit= '|')
Definition: utilxml.cpp:248
bool parsed
Definition: utilxml.h:43
const char * getName() const
Definition: utilxml.h:58
Definition: utilxml.h:38
XMLTag(const char *tagString=0)
Definition: utilxml.cpp:118
const char * toString() const
Definition: utilxml.cpp:285
StringPairMap attributes
Definition: utilxml.h:46
bool isEmpty() const
Definition: utilxml.h:60
SWORD_NAMESPACE_START char * stdstr(char **ipstr, const char *istr, unsigned int memPadFactor=1)
Definition: utilstr.h:44
const StringList getAttributeNames() const
Definition: utilxml.cpp:188
char * name
Definition: utilxml.h:42
const char * c_str() const
Definition: swbuf.h:158
std::list< SWBuf > StringList
Definition: swmodule.cpp:91
SWBuf & append(const char *str, long max=-1)
Definition: swbuf.h:274
void setText(const char *tagString)
Definition: utilxml.cpp:143
SWBuf junkBuf
Definition: utilxml.h:47
const char * getAttribute(const char *attribName, int partNum=-1, char partSplit= '|') const
Definition: utilxml.cpp:230
bool empty
Definition: utilxml.h:44
char * buf
Definition: utilxml.h:41
~XMLTag()
Definition: utilxml.cpp:180
bool isEndTag(const char *eID=0) const
Definition: utilxml.cpp:323
#define SWORD_NAMESPACE_END
Definition: defs.h:40
void setSize(unsigned long len)
Definition: swbuf.h:255
void parse() const
Definition: utilxml.cpp:32
bool endTag
Definition: utilxml.h:45
int getAttributePartCount(const char *attribName, char partSplit= '|') const
Definition: utilxml.cpp:218