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