1 /***************************************************************************
2     begin       : Sat Apr 18 2018
3     copyright   : (C) 2020 by Martin Preuss
4     email       : martin@libchipcard.de
5 
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,                 *
21  *   MA  02111-1307  USA                                                   *
22  *                                                                         *
23  ***************************************************************************/
24 
25 
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29 
30 
31 
32 #include "xmlcmd_lxml_p.h"
33 
34 #include <gwenhywfar/debug.h>
35 
36 
37 #include <ctype.h>
38 
39 
40 GWEN_INHERIT(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML)
41 
42 
43 
44 /* ------------------------------------------------------------------------------------------------
45  * forward declarations
46  * ------------------------------------------------------------------------------------------------
47  */
48 
49 static void GWENHYWFAR_CB _freeData(void *bp, void *p);
50 static void *_handleXmlPath(const char *entry, void *data, int idx, uint32_t flags);
51 
52 GWEN_XMLCMD_LXML_TWOSTRINGS *_twoStrings_new();
53 void _twoStrings_free(GWEN_XMLCMD_LXML_TWOSTRINGS *ts);
54 GWEN_XMLCMD_LXML_TWOSTRINGS *_twoStrings_fromString(const char *source, uint8_t delimiter);
55 
56 
57 
58 
59 /* ------------------------------------------------------------------------------------------------
60  * implementations
61  * ------------------------------------------------------------------------------------------------
62  */
63 
64 
GWEN_XmlCommanderLibXml_new(xmlNodePtr documentRoot,GWEN_DB_NODE * dbRoot)65 GWEN_XMLCOMMANDER *GWEN_XmlCommanderLibXml_new(xmlNodePtr documentRoot, GWEN_DB_NODE *dbRoot)
66 {
67   GWEN_XMLCOMMANDER *cmd;
68   GWEN_XMLCMD_LXML *xcmd;
69 
70   cmd=GWEN_XmlCommander_new();
71   GWEN_NEW_OBJECT(GWEN_XMLCMD_LXML, xcmd);
72   GWEN_INHERIT_SETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd, xcmd, _freeData);
73 
74   xcmd->docRoot=documentRoot;
75   xcmd->dbRoot=dbRoot;
76   xcmd->tempDbRoot=GWEN_DB_Group_new("dbTempRoot");
77 
78   xcmd->currentDbGroup=xcmd->dbRoot;
79   xcmd->currentTempDbGroup=xcmd->tempDbRoot;
80   xcmd->currentDocNode=documentRoot;
81 
82   return cmd;
83 }
84 
85 
86 
_freeData(GWEN_UNUSED void * bp,void * p)87 void _freeData(GWEN_UNUSED void *bp, void *p)
88 {
89   GWEN_XMLCMD_LXML *xcmd;
90 
91   xcmd=(GWEN_XMLCMD_LXML*) p;
92 
93   GWEN_DB_Group_free(xcmd->tempDbRoot);
94   GWEN_FREE_OBJECT(xcmd);
95 }
96 
97 
98 
GWEN_XmlCommanderLibXml_GetDocRoot(const GWEN_XMLCOMMANDER * cmd)99 xmlNodePtr GWEN_XmlCommanderLibXml_GetDocRoot(const GWEN_XMLCOMMANDER *cmd)
100 {
101   GWEN_XMLCMD_LXML *xcmd;
102 
103   assert(cmd);
104   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
105   assert(xcmd);
106 
107   return xcmd->docRoot;
108 }
109 
110 
111 
GWEN_XmlCommanderLibXml_GetCurrentDocNode(const GWEN_XMLCOMMANDER * cmd)112 xmlNodePtr GWEN_XmlCommanderLibXml_GetCurrentDocNode(const GWEN_XMLCOMMANDER *cmd)
113 {
114   GWEN_XMLCMD_LXML *xcmd;
115 
116   assert(cmd);
117   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
118   assert(xcmd);
119 
120   return xcmd->currentDocNode;
121 }
122 
123 
124 
GWEN_XmlCommanderLibXml_SetCurrentDocNode(GWEN_XMLCOMMANDER * cmd,xmlNodePtr n)125 void GWEN_XmlCommanderLibXml_SetCurrentDocNode(GWEN_XMLCOMMANDER *cmd, xmlNodePtr n)
126 {
127   GWEN_XMLCMD_LXML *xcmd;
128 
129   assert(cmd);
130   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
131   assert(xcmd);
132 
133   xcmd->currentDocNode=n;
134 }
135 
136 
137 
GWEN_XmlCommanderLibXml_GetDbRoot(const GWEN_XMLCOMMANDER * cmd)138 GWEN_DB_NODE *GWEN_XmlCommanderLibXml_GetDbRoot(const GWEN_XMLCOMMANDER *cmd)
139 {
140   GWEN_XMLCMD_LXML *xcmd;
141 
142   assert(cmd);
143   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
144   assert(xcmd);
145 
146   return xcmd->dbRoot;
147 }
148 
149 
150 
GWEN_XmlCommanderLibXml_GetCurrentDbGroup(const GWEN_XMLCOMMANDER * cmd)151 GWEN_DB_NODE *GWEN_XmlCommanderLibXml_GetCurrentDbGroup(const GWEN_XMLCOMMANDER *cmd)
152 {
153   GWEN_XMLCMD_LXML *xcmd;
154 
155   assert(cmd);
156   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
157   assert(xcmd);
158 
159   return xcmd->currentDbGroup;
160 }
161 
162 
163 
GWEN_XmlCommanderLibXml_SetCurrentDbGroup(GWEN_XMLCOMMANDER * cmd,GWEN_DB_NODE * db)164 void GWEN_XmlCommanderLibXml_SetCurrentDbGroup(GWEN_XMLCOMMANDER *cmd, GWEN_DB_NODE *db)
165 {
166   GWEN_XMLCMD_LXML *xcmd;
167 
168   assert(cmd);
169   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
170   assert(xcmd);
171 
172   xcmd->currentDbGroup=db;
173 }
174 
175 
176 
GWEN_XmlCommanderLibXml_GetTempDbRoot(const GWEN_XMLCOMMANDER * cmd)177 GWEN_DB_NODE *GWEN_XmlCommanderLibXml_GetTempDbRoot(const GWEN_XMLCOMMANDER *cmd)
178 {
179   GWEN_XMLCMD_LXML *xcmd;
180 
181   assert(cmd);
182   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
183   assert(xcmd);
184 
185   return xcmd->tempDbRoot;
186 }
187 
188 
189 
GWEN_XmlCommanderLibXml_GetCurrentTempDbGroup(const GWEN_XMLCOMMANDER * cmd)190 GWEN_DB_NODE *GWEN_XmlCommanderLibXml_GetCurrentTempDbGroup(const GWEN_XMLCOMMANDER *cmd)
191 {
192   GWEN_XMLCMD_LXML *xcmd;
193 
194   assert(cmd);
195   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
196   assert(xcmd);
197 
198   return xcmd->currentTempDbGroup;
199 }
200 
201 
202 
GWEN_XmlCommanderLibXml_SetCurrentTempDbGroup(GWEN_XMLCOMMANDER * cmd,GWEN_DB_NODE * db)203 void GWEN_XmlCommanderLibXml_SetCurrentTempDbGroup(GWEN_XMLCOMMANDER *cmd, GWEN_DB_NODE *db)
204 {
205   GWEN_XMLCMD_LXML *xcmd;
206 
207   assert(cmd);
208   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
209   assert(xcmd);
210 
211   xcmd->currentTempDbGroup=db;
212 }
213 
214 
215 
GWEN_XmlCommanderLibXml_EnterDocNode(GWEN_XMLCOMMANDER * cmd,xmlNodePtr xNode)216 void GWEN_XmlCommanderLibXml_EnterDocNode(GWEN_XMLCOMMANDER *cmd, xmlNodePtr xNode)
217 {
218   GWEN_XMLCMD_LXML *xcmd;
219 
220   assert(cmd);
221   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
222   assert(xcmd);
223 
224   assert(xNode);
225 
226   if (xcmd->currentStackPos<GWEN_XMLCMD_LXML_PATH_MAXDEPTH) {
227     xcmd->xmlNodeStack[xcmd->currentStackPos]=xNode;
228     xcmd->currentStackPos++;
229     xcmd->currentDocNode=xNode;
230   }
231   else {
232     DBG_ERROR(GWEN_LOGDOMAIN, "Stack full, SNH!");
233     abort();
234   }
235 }
236 
237 
238 
GWEN_XmlCommanderLibXml_LeaveDocNode(GWEN_XMLCOMMANDER * cmd)239 void GWEN_XmlCommanderLibXml_LeaveDocNode(GWEN_XMLCOMMANDER *cmd)
240 {
241   GWEN_XMLCMD_LXML *xcmd;
242   xmlNodePtr xNode;
243 
244   assert(cmd);
245   xcmd=GWEN_INHERIT_GETDATA(GWEN_XMLCOMMANDER, GWEN_XMLCMD_LXML, cmd);
246   assert(xcmd);
247 
248   if (xcmd->currentStackPos>0) {
249     xNode=xcmd->xmlNodeStack[xcmd->currentStackPos-1];
250     if (xNode==NULL) {
251       DBG_ERROR(GWEN_LOGDOMAIN, "Nothing on stack");
252       assert(xNode);
253     }
254     xcmd->currentStackPos--;
255     xcmd->currentDocNode=xNode;
256   }
257 }
258 
259 
260 
261 
262 
GWEN_XmlCommanderLibXml_GetXmlNode(xmlNodePtr n,const char * path,uint32_t flags)263 xmlNodePtr GWEN_XmlCommanderLibXml_GetXmlNode(xmlNodePtr n, const char *path, uint32_t flags)
264 {
265   return (xmlNodePtr)GWEN_Path_HandleWithIdx(path, n, flags, _handleXmlPath);
266 }
267 
268 
269 
GWEN_XmlCommanderLibXml_FindFirstElement(xmlNodePtr parent,const char * elemName)270 xmlNodePtr GWEN_XmlCommanderLibXml_FindFirstElement(xmlNodePtr parent, const char *elemName)
271 {
272   xmlNodePtr n;
273 
274   n=parent->children;
275   while (n) {
276     if (n->type==XML_ELEMENT_NODE) {
277       if (n->name && strcmp((const char *)n->name, elemName)==0) {
278         return n;
279       }
280     }
281     n=n->next;
282   } /* while */
283 
284   return NULL;
285 }
286 
287 
288 
GWEN_XmlCommanderLibXml_FindNextElement(xmlNodePtr elem,const char * elemName)289 xmlNodePtr GWEN_XmlCommanderLibXml_FindNextElement(xmlNodePtr elem, const char *elemName)
290 {
291   xmlNodePtr n;
292 
293   assert(elem);
294   n=elem->next;
295   while (n) {
296     if (n->type==XML_ELEMENT_NODE) {
297       if (n->name && strcmp((const char *)n->name, elemName)==0) {
298         return n;
299       }
300     }
301     n=n->next;
302   } /* while */
303 
304   return NULL;
305 }
306 
307 
308 
GWEN_XmlCommanderLibXml_SetXmlCharValue(xmlNodePtr n,const char * path,const char * value)309 int GWEN_XmlCommanderLibXml_SetXmlCharValue(xmlNodePtr n, const char *path, const char *value)
310 {
311   xmlNodePtr node;
312 
313   node=GWEN_XmlCommanderLibXml_GetXmlNode(n, path, 0);
314   if (!node) {
315     DBG_INFO(GWEN_LOGDOMAIN, "here");
316     return GWEN_ERROR_GENERIC;
317   }
318 
319   xmlNodeSetContent(node, BAD_CAST value);
320 
321   return 0;
322 }
323 
324 
325 
GWEN_XmlCommanderLibXml_GetXmlCharValue(xmlNodePtr n,const char * path,const char * defValue)326 const char *GWEN_XmlCommanderLibXml_GetXmlCharValue(xmlNodePtr n, const char *path, const char *defValue)
327 {
328   xmlNodePtr node;
329 
330   node=GWEN_XmlCommanderLibXml_GetXmlNode(n, path, GWEN_PATH_FLAGS_NAMEMUSTEXIST);
331   if (!node) {
332     DBG_INFO(GWEN_LOGDOMAIN, "path [%s] not found", path);
333     return defValue;
334   }
335   node=node->children;
336   if (node==0)
337     return defValue;
338 
339   while (node) {
340     if (node->type==XML_TEXT_NODE) {
341       return (const char *)node->content;
342     }
343     node=node->next;
344   }
345 
346   return defValue;
347 }
348 
349 
350 
GWEN_XmlCommanderLibXml_SetIntValue(xmlNodePtr n,const char * path,int value)351 int GWEN_XmlCommanderLibXml_SetIntValue(xmlNodePtr n, const char *path, int value)
352 {
353   char numbuf[32];
354 
355   snprintf(numbuf, sizeof(numbuf)-1, "%d", value);
356   numbuf[sizeof(numbuf)-1]=0;
357   return GWEN_XmlCommanderLibXml_SetXmlCharValue(n, path, numbuf);
358 }
359 
360 
361 
GWEN_XmlCommanderLibXml_GetIntValue(xmlNodePtr n,const char * path,int defValue)362 int GWEN_XmlCommanderLibXml_GetIntValue(xmlNodePtr n, const char *path, int defValue)
363 {
364   const char *s;
365   int i;
366 
367   s=GWEN_XmlCommanderLibXml_GetXmlCharValue(n, path, NULL);
368   if (s==NULL)
369     return defValue;
370   if (1!=sscanf(s, "%i", &i))
371     return defValue;
372   return i;
373 }
374 
375 
376 
GWEN_XmlCommanderLibXml_GetXmlCharValueByPath(xmlNodePtr elem,const char * path,const char * defValue)377 const char *GWEN_XmlCommanderLibXml_GetXmlCharValueByPath(xmlNodePtr elem, const char *path, const char *defValue)
378 {
379   GWEN_XMLCMD_LXML_TWOSTRINGS *tsLevel1;
380 
381   tsLevel1=_twoStrings_fromString(path, '@');
382   if (tsLevel1->string2) {
383     xmlNodePtr n;
384 
385     /* really two strings, so string1 is node, string2 is property name */
386     n=GWEN_XmlCommanderLibXml_GetXmlNode(elem, tsLevel1->string1, GWEN_PATH_FLAGS_PATHMUSTEXIST);
387     if (n) {
388       GWEN_XMLCMD_LXML_TWOSTRINGS *tsLevel2;
389       const char *result=NULL;
390 
391       tsLevel2=_twoStrings_fromString(tsLevel1->string2, ':');
392       if (tsLevel2->string2) {
393         /* really two strings, so string1 is namespace, string2 is property name */
394         result=(const char*) xmlGetNsProp(n, BAD_CAST tsLevel2->string2, BAD_CAST tsLevel2->string1);
395       }
396       else {
397         /* only one string, so no namespace given */
398         result=(const char*) xmlGetNoNsProp(n, BAD_CAST tsLevel2->string1);
399       }
400       if (result) {
401         _twoStrings_free(tsLevel2);
402         _twoStrings_free(tsLevel1);
403         return result;
404       } /* else fall-through */
405       _twoStrings_free(tsLevel2);
406     } /* else fall-through */
407   }
408   else {
409     xmlNodePtr n;
410 
411     /* only one string, so string1 is node */
412     n=GWEN_XmlCommanderLibXml_GetXmlNode(elem, tsLevel1->string1, GWEN_PATH_FLAGS_PATHMUSTEXIST);
413     if (n) {
414       n=n->children;
415       while (n) {
416         if (n->type==XML_TEXT_NODE) {
417           _twoStrings_free(tsLevel1);
418           return (const char *)n->content;
419         }
420         n=n->next;
421       }
422     }
423   }
424   _twoStrings_free(tsLevel1);
425 
426 
427   DBG_INFO(GWEN_LOGDOMAIN, "path [%s] not found", path);
428   return defValue;
429 }
430 
431 
432 
GWEN_XmlCommanderLibXml_SetXmlCharValueByPath(xmlNodePtr elem,const char * path,const char * value)433 void GWEN_XmlCommanderLibXml_SetXmlCharValueByPath(xmlNodePtr elem, const char *path, const char *value)
434 {
435   GWEN_XMLCMD_LXML_TWOSTRINGS *tsLevel1;
436 
437   tsLevel1=_twoStrings_fromString(path, '@');
438   if (tsLevel1->string2) {
439     xmlNodePtr n;
440 
441     /* really two strings, so string1 is node, string2 is property name */
442     n=GWEN_XmlCommanderLibXml_GetXmlNode(elem, tsLevel1->string1, 0);
443     if (n) {
444       GWEN_XMLCMD_LXML_TWOSTRINGS *tsLevel2;
445 
446       tsLevel2=_twoStrings_fromString(tsLevel1->string2, ':');
447       if (tsLevel2->string2) {
448         xmlNsPtr nameSpace;
449 
450         /* really two strings, so string1 is namespace, string2 is property name */
451         nameSpace=xmlSearchNs(n->doc, n, BAD_CAST tsLevel2->string1);
452         xmlNewNsProp(n, nameSpace, BAD_CAST tsLevel2->string2, BAD_CAST value);
453       }
454       else {
455         /* only one string, so no namespace given */
456         xmlNewProp(n, BAD_CAST tsLevel2->string1, BAD_CAST value);
457       }
458       _twoStrings_free(tsLevel2);
459     } /* else fall-through */
460   }
461   else {
462     xmlNodePtr n;
463 
464     /* only one string, so string1 is node */
465     n=GWEN_XmlCommanderLibXml_GetXmlNode(elem, tsLevel1->string1, 0);
466     if (n)
467       xmlNodeSetContent(n, BAD_CAST value);
468   }
469   _twoStrings_free(tsLevel1);
470 }
471 
472 
473 
474 
475 
476 
_handleXmlPath(const char * entry,void * data,int idx,uint32_t flags)477 void *_handleXmlPath(const char *entry, void *data, int idx, uint32_t flags)
478 {
479   xmlNodePtr n;
480   xmlNodePtr nn;
481   int i;
482   xmlNsPtr nameSpace=NULL;
483   const char *p;
484   const char *name;
485 
486   n=(xmlNodePtr)data;
487 
488   name=entry;
489   p=strchr(entry, ':');
490   if (p) {
491     char prefix[32];
492     int plen;
493 
494     plen=p-entry;
495     if (plen) {
496       if (plen>=sizeof(prefix)) {
497         DBG_ERROR(GWEN_LOGDOMAIN, "Prefix too long (%d>%d)", (int)plen, (int)sizeof(prefix));
498         return 0;
499       }
500       strncpy(prefix, entry, plen);
501       prefix[plen]=0;
502       nameSpace=xmlSearchNs(n->doc, n, BAD_CAST prefix);
503       if (!nameSpace) {
504         DBG_ERROR(GWEN_LOGDOMAIN, "Namespace \"%s\" not found", prefix);
505         return 0;
506       }
507     }
508     name=p+1;
509   }
510 
511   /* check whether we are allowed to simply create the node */
512   if (
513     ((flags & GWEN_PATH_FLAGS_LAST) &&
514      (((flags & GWEN_PATH_FLAGS_VARIABLE) &&
515        (flags & GWEN_PATH_FLAGS_CREATE_VAR)) ||
516       (!(flags & GWEN_PATH_FLAGS_VARIABLE) &&
517        (flags & GWEN_PATH_FLAGS_CREATE_GROUP)))
518     ) ||
519     (
520       !(flags & GWEN_PATH_FLAGS_LAST) &&
521       (flags & GWEN_PATH_FLAGS_PATHCREATE))
522   ) {
523     /* simply create the new variable/group */
524     if (idx!=0) {
525       DBG_ERROR(GWEN_LOGDOMAIN, "Index is not 0, not creating %s[%d]", entry, idx);
526       return 0;
527     }
528     DBG_VERBOUS(GWEN_LOGDOMAIN, "Unconditionally creating entry \"%s\"", entry);
529     nn=xmlNewChild(n, nameSpace, BAD_CAST name, NULL);
530     return nn;
531   }
532 
533   /* find the node */
534 
535   nn=n->children;
536   i=idx;
537   while (nn) {
538     if (nn->type==XML_ELEMENT_NODE) {
539       if (nn->name && strcmp((const char *)nn->name, name)==0) {
540         if (i--==0)
541           break;
542       }
543     }
544     nn=nn->next;
545   } /* while */
546 
547   if (!nn) {
548     /* node not found, check, if we are allowed to create it */
549     if (
550       (!(flags & GWEN_PATH_FLAGS_LAST) &&
551        (flags & GWEN_PATH_FLAGS_PATHMUSTEXIST)) ||
552       (flags & GWEN_PATH_FLAGS_NAMEMUSTEXIST)
553     ) {
554       DBG_VERBOUS(GWEN_LOGDOMAIN, "Entry \"%s\" does not exist", entry);
555       return 0;
556     }
557     /* create the new variable/group */
558     if (idx!=0) {
559       DBG_INFO(GWEN_LOGDOMAIN, "Index is not 0, not creating %s[%d]",
560                entry, idx);
561       return 0;
562     }
563     DBG_VERBOUS(GWEN_LOGDOMAIN,
564                 "Entry \"%s\" not found, creating", entry);
565     nn=xmlNewChild(n, nameSpace, BAD_CAST name, NULL);
566   } /* if node not found */
567   else {
568     /* node does exist, check whether this is ok */
569     if (
570       ((flags & GWEN_PATH_FLAGS_LAST) &&
571        (flags & GWEN_PATH_FLAGS_NAMEMUSTNOTEXIST)) ||
572       (!(flags & GWEN_PATH_FLAGS_LAST) &&
573        (flags & GWEN_PATH_FLAGS_PATHMUSTNOTEXIST))
574     ) {
575       DBG_VERBOUS(GWEN_LOGDOMAIN, "Entry \"%s\" already exists", entry);
576       return 0;
577     }
578   }
579 
580   return nn;
581 }
582 
583 
584 
585 
586 
587 
588 
_twoStrings_new()589 GWEN_XMLCMD_LXML_TWOSTRINGS *_twoStrings_new()
590 {
591   GWEN_XMLCMD_LXML_TWOSTRINGS *ts;
592 
593   GWEN_NEW_OBJECT(GWEN_XMLCMD_LXML_TWOSTRINGS, ts);
594   return ts;
595 }
596 
597 
598 
_twoStrings_free(GWEN_XMLCMD_LXML_TWOSTRINGS * ts)599 void _twoStrings_free(GWEN_XMLCMD_LXML_TWOSTRINGS *ts)
600 {
601   if (ts) {
602     if (ts->ownStringsBitmap & 1) {
603       if (ts->string1)
604         free(ts->string1);
605     }
606     if (ts->ownStringsBitmap & 2) {
607       if (ts->string2)
608         free(ts->string2);
609     }
610     GWEN_FREE_OBJECT(ts);
611   }
612 }
613 
614 
615 
_twoStrings_fromString(const char * source,uint8_t delimiter)616 GWEN_XMLCMD_LXML_TWOSTRINGS *_twoStrings_fromString(const char *source, uint8_t delimiter)
617 {
618   const char *s;
619   GWEN_XMLCMD_LXML_TWOSTRINGS *ts;
620 
621   ts=_twoStrings_new();
622 
623   s=strchr(source, delimiter);
624   if (s) {
625     int idx;
626 
627     idx=s-source;
628     ts->string1=strndup(source, idx);
629     ts->ownStringsBitmap|=1;
630     s++;
631     if (s &&*s)
632       ts->string2=(char*)s;
633 
634     return ts;
635   }
636   else {
637     ts->string1=(char*)source;
638     return ts;
639   }
640 }
641 
642 
643 
644 
645