1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 #include <Inventor/C/XML/element.h>
34 #include "elementp.h"
35 
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif // HAVE_CONFIG_H
39 
40 #include <cstdlib>
41 #include <cstdio>
42 #include <cstring>
43 #include <cassert>
44 
45 #include <Inventor/C/base/string.h>
46 #include <Inventor/lists/SbList.h>
47 
48 #include <Inventor/C/XML/document.h>
49 #include <Inventor/C/XML/attribute.h>
50 #include <Inventor/C/XML/path.h>
51 #include "attributep.h"
52 #include "utils.h"
53 
54 // *************************************************************************
55 
56 struct cc_xml_elt {
57   char * type;
58   char * data;
59   char * cdata;
60   cc_xml_elt * parent;
61   SbList<cc_xml_attr *> attributes;
62   SbList<cc_xml_elt *> children;
63 };
64 
65 // *************************************************************************
66 
67 /*!
68   Creates a new element with no type, no attributes, and no children elements.
69 */
70 
71 cc_xml_elt *
cc_xml_elt_new(void)72 cc_xml_elt_new(void)
73 {
74   cc_xml_elt * elt = new cc_xml_elt;
75   assert(elt);
76   elt->type = NULL;
77   elt->data = NULL;
78   elt->cdata = NULL;
79   elt->parent = NULL;
80   return elt;
81 }
82 
83 /*!
84   Creates a new element with the given type and attributes.
85   The \a attrs argument can be NULL.
86 */
87 
88 cc_xml_elt *
cc_xml_elt_new_from_data(const char * type,cc_xml_attr ** attrs)89 cc_xml_elt_new_from_data(const char * type, cc_xml_attr ** attrs)
90 {
91   cc_xml_elt * elt = cc_xml_elt_new();
92   cc_xml_elt_set_type_x(elt, type);
93   if (attrs) { cc_xml_elt_set_attributes_x(elt, attrs); }
94   return elt;
95 }
96 
97 /*!
98   Returns a clone of the element, including child elements and attributes.
99   Only the parent connection is dropped.
100 */
101 
102 cc_xml_elt *
cc_xml_elt_clone(const cc_xml_elt * elt)103 cc_xml_elt_clone(const cc_xml_elt * elt)
104 {
105   cc_xml_elt * clone = cc_xml_elt_new();
106   if (elt->type) {
107     cc_xml_elt_set_type_x(clone, elt->type);
108   }
109   if (elt->data) {
110     clone->data = cc_xml_strdup(elt->data);
111   }
112   if (elt->cdata) {
113     clone->cdata = cc_xml_strdup(elt->cdata);
114   }
115   int i = 0;
116   for (i = 0; i < elt->attributes.getLength(); ++i) {
117     cc_xml_elt_set_attribute_x(clone, cc_xml_attr_clone(elt->attributes[i]));
118   }
119   for (i = 0; i < elt->children.getLength(); ++i) {
120     cc_xml_elt_add_child_x(clone, cc_xml_elt_clone(elt->children[i]));
121   }
122   return clone;
123 }
124 
125 /*!
126   Frees the given \a elt element, including its attributes and children.
127 */
128 
129 void
cc_xml_elt_delete_x(cc_xml_elt * elt)130 cc_xml_elt_delete_x(cc_xml_elt * elt)
131 {
132   assert(elt);
133   delete [] elt->type;
134   delete [] elt->data;
135   delete [] elt->cdata;
136   if (elt->attributes.getLength() > 0) {
137     const int num = elt->attributes.getLength();
138     for (int i = 0; i < num; ++i) {
139       cc_xml_attr_delete_x(elt->attributes[i]);
140     }
141   }
142   if (elt->children.getLength() > 0) {
143     const int num = elt->children.getLength();
144     for (int i = 0; i < num; ++i) {
145       cc_xml_elt_delete_x(elt->children[i]);
146     }
147   }
148   delete elt;
149 }
150 
151 // *************************************************************************
152 
153 /*!
154   Sets the element type identifier.  The old value will be freed, if any.
155   The \a type argument can be NULL to just clear the old value.
156 */
157 
158 void
cc_xml_elt_set_type_x(cc_xml_elt * elt,const char * type)159 cc_xml_elt_set_type_x(cc_xml_elt * elt, const char * type)
160 {
161   assert(elt);
162   delete [] elt->type;
163   elt->type = NULL;
164   if (type) elt->type = cc_xml_strdup(type);
165 }
166 
167 /*!
168   Returns the element type identifier if one is set, and NULL otherwise.
169 */
170 
171 const char *
cc_xml_elt_get_type(const cc_xml_elt * elt)172 cc_xml_elt_get_type(const cc_xml_elt * elt)
173 {
174   return elt->type;
175 }
176 
177 // FIXME: document behaviour
178 
179 void
cc_xml_elt_set_cdata_x(cc_xml_elt * elt,const char * cdata)180 cc_xml_elt_set_cdata_x(cc_xml_elt * elt, const char * cdata)
181 {
182   assert(elt);
183   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
184     if ( cc_xml_elt_get_num_children(elt) == 0 ) {
185       // cdata child not existing yet
186       cc_xml_elt * child = cc_xml_elt_new();
187       cc_xml_elt_set_type_x(child, COIN_XML_CDATA_TYPE);
188       cc_xml_elt_add_child_x(elt, child);
189       elt = child;
190     } else if ( (cc_xml_elt_get_num_children(elt) == 1) &&
191          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
192       // we really want to manipulate the cdata child, not this element
193       elt = elt->children[0];
194     } else {
195       // FIXME: warn that this doesn't make sense  20021119 larsa
196       // but we can run through anyways
197     }
198   }
199   delete [] elt->cdata;
200   elt->cdata = NULL;
201   if ( cdata ) elt->cdata = cc_xml_strdup(cdata);
202   delete [] elt->data;
203   elt->data = NULL;
204   // Update data to whitespace-stripped cdata
205   if( cdata) {
206     const char * startptr = elt->cdata;
207     const char * endptr = startptr + (strlen(startptr) - 1);
208     while ( *startptr == '\t' || *startptr == '\r' || *startptr == '\n' || *startptr == ' ' ) {
209       startptr++;
210     }
211     // FIXME: avoid whitespace-only cdata from parser, then uncomment assert's.
212     // assert(startptr <= endptr && "just whitespace from the front");
213     while ( endptr > startptr && (*endptr == '\t' || *endptr == '\r' || *endptr == '\n' || *endptr == ' ') ) {
214       endptr--;
215     }
216     //  assert(endptr > startptr && "makes no sense at all");
217     endptr++;
218     if( endptr > startptr) {
219       elt->data = cc_xml_strndup(startptr, static_cast<int>(endptr - startptr));
220     }
221   }
222 }
223 
224 const char *
cc_xml_elt_get_cdata(const cc_xml_elt * elt)225 cc_xml_elt_get_cdata(const cc_xml_elt * elt)
226 {
227   assert(elt);
228   if (strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0) {
229     if ((cc_xml_elt_get_num_children(elt) == 1) &&
230         (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0)) {
231       return elt->children[0]->cdata;
232     }
233   }
234   return elt->cdata;
235 }
236 
237 const char *
cc_xml_elt_get_data(const cc_xml_elt * elt)238 cc_xml_elt_get_data(const cc_xml_elt * elt)
239 {
240   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
241     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
242          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
243       return elt->children[0]->data;
244     }
245   }
246   return elt->data;
247 }
248 
249 // *************************************************************************
250 
251 void
cc_xml_elt_remove_all_attributes_x(cc_xml_elt * elt)252 cc_xml_elt_remove_all_attributes_x(cc_xml_elt * elt)
253 {
254   assert(elt);
255   if (elt->attributes.getLength()) {
256   }
257 }
258 
259 void
cc_xml_elt_set_attribute_x(cc_xml_elt * elt,cc_xml_attr * attr)260 cc_xml_elt_set_attribute_x(cc_xml_elt * elt, cc_xml_attr * attr)
261 {
262   cc_xml_attr * shadowed = cc_xml_elt_get_attribute(elt, cc_xml_attr_get_name(attr));
263   if (shadowed) {
264     cc_xml_attr_set_value_x(shadowed, cc_xml_attr_get_value(attr));
265     // FIXME: free attr, or replace shadowed by attr and free shadowed?
266   } else {
267     elt->attributes.append(attr);
268   }
269 }
270 
271 void
cc_xml_elt_set_attributes_x(cc_xml_elt * elt,cc_xml_attr ** attrs)272 cc_xml_elt_set_attributes_x(cc_xml_elt * elt, cc_xml_attr ** attrs)
273 {
274   for (int c = 0; attrs[c] != NULL; ++c) {
275     cc_xml_elt_set_attribute_x(elt, attrs[c]);
276   }
277 }
278 
279 cc_xml_attr *
cc_xml_elt_get_attribute(const cc_xml_elt * elt,const char * attrname)280 cc_xml_elt_get_attribute(const cc_xml_elt * elt, const char * attrname)
281 {
282   assert(elt);
283   const int num = elt->attributes.getLength();
284   for (int c = 0; c < num; ++c) {
285     if (strcmp(attrname, cc_xml_attr_get_name(elt->attributes[c])) == 0)
286       return elt->attributes[c];
287   }
288   return NULL;
289 }
290 
291 int
cc_xml_elt_get_num_attributes(const cc_xml_elt * elt)292 cc_xml_elt_get_num_attributes(const cc_xml_elt * elt)
293 {
294   return elt->attributes.getLength();
295 }
296 
297 const cc_xml_attr **
cc_xml_elt_get_attributes(const cc_xml_elt * elt)298 cc_xml_elt_get_attributes(const cc_xml_elt * elt)
299 {
300   assert(elt);
301   return const_cast<const cc_xml_attr **>(elt->attributes.getArrayPtr());
302 }
303 
304 // *************************************************************************
305 
306 cc_xml_elt *
cc_xml_elt_get_parent(const cc_xml_elt * elt)307 cc_xml_elt_get_parent(const cc_xml_elt * elt)
308 {
309   assert(elt);
310   return elt->parent;
311 }
312 
313 int
cc_xml_elt_get_num_children(const cc_xml_elt * elt)314 cc_xml_elt_get_num_children(const cc_xml_elt * elt)
315 {
316   assert(elt);
317   return elt->children.getLength();
318 }
319 
320 /*!
321   Returns the number of child elements of the given type.
322 */
323 
324 int
cc_xml_elt_get_num_children_of_type(const cc_xml_elt * elt,const char * type)325 cc_xml_elt_get_num_children_of_type(const cc_xml_elt * elt, const char * type)
326 {
327   assert(elt);
328   int count = 0;
329   const int numchildren = elt->children.getLength();
330   for (int i = 0; i < numchildren; ++i) {
331     if (strcmp(type, cc_xml_elt_get_type(elt->children[i])) == 0) ++count;
332   }
333   return count;
334 }
335 
336 /*!
337   Returns the child element at index \a childidx.
338 
339   The index has to be an existing index, invalid values are considered programming errors.
340 */
341 
342 cc_xml_elt *
cc_xml_elt_get_child(const cc_xml_elt * elt,int childidx)343 cc_xml_elt_get_child(const cc_xml_elt * elt, int childidx)
344 {
345   assert(elt);
346   assert(childidx >= 0);
347   assert(childidx < cc_xml_elt_get_num_children(elt));
348   return elt->children[childidx];
349 }
350 
351 /*!
352   Returns the child index of the child element.
353 
354   Giving a child element that is not a child is considered a programming error.
355 */
356 
357 int
cc_xml_elt_get_child_index(const cc_xml_elt * elt,const cc_xml_elt * child)358 cc_xml_elt_get_child_index(const cc_xml_elt * elt, const cc_xml_elt * child)
359 {
360   assert(elt);
361   const int numchildren = elt->children.getLength();
362   for (int idx = 0; idx < numchildren; ++idx) {
363     if (elt->children[idx] == child) return idx;
364   }
365   assert(!"no such child");
366   return -1;
367 }
368 
369 /*
370   Returns the child element's index when only counting elements of that given type.
371 
372   Giving a child element that is not a child is considered a programming error.
373 */
374 
375 int
cc_xml_elt_get_child_type_index(const cc_xml_elt * elt,const cc_xml_elt * child)376 cc_xml_elt_get_child_type_index(const cc_xml_elt * elt, const cc_xml_elt * child)
377 {
378   assert(elt);
379   const int numchildren = elt->children.getLength();
380   int idx = -1;
381   const char * type = cc_xml_elt_get_type(child);
382   for (int i = 0; i < numchildren; ++i) {
383     if (strcmp(cc_xml_elt_get_type(elt->children[i]), type) == 0) ++idx;
384     if (elt->children[i] == child) return idx;
385   }
386   assert(!"no such child");
387   return -1;
388 }
389 
390 cc_xml_path *
cc_xml_elt_get_path(const cc_xml_elt * elt)391 cc_xml_elt_get_path(const cc_xml_elt * elt)
392 {
393   assert(elt);
394   cc_xml_path * path = cc_xml_path_new();
395   while ( elt != NULL ) {
396     const cc_xml_elt * parent = cc_xml_elt_get_parent(elt);
397     if ( parent != NULL ) {
398       int idx = cc_xml_elt_get_child_type_index(parent, elt);
399       cc_xml_path_prepend_x(path, elt->type, idx);
400     }
401     elt = parent;
402   }
403   return path;
404 }
405 
406 cc_xml_elt *
cc_xml_elt_get_child_of_type(const cc_xml_elt * elt,const char * type,int idx)407 cc_xml_elt_get_child_of_type(const cc_xml_elt * elt, const char * type, int idx)
408 {
409   assert(elt);
410   if ( !elt->children.getLength() ) return NULL;
411   int i;
412   for ( i = 0; i < elt->children.getLength(); i++ ) {
413     if ( strcmp(elt->children[i]->type, type) == 0 ) {
414       if ( idx == 0 ) return elt->children[i];
415       idx -= 1;
416     }
417   }
418   return NULL;
419 }
420 
421 cc_xml_elt *
cc_xml_elt_get_child_of_type_x(cc_xml_elt * elt,const char * type,int idx)422 cc_xml_elt_get_child_of_type_x(cc_xml_elt * elt, const char * type, int idx)
423 {
424   assert(elt);
425   if ( elt->children.getLength() ) {
426     int i;
427     for ( i = 0; i < elt->children.getLength(); i++ ) {
428       if ( strcmp(elt->children[i]->type, type) == 0 ) {
429         if ( idx == 0 ) return elt->children[i];
430         idx -= 1;
431       }
432     }
433   }
434   cc_xml_elt * child = NULL;
435   while ( idx >= 0 ) {
436     child = cc_xml_elt_new();
437     cc_xml_elt_set_type_x(child, type);
438     cc_xml_elt_add_child_x(elt, child);
439     idx -= 1;
440   }
441   return child;
442 }
443 
444 // *************************************************************************
445 
446 void
cc_xml_elt_set_parent_x(cc_xml_elt * elt,cc_xml_elt * parent)447 cc_xml_elt_set_parent_x(cc_xml_elt * elt, cc_xml_elt * parent)
448 {
449   assert(elt);
450   elt->parent = parent;
451 }
452 
453 void
cc_xml_elt_add_child_x(cc_xml_elt * elt,cc_xml_elt * child)454 cc_xml_elt_add_child_x(cc_xml_elt * elt, cc_xml_elt * child)
455 {
456   assert(elt);
457   assert(child);
458   if (child->parent != NULL) {
459     // FIXME: ERROR - element already a child of another element
460     return;
461   }
462 
463   //int numchildren = cc_xml_elt_get_num_children(elt);
464   elt->children.append(child);
465   child->parent = elt;
466 }
467 
468 /*!  This function will not free the child being removed.  Giving a
469   nonexistent child to this function is a programming error and will
470   result in an assert.
471 */
472 
473 void
cc_xml_elt_remove_child_x(cc_xml_elt * elt,cc_xml_elt * child)474 cc_xml_elt_remove_child_x(cc_xml_elt * elt, cc_xml_elt * child)
475 {
476   assert(elt);
477   assert(child);
478   const int numchildren = elt->children.getLength();
479   for (int i = 0; i < numchildren; ++i) {
480     if (elt->children[i] == child) {
481       elt->children.remove(i);
482       child->parent = NULL;
483       return;
484     }
485   }
486   assert(!"no such child");
487 }
488 
489 void
cc_xml_elt_insert_child_x(cc_xml_elt * elt,cc_xml_elt * child,int idx)490 cc_xml_elt_insert_child_x(cc_xml_elt * elt, cc_xml_elt * child, int idx)
491 {
492   assert(elt);
493   assert(child);
494   if (child->parent != NULL) {
495     // FIXME: error, child already a child of another element
496     return;
497   }
498   const int numchildren = elt->children.getLength();
499   assert(idx >= 0 && idx <= numchildren);
500   elt->children.insert(child, idx);
501   child->parent = elt;
502 }
503 
504 int
cc_xml_elt_replace_child_x(cc_xml_elt * elt,cc_xml_elt * oldchild,cc_xml_elt * newchild)505 cc_xml_elt_replace_child_x(cc_xml_elt * elt, cc_xml_elt * oldchild, cc_xml_elt * newchild)
506 {
507   assert(elt);
508   int idx = cc_xml_elt_get_child_index(elt, oldchild);
509   if ( idx == -1 ) return FALSE;
510   cc_xml_elt_remove_child_x(elt, oldchild);
511   cc_xml_elt_insert_child_x(elt, newchild, idx);
512   return TRUE;
513 }
514 
515 // *************************************************************************
516 
517 int
cc_xml_elt_get_boolean(const cc_xml_elt * elt,int * value)518 cc_xml_elt_get_boolean(const cc_xml_elt * elt, int * value)
519 {
520   assert(elt);
521   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
522     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
523          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
524       elt = elt->children[0];
525     } else {
526       // FIXME: warn about structure senselessness
527     }
528   }
529   const char * data = cc_xml_elt_get_data(elt);
530   assert(value != NULL);
531   if ( data == NULL ) return FALSE;
532   if ( cc_xml_strieq(data, "true") ||
533        cc_xml_strieq(data, "on") ||
534        cc_xml_strieq(data, "t") ) {
535     *value = TRUE;
536     return TRUE;
537   }
538   if ( cc_xml_strieq(data, "false") ||
539        cc_xml_strieq(data, "off") ||
540        cc_xml_strieq(data, "f") ) {
541     *value = FALSE;
542     return TRUE;
543   }
544   return FALSE;
545 }
546 
547 int
cc_xml_elt_get_integer(const cc_xml_elt * elt,int * value)548 cc_xml_elt_get_integer(const cc_xml_elt * elt, int * value)
549 {
550   assert(elt);
551   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
552     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
553          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
554       elt = elt->children[0];
555     } else {
556       // FIXME: warn about structure senselessness
557     }
558   }
559   const char * data = cc_xml_elt_get_data(elt);
560   assert(value != NULL);
561   if ( data == NULL ) return FALSE;
562   if ( sscanf(data, "%d", value) == 1 ) return TRUE;
563   return FALSE;
564 }
565 
566 int
cc_xml_elt_get_uint64(const cc_xml_elt * elt,uint64_t * value)567 cc_xml_elt_get_uint64(const cc_xml_elt * elt, uint64_t * value)
568 {
569   assert(elt);
570   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
571     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
572          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
573       elt = elt->children[0];
574     } else {
575       // FIXME: warn about structure senselessness
576     }
577   }
578   const char * data = cc_xml_elt_get_data(elt);
579   assert(value != NULL);
580   if ( data == NULL ) return FALSE;
581   if ( sscanf(data, "%lld", value) == 1 ) return TRUE; // FIXME: unsigned
582   return FALSE;
583 }
584 
585 int
cc_xml_elt_get_int64(const cc_xml_elt * elt,int64_t * value)586 cc_xml_elt_get_int64(const cc_xml_elt * elt, int64_t * value)
587 {
588   assert(elt);
589   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
590     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
591          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
592       elt = elt->children[0];
593     } else {
594       // FIXME: warn about structure senselessness
595     }
596   }
597   const char * data = cc_xml_elt_get_data(elt);
598   assert(value != NULL);
599   if ( data == NULL ) return FALSE;
600   if ( sscanf(data, "%lld", value) == 1 ) return TRUE;
601   return FALSE;
602 }
603 
604 int
cc_xml_elt_get_uint32(const cc_xml_elt * elt,uint32_t * value)605 cc_xml_elt_get_uint32(const cc_xml_elt * elt, uint32_t * value)
606 {
607   assert(elt);
608   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
609     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
610          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
611       elt = elt->children[0];
612     } else {
613       // FIXME: warn about structure senselessness
614     }
615   }
616   const char * data = cc_xml_elt_get_data(elt);
617   assert(value != NULL);
618   if ( data == NULL ) return FALSE;
619   if ( sscanf(data, "%u", value) == 1 ) return TRUE; // FIXME: unsigned
620   return FALSE;
621 }
622 
623 int
cc_xml_elt_get_int32(const cc_xml_elt * elt,int32_t * value)624 cc_xml_elt_get_int32(const cc_xml_elt * elt, int32_t * value)
625 {
626   assert(elt);
627   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
628     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
629          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
630       elt = elt->children[0];
631     } else {
632       // FIXME: warn about structure senselessness
633     }
634   }
635   const char * data = cc_xml_elt_get_data(elt);
636   assert(value != NULL);
637   if ( data == NULL ) return FALSE;
638   if ( sscanf(data, "%u", value) == 1 ) return TRUE;
639   return FALSE;
640 }
641 
642 int
cc_xml_elt_get_float(const cc_xml_elt * elt,float * value)643 cc_xml_elt_get_float(const cc_xml_elt * elt, float * value)
644 {
645   assert(elt);
646   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
647     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
648          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
649       elt = elt->children[0];
650     } else {
651       // FIXME: warn about structure senselessness
652     }
653   }
654   const char * data = cc_xml_elt_get_data(elt);
655   assert(value != NULL);
656   if ( data == NULL ) return FALSE;
657   if ( sscanf(data, "%g", value) == 1 ) return TRUE;
658   return FALSE;
659 }
660 
661 int
cc_xml_elt_get_double(const cc_xml_elt * elt,double * value)662 cc_xml_elt_get_double(const cc_xml_elt * elt, double * value)
663 {
664   assert(elt);
665   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
666     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
667          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
668       elt = elt->children[0];
669     } else {
670       // FIXME: warn about structure senselessness
671     }
672   }
673   const char * data = cc_xml_elt_get_data(elt);
674   assert(value != NULL);
675   if ( data == NULL ) return FALSE;
676   if ( sscanf(data, "%lg", value) == 1 ) return TRUE;
677   return FALSE;
678 }
679 
680 void
cc_xml_elt_set_boolean_x(cc_xml_elt * elt,int value)681 cc_xml_elt_set_boolean_x(cc_xml_elt * elt, int value)
682 {
683   assert(elt);
684   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
685     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
686          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
687       elt = elt->children[0];
688     } else {
689       // FIXME: warn about structure senselessness
690     }
691   }
692   cc_xml_elt_set_cdata_x(elt, value ? "true" : "false");
693 }
694 
695 void
cc_xml_elt_set_integer_x(cc_xml_elt * elt,int value)696 cc_xml_elt_set_integer_x(cc_xml_elt * elt, int value)
697 {
698   assert(elt);
699   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
700     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
701          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
702       elt = elt->children[0];
703     } else {
704       // FIXME: warn about structure senselessness
705     }
706   }
707   cc_string str;
708   cc_string_construct(&str);
709   cc_string_sprintf(&str, "%d", value);
710   cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
711   cc_string_clean(&str);
712 }
713 
714 void
cc_xml_elt_set_uint64_x(cc_xml_elt * elt,uint64_t value)715 cc_xml_elt_set_uint64_x(cc_xml_elt * elt, uint64_t value)
716 {
717   assert(elt);
718   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
719     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
720          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
721       elt = elt->children[0];
722     } else {
723       // FIXME: warn about structure senselessness
724     }
725   }
726   cc_string str;
727   cc_string_construct(&str);
728   cc_string_sprintf(&str, "%lld", value); // FIXME: unsigned
729   cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
730   cc_string_clean(&str);
731 }
732 
733 void
cc_xml_elt_set_int64_x(cc_xml_elt * elt,int64_t value)734 cc_xml_elt_set_int64_x(cc_xml_elt * elt, int64_t value)
735 {
736   assert(elt);
737   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
738     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
739          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
740       elt = elt->children[0];
741     } else {
742       // FIXME: warn about structure senselessness
743     }
744   }
745   cc_string str;
746   cc_string_construct(&str);
747   cc_string_sprintf(&str, "%lld", value);
748   cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
749   cc_string_clean(&str);
750 }
751 
752 void
cc_xml_elt_set_uint32_x(cc_xml_elt * elt,uint32_t value)753 cc_xml_elt_set_uint32_x(cc_xml_elt * elt, uint32_t value)
754 {
755   assert(elt);
756   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
757     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
758          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
759       elt = elt->children[0];
760     } else {
761       // FIXME: warn about structure senselessness
762     }
763   }
764   cc_string str;
765   cc_string_construct(&str);
766   cc_string_sprintf(&str, "%ld", value); // FIXME: unsigned
767   cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
768   cc_string_clean(&str);
769 }
770 
771 void
cc_xml_elt_set_int32_x(cc_xml_elt * elt,int32_t value)772 cc_xml_elt_set_int32_x(cc_xml_elt * elt, int32_t value)
773 {
774   assert(elt);
775   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
776     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
777          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
778       elt = elt->children[0];
779     } else {
780       // FIXME: warn about structure senselessness
781     }
782   }
783   cc_string str;
784   cc_string_construct(&str);
785   cc_string_sprintf(&str, "%ld", value);
786   cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
787   cc_string_clean(&str);
788 }
789 
790 void
cc_xml_elt_set_float_x(cc_xml_elt * elt,float value)791 cc_xml_elt_set_float_x(cc_xml_elt * elt, float value)
792 {
793   assert(elt);
794   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
795     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
796          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
797       elt = elt->children[0];
798     } else {
799       // FIXME: warn about structure senselessness
800     }
801   }
802   cc_string str;
803   cc_string_construct(&str);
804   cc_string_sprintf(&str, "%g", value);
805   cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
806   cc_string_clean(&str);
807 }
808 
809 void
cc_xml_elt_set_double_x(cc_xml_elt * elt,double value)810 cc_xml_elt_set_double_x(cc_xml_elt * elt, double value)
811 {
812   assert(elt);
813   if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
814     if ( (cc_xml_elt_get_num_children(elt) == 1) &&
815          (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
816       elt = elt->children[0];
817     } else {
818       // FIXME: warn about structure senselessness
819     }
820   }
821   cc_string str;
822   cc_string_construct(&str);
823   cc_string_sprintf(&str, "%lf", value);
824   cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
825   cc_string_clean(&str);
826 }
827 
828 // *************************************************************************
829 
830 void
cc_xml_elt_strip_whitespace_x(cc_xml_elt * elt)831 cc_xml_elt_strip_whitespace_x(cc_xml_elt * elt)
832 {
833   // FIXME: update to reflect cdata change - remove function probably
834   assert(elt->type && strcmp(elt->type, COIN_XML_CDATA_TYPE) == 0);
835   assert(0);
836   const char * startptr = elt->data;
837   const char * endptr = startptr + (strlen(startptr) - 1);
838   while ( *startptr ) {
839     switch ( *startptr ) {
840     case '\t':
841     case '\r':
842     case '\n':
843     case ' ':
844       break;
845     default:
846       goto cont1;
847     }
848     startptr++;
849   }
850   assert(0 && "just whitespace from the front");
851 cont1:
852   while ( endptr > startptr ) {
853     switch ( *endptr ) {
854     case '\t':
855     case '\r':
856     case '\n':
857     case ' ':
858       break;
859     default:
860       goto cont2;
861     }
862     endptr--;
863   }
864   assert(0 && "makes no sense at all");
865 cont2:
866   endptr++;
867   if ( (startptr == elt->data) &&
868        (endptr == (startptr + strlen(startptr))) ) return;
869   char * substr = cc_xml_strndup(startptr, static_cast<int>(endptr - startptr));
870   cc_xml_elt_set_cdata_x(elt, substr);
871   delete [] substr;
872 }
873 
874 // *************************************************************************
875 
876 void
cc_xml_elt_dump_to_file(const cc_xml_elt * elt,int indent,FILE * fp)877 cc_xml_elt_dump_to_file(const cc_xml_elt * elt, int indent, FILE * fp)
878 {
879   // todo
880   // - support notation for empty elements and shortcuts
881   // - document-wide settings?  how to propagate...
882   assert( elt && elt->type != NULL );
883   int i;
884   if ( cc_xml_elt_get_num_children(elt) == 1 &&
885        strcmp(COIN_XML_CDATA_TYPE, elt->children[0]->type) == 0 ) {
886     for ( i = 0; i < indent; i++ ) fprintf(fp, " ");
887     if ( elt->children[0]->data != NULL )
888       fprintf(fp, "<%s>%s</%s>\n", elt->type, elt->children[0]->data, elt->type);
889     else
890       fprintf(fp, "<%s/>\n", elt->type);
891   } else if ( strcmp(COIN_XML_CDATA_TYPE, elt->type) != 0 ) {
892     for ( i = 0; i < indent; i++ ) fprintf(fp, " ");
893     fprintf(fp, "<%s>\n", elt->type);
894     const int children = cc_xml_elt_get_num_children(elt);
895     for ( i = 0; i < children; i++ ) {
896       cc_xml_elt * child = cc_xml_elt_get_child(elt, i);
897       cc_xml_elt_dump_to_file(child, indent + 2, fp);
898     }
899     for ( i = 0; i < indent; i++ ) fprintf(fp, " ");
900     fprintf(fp, "</%s>\n", elt->type);
901   } else if ( elt->data != NULL ) {
902     for ( i = 0; i < indent; i++ ) fprintf(fp, " ");
903     fprintf(fp, "%s\n", elt->data);
904   }
905 }
906 
907 // *************************************************************************
908 
909 /*!
910   \fn cc_xml_elt * cc_xml_elt_get_traversal_next(cc_xml_elt * root, cc_xml_elt * here)
911 
912   Utility function for flattening recursive traversals to do-while loops.
913 
914   \ingroup cc_xml_elt
915 */
916 
917 cc_xml_elt *
cc_xml_elt_get_traversal_next(const cc_xml_elt * root,cc_xml_elt * here)918 cc_xml_elt_get_traversal_next(const cc_xml_elt * root, cc_xml_elt * here)
919 {
920   assert(root && here);
921 
922   // first traverse into children - but ignore any "cdata" element...
923   if (cc_xml_elt_get_num_children(here) > 0) {
924     // Check if we can find a child that is not cdata..
925     for (int i = 0; i < cc_xml_elt_get_num_children(here); ++i) {
926       if (strcmp(COIN_XML_CDATA_TYPE, here->children[i]->type) != 0) {
927         return cc_xml_elt_get_child(here, i);
928       }
929     }
930   }
931 
932   // if we're here then 'here' has no children (except possibly cdata).
933   do {
934     cc_xml_elt * parent = cc_xml_elt_get_parent(here);
935     if (parent == NULL) return NULL; // here is the root
936     int idx = cc_xml_elt_get_child_index(parent, here);
937     // if 'here' was the last child then set here as parent..
938     if (idx == (cc_xml_elt_get_num_children(parent) - 1)) {
939       here = parent;
940       // if we're back to root, simply quit..
941       if (here == root) return NULL;
942     } else {
943       // there is more children than 'here' left, find someone thats not "cdata".
944       do {
945           ++idx;
946           here = cc_xml_elt_get_child(parent, idx);
947 
948           // return this element if it's not a "cdata" element.
949           if (strcmp(COIN_XML_CDATA_TYPE, here->type) != 0) return here;
950 
951       } while (idx != (cc_xml_elt_get_num_children(parent) - 1));
952     }
953   } while (TRUE);
954 
955   return NULL;
956 } // cc_xml_elt_get_traversal_next()
957 
958 // *************************************************************************
959 
960 const cc_xml_elt *
cc_xml_elt_find(const cc_xml_elt * root,const cc_xml_path * path)961 cc_xml_elt_find(const cc_xml_elt * root, const cc_xml_path * path)
962 {
963   assert(root && path);
964   cc_xml_elt * elt = const_cast<cc_xml_elt *>(root);
965   while ( (elt != NULL) && (!cc_xml_path_match_p(path, elt)) ) {
966     elt = cc_xml_elt_get_traversal_next(root, elt);
967   }
968   return elt;
969 } // cc_xml_elt_find()
970 
971 const cc_xml_elt *
cc_xml_elt_find_next(const cc_xml_elt * root,cc_xml_elt * from,cc_xml_path * path)972 cc_xml_elt_find_next(const cc_xml_elt * root, cc_xml_elt * from, cc_xml_path * path)
973 {
974   assert(root && from && path);
975   cc_xml_elt * elt = from;
976   do {
977     elt = cc_xml_elt_get_traversal_next(root, elt);
978   } while ( (elt != NULL) && (!cc_xml_path_match_p(path, elt)) );
979   return elt;
980 } // cc_xml_elt_find_next()
981 
982 // *************************************************************************
983 
984 cc_xml_elt *
cc_xml_elt_create_x(cc_xml_elt * from,cc_xml_path * path)985 cc_xml_elt_create_x(cc_xml_elt * from, cc_xml_path * path)
986 {
987   assert(from && path);
988   cc_xml_elt * current = from;
989   const char * type;
990   int idx;
991   int length = cc_xml_path_get_length(path);
992   int i;
993   for ( i = 0; i < length; i++ ) {
994     int lastpos = -1, lastidx = -1;
995     type = cc_xml_path_get_type(path, i);
996     idx = cc_xml_path_get_index(path, i);
997     if ( current->children.getLength() ) {
998       int child;
999       for ( child = 0; child < current->children.getLength(); child++ ) {
1000         if ( strcmp(type, cc_xml_elt_get_type(current->children[child])) == 0 ) {
1001           lastpos = child;
1002           lastidx += 1;
1003           if ( idx == -1 || idx == lastidx ) {
1004             // match - enter this child
1005             goto cont;
1006           }
1007         }
1008       }
1009       // no match - need to create for idx idx
1010       if ( idx == -1 ) idx = lastidx + 1;
1011       while ( lastidx < idx ) {
1012         // insert empty children after lastpos - enter last one
1013         lastidx += 1;
1014         lastpos += 1;
1015         cc_xml_elt * elt = cc_xml_elt_new();
1016         cc_xml_elt_set_type_x(elt, type);
1017         if ( lastpos == 0 ) lastpos = cc_xml_elt_get_num_children(current);
1018         cc_xml_elt_insert_child_x(current, elt, lastpos);
1019       }
1020     } else {
1021       // no children - need to create them
1022       lastpos = -1;
1023       lastidx = -1;
1024       if ( idx == -1 ) idx = 0;
1025       while ( lastidx < idx ) {
1026         cc_xml_elt * newelt = cc_xml_elt_new();
1027         cc_xml_elt_set_type_x(newelt, type);
1028         cc_xml_elt_add_child_x(current, newelt);
1029         lastidx += 1;
1030         lastpos += 1;
1031       }
1032     }
1033 cont:
1034     current = current->children[lastpos];
1035   }
1036   return current;
1037 } // cc_xml_elt_create_x()
1038 
1039 // *************************************************************************
1040 
1041 size_t
cc_xml_elt_calculate_size(const cc_xml_elt * elt,int indent,int indentincrement)1042 cc_xml_elt_calculate_size(const cc_xml_elt * elt, int indent, int indentincrement)
1043 {
1044   assert(elt);
1045   assert(elt->type);
1046   assert(indent >= 0);
1047 
1048   size_t bytes = 0;
1049 
1050 // macro to advance a number of bytes
1051 #define ADVANCE_NUM_BYTES(num) \
1052   do { bytes += num; } while (0)
1053 
1054 // macro to advance a number of blanks (indentation)
1055 #define ADVANCE_NUM_SPACES(num) \
1056   do { bytes += num; } while (0)
1057 
1058 // macro to increment bytecount for string literal
1059 #define ADVANCE_STRING_LITERAL(str) \
1060   do { static const char strobj[] = str; bytes += (sizeof(strobj) - 1); } while (0)
1061 
1062 // macro to increment bytecount for run-time string
1063 #define ADVANCE_STRING(str) \
1064   do { bytes += strlen(str); } while (0)
1065 
1066   // duplicate block - see cc_xml_elt_write_to_buffer()
1067   if (elt->type && strcmp(elt->type, COIN_XML_CDATA_TYPE) == 0) {
1068     // this is a leaf element character data container
1069     ADVANCE_STRING(elt->cdata);
1070   } else {
1071     ADVANCE_NUM_SPACES(indent);
1072     ADVANCE_STRING_LITERAL("<");
1073     ADVANCE_STRING(elt->type);
1074 
1075     int c;
1076     const int numattributes = elt->attributes.getLength();
1077     for (c = 0; c < numattributes; ++c) {
1078       ADVANCE_STRING_LITERAL(" ");
1079       ADVANCE_NUM_BYTES(cc_xml_attr_calculate_size(elt->attributes[c]));
1080     }
1081 
1082     const int numchildren = elt->children.getLength();
1083     if (numchildren == 0) { // close element directly
1084       ADVANCE_STRING_LITERAL("/>\n");
1085     } else if ((numchildren == 1) &&
1086                (strcmp(cc_xml_elt_get_type(elt->children[0]), COIN_XML_CDATA_TYPE) == 0)) {
1087       ADVANCE_STRING_LITERAL(">");
1088       ADVANCE_STRING(cc_xml_elt_get_cdata(elt->children[0]));
1089       ADVANCE_STRING_LITERAL("</");
1090       ADVANCE_STRING(elt->type);
1091       ADVANCE_STRING_LITERAL(">\n");
1092     } else {
1093       ADVANCE_STRING_LITERAL(">\n");
1094       for (c = 0; c < numchildren; ++c) {
1095         ADVANCE_NUM_BYTES(cc_xml_elt_calculate_size(elt->children[c], indent + indentincrement, indentincrement));
1096       }
1097       ADVANCE_NUM_SPACES(indent);
1098       ADVANCE_STRING_LITERAL("</");
1099       ADVANCE_STRING(elt->type);
1100       ADVANCE_STRING_LITERAL(">\n");
1101     }
1102   }
1103 
1104 #undef ADVANCE_NUM_BYTES
1105 #undef ADVANCE_NUM_SPACES
1106 #undef ADVANCE_STRING
1107 #undef ADVANCE_STRING_LITERAL
1108 
1109   return bytes;
1110 }
1111 
1112 size_t
cc_xml_elt_write_to_buffer(const cc_xml_elt * elt,char * buffer,size_t bufsize,int indent,int indentincrement)1113 cc_xml_elt_write_to_buffer(const cc_xml_elt * elt, char * buffer, size_t bufsize, int indent, int indentincrement)
1114 {
1115   assert(elt);
1116   assert(elt->type);
1117   assert(indent >= 0);
1118 
1119   const size_t assumed = cc_xml_elt_calculate_size(elt, indent, indentincrement);
1120 
1121   size_t bytes = 0;
1122   char * hereptr = buffer;
1123   size_t bytesleft = bufsize;
1124 
1125 // macro to advance buffer pointer and decrement bytesleft count
1126 #define ADVANCE_NUM_BYTES(len)          \
1127   do { const int length = (len);        \
1128        bytes += length;                 \
1129        hereptr += length;               \
1130        bytesleft -= length; } while (0)
1131 
1132 // macro to copy in a string literal and advance pointers
1133 #define ADVANCE_STRING_LITERAL(str)                \
1134   do { static const char strobj[] = str;           \
1135        const int strlength = (sizeof(strobj) - 1); \
1136        strncpy(hereptr, strobj, strlength);        \
1137        ADVANCE_NUM_BYTES(strlength); } while (0)
1138 
1139 // macro to copy in a run-time string and advance pointers
1140 #define ADVANCE_STRING(str)                                 \
1141   do { const int strlength = static_cast<int>(strlen(str)); \
1142        strncpy(hereptr, str, strlength);                    \
1143        ADVANCE_NUM_BYTES(strlength); } while (0)
1144 
1145 // macro to advance a number of blanks (indentation)
1146 #define ADVANCE_NUM_SPACES(num)                  \
1147   do { const int strlength = (num);              \
1148        memset(hereptr, ' ', strlength);          \
1149        ADVANCE_NUM_BYTES(strlength); } while (0)
1150 
1151   // ***********************************************************************
1152   // almost duplicate block - see cc_xml_elt_calculate_size()
1153   if (elt->type && strcmp(elt->type, COIN_XML_CDATA_TYPE) == 0) {
1154     // this is a leaf element character data container
1155     ADVANCE_STRING(elt->cdata);
1156   } else {
1157     ADVANCE_NUM_SPACES(indent);
1158     ADVANCE_STRING_LITERAL("<");
1159     ADVANCE_STRING(elt->type);
1160 
1161     int c;
1162     const int numattributes = elt->attributes.getLength();
1163     for (c = 0; c < numattributes; ++c) {
1164       ADVANCE_STRING_LITERAL(" ");
1165       ADVANCE_NUM_BYTES(cc_xml_attr_write_to_buffer(elt->attributes[c], hereptr, bytesleft));
1166     }
1167 
1168     const int numchildren = elt->children.getLength();
1169     if (numchildren == 0) { // close element directly
1170       ADVANCE_STRING_LITERAL("/>\n");
1171     } else if ((numchildren == 1) &&
1172                (strcmp(cc_xml_elt_get_type(elt->children[0]), COIN_XML_CDATA_TYPE) == 0)) {
1173       ADVANCE_STRING_LITERAL(">");
1174       ADVANCE_STRING(cc_xml_elt_get_cdata(elt->children[0]));
1175       ADVANCE_STRING_LITERAL("</");
1176       ADVANCE_STRING(elt->type);
1177       ADVANCE_STRING_LITERAL(">\n");
1178     } else {
1179       ADVANCE_STRING_LITERAL(">\n");
1180       for (c = 0; c < numchildren; ++c) {
1181         ADVANCE_NUM_BYTES(cc_xml_elt_write_to_buffer(elt->children[c], hereptr, bytesleft, indent + indentincrement, indentincrement));
1182       }
1183       ADVANCE_NUM_SPACES(indent);
1184       ADVANCE_STRING_LITERAL("</");
1185       ADVANCE_STRING(elt->type);
1186       ADVANCE_STRING_LITERAL(">\n");
1187     }
1188   }
1189 
1190 #undef ADVANCE_NUM_BYTES
1191 #undef ADVANCE_NUM_SPACES
1192 #undef ADVANCE_STRING
1193 #undef ADVANCE_STRING_LITERAL
1194 
1195   assert(bytes == assumed);
1196   return bytes;
1197 }
1198 
1199 // *************************************************************************
1200