1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 21 окт. 2015 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins 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
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ui/ui.h>
23 #include <string.h>
24 #include <core/debug.h>
25 #include <core/alloc.h>
26 #include <core/calc/Expression.h>
27 #include <core/calc/Variables.h>
28 #include <errno.h>
29 
30 #include <ui/XMLHandler.h>
31 
32 namespace lsp
33 {
34     class ui_recording_handler: public XMLPlaybackNode
35     {
36         private:
37             ui_recording_handler & operator = (const ui_recording_handler &src);
38 
39         protected:
40             ui_builder             *pBuilder;
41 
42         public:
ui_recording_handler(ui_builder * bld,XMLNode * handler)43             explicit ui_recording_handler(ui_builder *bld, XMLNode *handler):
44                 XMLPlaybackNode(handler)
45             {
46                 pBuilder    = bld;
47             }
48 
~ui_recording_handler()49             virtual ~ui_recording_handler()
50             {
51                 pBuilder    = NULL;
52             }
53     };
54 
55     class ui_for_handler: public ui_recording_handler
56     {
57         private:
58             LSPString      *pID;
59             ssize_t         nFirst;
60             ssize_t         nLast;
61             ssize_t         nStep;
62 
63         protected:
iterate(ssize_t value)64             status_t iterate(ssize_t value)
65             {
66                 status_t res;
67                 if ((res = pBuilder->vars()->set_int(pID, value)) != STATUS_OK)
68                     return res;
69                 return playback();
70             }
71 
72         public:
ui_for_handler(ui_builder * bld,XMLNode * handler)73             explicit ui_for_handler(ui_builder *bld, XMLNode *handler) : ui_recording_handler(bld, handler)
74             {
75                 pID         = NULL;
76                 nFirst      = 0;
77                 nLast       = 0;
78                 nStep       = 1;
79             }
80 
init(const LSPString * const * atts)81             virtual status_t init(const LSPString * const *atts)
82             {
83                 bool increment_set = false;
84                 status_t res;
85 
86                 for ( ; *atts != NULL; atts += 2)
87                 {
88                     const LSPString *name   = atts[0];
89                     const LSPString *value  = atts[1];
90 
91                     if ((name == NULL) || (value == NULL))
92                         continue;
93 
94                     if (name->equals_ascii("id"))
95                     {
96                         if (pID != NULL)
97                             return STATUS_CORRUPTED;
98                         LSPString tmp;
99                         if ((res = pBuilder->eval_string(&tmp, value)) != STATUS_OK)
100                             return res;
101                         if ((pID = tmp.release()) == NULL)
102                             return STATUS_NO_MEM;
103                     }
104                     else if (name->equals_ascii("first"))
105                     {
106                         if ((res = pBuilder->eval_int(&nFirst, value)) != STATUS_OK)
107                             return res;
108                     }
109                     else if (name->equals_ascii("last"))
110                     {
111                         if ((res = pBuilder->eval_int(&nLast, value)) != STATUS_OK)
112                             return res;
113                     }
114                     else if (name->equals_ascii("step"))
115                     {
116                         if ((res = pBuilder->eval_int(&nStep, value)) != STATUS_OK)
117                             return res;
118                         increment_set = true;
119                     }
120                     else
121                     {
122                         lsp_error("Unknown attribute: %s", name->get_utf8());
123                         return STATUS_CORRUPTED;
124                     }
125                 }
126 
127                 // Compute increment
128                 if (!increment_set)
129                     nStep       = (nFirst <= nLast) ? 1 : -1;
130 
131                 return STATUS_OK;
132             }
133 
~ui_for_handler()134             virtual ~ui_for_handler()
135             {
136                 if (pID != NULL)
137                 {
138                     delete pID;
139                     pID     = NULL;
140                 }
141             }
142 
143         public:
execute()144             virtual status_t execute()
145             {
146                 status_t res;
147                 if (pID == NULL)
148                     return STATUS_OK;
149 
150                 // Create new scope
151                 if ((res = pBuilder->push_scope()) != STATUS_OK)
152                     return res;
153 
154                 // Perform a loop
155                 if (nFirst <= nLast)
156                 {
157                     for (ssize_t value = nFirst; value <= nLast; value += nStep)
158                         if ((res = iterate(value)) != STATUS_OK)
159                             break;
160                 }
161                 else
162                 {
163                     for (ssize_t value = nFirst; value >= nLast; value += nStep)
164                         if ((res = iterate(value)) != STATUS_OK)
165                             break;
166                 }
167 
168                 // Pop scope and return
169                 return (res == STATUS_OK) ? pBuilder->pop_scope() : res;
170             }
171     };
172 
173     class ui_attribute_handler: public ui_recording_handler
174     {
175         private:
176             cvector<LSPString>      vAtts;
177             size_t                  nLevel;
178             ssize_t                 nRecursion;
179 
180         public:
ui_attribute_handler(ui_builder * bld,XMLNode * handler)181             explicit ui_attribute_handler(ui_builder *bld, XMLNode *handler) : ui_recording_handler(bld, handler)
182             {
183                 nLevel     = 0;
184                 nRecursion = 0;
185             }
186 
~ui_attribute_handler()187             virtual ~ui_attribute_handler()
188             {
189                 for (size_t i=0, n=vAtts.size(); i<n; ++i)
190                 {
191                     LSPString *s = vAtts.at(i);
192                     if (s != NULL)
193                         delete s;
194                 }
195                 vAtts.flush();
196             }
197 
198         public:
init(const LSPString * const * atts)199             virtual status_t init(const LSPString * const *atts)
200             {
201                 status_t res;
202 
203                 // Generate list of appended properties
204                 for ( ; *atts != NULL; atts += 2)
205                 {
206                     const LSPString *name   = atts[0];
207                     const LSPString *value  = atts[1];
208 
209                     if ((name == NULL) || (value == NULL))
210                         continue;
211 
212                     if ((*atts)->equals_ascii("ui:recursion"))
213                     {
214                         if ((res = pBuilder->eval_int(&nRecursion, value)) != STATUS_OK)
215                             return res;
216                     }
217 
218                     // Process name
219                     LSPString *xname        = name->clone();
220                     if (xname == NULL)
221                         return STATUS_NO_MEM;
222                     else if (!vAtts.add(xname))
223                     {
224                         delete xname;
225                         return STATUS_NO_MEM;
226                     }
227 
228                     // Process value
229                     LSPString *xattr        = new LSPString();
230                     if (xattr == NULL)
231                         return STATUS_NO_MEM;
232                     else if (!vAtts.add(xattr))
233                     {
234                         delete xattr;
235                         return STATUS_NO_MEM;
236                     }
237 
238                     // Evaluate string
239                     if ((res = pBuilder->eval_string(xattr, value)) != STATUS_OK)
240                         return res;
241                 }
242 
243                 return STATUS_OK;
244             }
245 
playback_start_element(xml::IXMLHandler * handler,const LSPString * name,const LSPString * const * atts)246             virtual status_t playback_start_element(xml::IXMLHandler *handler, const LSPString *name, const LSPString * const *atts)
247             {
248                 size_t level = nLevel++;
249 
250                 // Skip parameter substitution for control tags
251                 if (name->starts_with_ascii("ui:"))
252                     return ui_recording_handler::playback_start_element(handler, name, atts);
253 
254                 cvector<LSPString> tmp;
255 
256                 // Need to override attributes?
257                 if ((nRecursion < 0) || (level <= size_t(nRecursion)))
258                 {
259                     // Copy attributes
260                     for (size_t i=0; atts[i] != NULL; ++i)
261                         if (!tmp.add(const_cast<LSPString *>(atts[i])))
262                             return STATUS_NO_MEM;
263 
264                     // Append unexisting attributes
265                     LSPString **vatts = vAtts.get_array();
266                     for (size_t i=0, n=vAtts.size(); i<n; i += 2)
267                     {
268                         LSPString *name   = vatts[i];
269                         LSPString *value  = vatts[i+1];
270 
271                         // Check for duplicate
272                         for (size_t j=0; atts[j] != NULL; j+=2)
273                             if (atts[j]->equals(name))
274                             {
275                                 name = NULL;
276                                 break;
277                             }
278 
279                         // Append property if it does not exist
280                         if (name == NULL)
281                             continue;
282 
283                         if (!tmp.add(name))
284                             return STATUS_NO_MEM;
285                         if (!tmp.add(value))
286                             return STATUS_NO_MEM;
287                     }
288 
289                     // Append argument terminator
290                     if (!tmp.add(NULL))
291                         return STATUS_NO_MEM;
292 
293                     // Override properties with our own list
294                     atts = tmp.get_array();
295                 }
296                 return ui_recording_handler::playback_start_element(handler, name, atts);
297             }
298 
playback_end_element(xml::IXMLHandler * handler,const LSPString * name)299             virtual status_t playback_end_element(xml::IXMLHandler *handler, const LSPString *name)
300             {
301                 --nLevel;
302                 return ui_recording_handler::playback_end_element(handler, name);
303             }
304     };
305 
306     class ui_set_handler: public XMLNode
307     {
308         private:
309             ui_set_handler & operator = (const ui_set_handler &src);
310 
311         protected:
312             ui_builder             *pBuilder;
313 
314         public:
ui_set_handler(ui_builder * bld)315             explicit ui_set_handler(ui_builder *bld)
316             {
317                 pBuilder    = bld;
318             }
319 
~ui_set_handler()320             virtual ~ui_set_handler()
321             {
322                 pBuilder    = NULL;
323             }
324 
325         public:
init(const LSPString * const * atts)326             virtual status_t init(const LSPString * const *atts)
327             {
328                 status_t res;
329                 size_t flags = 0;
330                 LSPString v_name;
331                 calc::value_t v_value;
332                 calc::init_value(&v_value);
333 
334                 for ( ; *atts != NULL; atts += 2)
335                 {
336                     const LSPString *name   = atts[0];
337                     const LSPString *value  = atts[1];
338 
339                     if ((name == NULL) || (value == NULL))
340                         continue;
341 
342                     if (name->equals_ascii("id"))
343                     {
344                         if ((res = pBuilder->eval_string(&v_name, value)) != STATUS_OK)
345                             return res;
346                         flags      |= 1;
347                     }
348                     else if (name->equals_ascii("value"))
349                     {
350                         if ((res = pBuilder->evaluate(&v_value, value)) != STATUS_OK)
351                             return res;
352                         flags      |= 2;
353                     }
354                     else
355                     {
356                         lsp_error("Unknown attribute: %s", name->get_utf8());
357                         return STATUS_CORRUPTED;
358                     }
359                 }
360 
361                 if (flags != 3)
362                 {
363                     lsp_error("Not all attributes are set");
364                     return STATUS_CORRUPTED;
365                 }
366 
367                 // Set variable and destroy value
368                 res = pBuilder->vars()->set(&v_name, &v_value);
369                 calc::destroy_value(&v_value);
370                 return res;
371             }
372     };
373 
374     class ui_if_handler: public XMLNode
375     {
376         private:
377             ui_if_handler & operator = (const ui_if_handler &src);
378 
379         protected:
380             ui_builder             *pBuilder;
381             XMLNode                *pHandler;
382             bool                    bPass;
383 
384         public:
ui_if_handler(ui_builder * bld,XMLNode * handler)385             explicit ui_if_handler(ui_builder *bld, XMLNode *handler)
386             {
387                 pBuilder    = bld;
388                 pHandler    = handler;
389                 bPass       = true;
390             }
391 
~ui_if_handler()392             virtual ~ui_if_handler()
393             {
394                 pBuilder    = NULL;
395                 pHandler    = NULL;
396             }
397 
398         public:
init(const LSPString * const * atts)399             virtual status_t init(const LSPString * const *atts)
400             {
401                 status_t res;
402                 bool valid = false;
403 
404                 for ( ; *atts != NULL; atts += 2)
405                 {
406                     const LSPString *name   = atts[0];
407                     const LSPString *value  = atts[1];
408 
409                     if ((name == NULL) || (value == NULL))
410                         continue;
411 
412                     if (name->equals_ascii("test"))
413                     {
414                         if ((res = pBuilder->eval_bool(&bPass, value)) != STATUS_OK)
415                             return res;
416                         valid = true;
417                     }
418                     else
419                     {
420                         lsp_error("Unknown attribute: %s", name->get_utf8());
421                         return STATUS_CORRUPTED;
422                     }
423                 }
424 
425                 if (!valid)
426                 {
427                     lsp_error("Not all attributes are set");
428                     return STATUS_CORRUPTED;
429                 }
430 
431                 return STATUS_OK;
432             }
433 
start_element(XMLNode ** child,const LSPString * name,const LSPString * const * atts)434             virtual status_t start_element(XMLNode **child, const LSPString *name, const LSPString * const *atts)
435             {
436                 return (bPass) ? pHandler->start_element(child, name, atts) : STATUS_OK;
437             }
438 
end_element(const LSPString * name)439             virtual status_t end_element(const LSPString *name)
440             {
441                 return (bPass) ? pHandler->end_element(name) : STATUS_OK;
442             }
443 
completed(XMLNode * child)444             virtual status_t completed(XMLNode *child)
445             {
446                 return (bPass) ? pHandler->completed(child) : STATUS_OK;
447             }
448 
quit()449             virtual status_t quit()
450             {
451                 return (bPass) ? pHandler->quit() : STATUS_OK;
452             }
453 
enter()454             virtual status_t enter()
455             {
456                 return (bPass) ? pHandler->enter() : STATUS_OK;
457             }
458     };
459 
460     class ui_widget_handler: public XMLNode
461     {
462         private:
463             ui_widget_handler & operator = (const ui_widget_handler *);
464 
465         private:
466             ui_builder             *pBuilder;
467             CtlWidget              *pWidget;
468             ui_widget_handler      *pChild;
469             ui_recording_handler   *pSpecial;
470             XMLNode                *pOther;
471 
472         public:
ui_widget_handler(ui_builder * bld,CtlWidget * widget)473             explicit ui_widget_handler(ui_builder *bld, CtlWidget *widget)
474             {
475                 pBuilder    = bld;
476                 pWidget     = widget;
477                 pChild      = NULL;
478                 pSpecial    = NULL;
479                 pOther      = NULL;
480             }
481 
~ui_widget_handler()482             virtual ~ui_widget_handler()
483             {
484                 if (pChild != NULL)
485                     pChild = NULL;
486             }
487 
488         public:
enter()489             virtual status_t enter()
490             {
491                 pWidget->begin();
492                 return STATUS_OK;
493             }
494 
start_element(XMLNode ** child,const LSPString * name,const LSPString * const * atts)495             virtual status_t start_element(XMLNode **child, const LSPString *name, const LSPString * const *atts)
496             {
497                 status_t res;
498 
499                 // Check for special conditions
500                 if (name->starts_with_ascii("ui:"))
501                 {
502                     // Build special handler
503                     if (name->equals_ascii("ui:for"))
504                     {
505                         if ((pSpecial = new ui_for_handler(pBuilder, this)) == NULL)
506                             return STATUS_NO_MEM;
507                         if ((res = pSpecial->init(atts)) != STATUS_OK)
508                             return res;
509                         *child  = pSpecial;
510                     }
511                     else if (name->equals_ascii("ui:attributes"))
512                     {
513                         if ((pSpecial = new ui_attribute_handler(pBuilder, this)) == NULL)
514                             return STATUS_NO_MEM;
515                         if ((res = pSpecial->init(atts)) != STATUS_OK)
516                             return res;
517                         *child  = pSpecial;
518                     }
519                     else if (name->equals_ascii("ui:set"))
520                     {
521                         ui_set_handler *h = new ui_set_handler(pBuilder);
522                         if (h == NULL)
523                             return STATUS_NO_MEM;
524                         if ((res = h->init(atts)) != STATUS_OK)
525                             return res;
526                         *child  = pOther    = h;
527                     }
528                     else if (name->equals_ascii("ui:if"))
529                     {
530                         ui_if_handler *h = new ui_if_handler(pBuilder, this);
531                         if (h == NULL)
532                             return STATUS_NO_MEM;
533                         if ((res = h->init(atts)) != STATUS_OK)
534                             return res;
535                         *child  = pOther    = h;
536                     }
537                     else
538                         res         = STATUS_CORRUPTED;
539 
540                     return res;
541                 }
542 
543                 // Get UI
544                 plugin_ui *ui               = pBuilder->get_ui();
545 
546                 // Create widget
547                 CtlWidget *widget           = ui->create_widget(name->get_utf8());
548                 if (widget == NULL)
549                     return STATUS_OK;       // No handler
550                 widget->init();
551 
552                 // Initialize pWidget parameters
553                 for ( ; *atts != NULL; atts += 2)
554                 {
555                     LSPString aname, avalue;
556                     if ((res = pBuilder->eval_string(&aname, atts[0])) != STATUS_OK)
557                         return res;
558                     if ((res = pBuilder->eval_string(&avalue, atts[1])) != STATUS_OK)
559                         return res;
560 
561                     // Set widget attribute
562                     widget->set(aname.get_utf8(), avalue.get_utf8());
563                 }
564 
565                 // Create handler
566                 *child = pChild = new ui_widget_handler(pBuilder, widget);
567                 return (pChild != NULL) ? STATUS_OK : STATUS_NO_MEM;
568             }
569 
quit()570             virtual status_t quit()
571             {
572                 pWidget->end();
573                 return STATUS_OK;
574             }
575 
completed(XMLNode * child)576             virtual status_t completed(XMLNode *child)
577             {
578                 status_t res = STATUS_OK;
579                 if ((child == pChild) && (pChild != NULL))
580                 {
581                     if ((pWidget != NULL) && (pChild->pWidget != NULL))
582                     {
583                         CtlWidget *w = pChild->pWidget;
584                         if (w != NULL)
585                             res = pWidget->add(w);
586                     }
587 
588                     // Remove child
589                     delete pChild;
590                     pChild  = NULL;
591                 }
592                 else if ((child == pSpecial) && (pSpecial != NULL))
593                 {
594                     ui_recording_handler * special = pSpecial;
595                     pSpecial = NULL;
596 
597                     res = special->execute();
598                     delete special;
599                 }
600                 if ((child == pOther) && (pOther != NULL))
601                 {
602                     delete pOther;
603                     pOther = NULL;
604                 }
605 
606                 return res;
607             }
608     };
609 
610     class ui_root_handler: public XMLNode
611     {
612         private:
613             ui_root_handler & operator = (const ui_root_handler &);
614 
615         private:
616             ui_builder         *pBuilder;
617             ui_widget_handler  *pChild;
618 
619         public:
ui_root_handler(ui_builder * bld)620             explicit ui_root_handler(ui_builder *bld)
621             {
622                 pBuilder    = bld;
623                 pChild      = NULL;
624             }
625 
~ui_root_handler()626             virtual ~ui_root_handler()
627             {
628                 if (pChild != NULL)
629                 {
630                     delete pChild;
631                     pChild = NULL;
632                 }
633             }
634 
635         public:
start_element(XMLNode ** child,const LSPString * name,const LSPString * const * atts)636             virtual status_t start_element(XMLNode **child, const LSPString *name, const LSPString * const *atts)
637             {
638                 status_t res;
639 
640                 // Check that root tag is valid
641                 const char *root_tag = widget_ctl(WC_PLUGIN);
642                 if (!name->equals_ascii(root_tag))
643                 {
644                     lsp_error("expected root element <%s>", root_tag);
645                     return STATUS_CORRUPTED;
646                 }
647 
648                 // Get UI
649                 plugin_ui *ui           = pBuilder->get_ui();
650 
651                 // Create widget
652                 CtlWidget *widget       = ui->create_widget(name->get_utf8());
653                 if (widget == NULL)
654                     return STATUS_OK;       // No handler
655                 widget->init();
656 
657                 // Initialize widget parameters
658                 for ( ; *atts != NULL; atts += 2)
659                 {
660                     LSPString aname, avalue;
661                     if ((res = pBuilder->eval_string(&aname, atts[0])) != STATUS_OK)
662                         return res;
663                     if ((res = pBuilder->eval_string(&avalue, atts[1])) != STATUS_OK)
664                         return res;
665 
666                     // Set widget attribute
667                     widget->set(aname.get_utf8(), avalue.get_utf8());
668                 }
669 
670                 // Create handler
671                 *child = pChild = new ui_widget_handler(pBuilder, widget);
672                 return (pChild != NULL) ? STATUS_OK : STATUS_NO_MEM;
673             }
674     };
675 
676     //-------------------------------------------------------------------------
677     // UI Builder implementation
ui_builder(plugin_ui * ui)678     ui_builder::ui_builder(plugin_ui *ui)
679     {
680         CtlPortResolver *r = new CtlPortResolver();
681         r->init(ui);
682 
683         pUI         = ui;
684         pPlugin     = r;
685         vRoot.set_resolver(r);
686     }
687 
~ui_builder()688     ui_builder::~ui_builder()
689     {
690         for (size_t i=0, n=vStack.size(); i<n; ++i)
691         {
692             calc::Resolver *r = vStack.at(i);
693             if (r != NULL)
694                 delete r;
695         }
696 
697         vRoot.set_resolver(NULL);
698         vStack.flush();
699         if (pPlugin != NULL)
700             delete pPlugin;
701     }
702 
703     // Evaluate
evaluate(calc::value_t * value,const LSPString * expr)704     status_t ui_builder::evaluate(calc::value_t *value, const LSPString *expr)
705     {
706         status_t res;
707         calc::Expression e;
708 
709         // Parse expression
710         if ((res = e.parse(expr, calc::Expression::FLAG_STRING)) != STATUS_OK)
711         {
712             lsp_error("Could not parse expression: %s", expr->get_utf8());
713             return res;
714         }
715         e.set_resolver(vars());
716 
717         // Evaluate expression
718         res = e.evaluate(value);
719         if (res != STATUS_OK)
720             lsp_error("Could not evaluate expression: %s", expr->get_utf8());
721 
722         return res;
723     }
724 
push_scope()725     status_t ui_builder::push_scope()
726     {
727         // Create variables
728         calc::Variables *v = new calc::Variables();
729         if (v == NULL)
730             return STATUS_NO_MEM;
731 
732         // Bind resolver, push to stack and quit
733         v->set_resolver(vars());
734         if (!vStack.push(v))
735         {
736             delete v;
737             return STATUS_NO_MEM;
738         }
739 
740         return STATUS_OK;
741     }
742 
pop_scope()743     status_t ui_builder::pop_scope()
744     {
745         calc::Variables *r = NULL;
746         if (!vStack.pop(&r))
747             return STATUS_BAD_STATE;
748         if (r != NULL)
749             delete r;
750         return STATUS_OK;
751     }
752 
eval_string(LSPString * value,const LSPString * expr)753     status_t ui_builder::eval_string(LSPString *value, const LSPString *expr)
754     {
755         calc::value_t v;
756 
757         init_value(&v);
758         status_t res = evaluate(&v, expr);
759         if (res != STATUS_OK)
760             return res;
761 
762         if ((res = calc::cast_string(&v)) == STATUS_OK)
763         {
764             if (v.type == calc::VT_STRING)
765                 value->swap(v.v_str);
766             else
767             {
768                 lsp_error("Evaluation error: bad return type of expression %s", expr->get_utf8());
769                 res = STATUS_BAD_TYPE;
770             }
771         }
772         destroy_value(&v);
773         return res;
774     }
775 
eval_bool(bool * value,const LSPString * expr)776     status_t ui_builder::eval_bool(bool *value, const LSPString *expr)
777     {
778         calc::value_t v;
779         init_value(&v);
780 
781         status_t res = evaluate(&v, expr);
782         if (res != STATUS_OK)
783             return res;
784 
785         if ((res = calc::cast_bool(&v)) == STATUS_OK)
786         {
787             if (v.type == calc::VT_BOOL)
788                 *value  = v.v_bool;
789             else
790             {
791                 lsp_error("Evaluation error: bad return type of expression %s", expr->get_utf8());
792                 res = STATUS_BAD_TYPE;
793             }
794         }
795         destroy_value(&v);
796         return res;
797     }
798 
eval_int(ssize_t * value,const LSPString * expr)799     status_t ui_builder::eval_int(ssize_t *value, const LSPString *expr)
800     {
801         LSPString tmp;
802         status_t res = eval_string(&tmp, expr);
803         if (res != STATUS_OK)
804             return res;
805 
806         // Parse string as integer value
807         errno = 0;
808         char *eptr = NULL;
809         const char *p = tmp.get_utf8();
810         long v = ::strtol(p, &eptr, 10);
811         if ((errno != 0) || (eptr == NULL) || (*eptr != '\0'))
812         {
813             lsp_error("Evaluation error: bad return type of expression %s", expr->get_utf8());
814             return STATUS_INVALID_VALUE;
815         }
816 
817         // Store value
818         *value = v;
819         return STATUS_OK;
820     }
821 
build(const LSPString * path)822     status_t ui_builder::build(const LSPString *path)
823     {
824         ui_root_handler root(this);
825         XMLHandler handler;
826         return handler.parse(path, &root);
827     }
828 
build(const char * path)829     status_t ui_builder::build(const char *path)
830     {
831         ui_root_handler root(this);
832         XMLHandler handler;
833         return handler.parse(path, &root);
834     }
835 }
836