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: 29 окт. 2019 г.
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 <core/debug.h>
23 #include <core/files/xml/PushParser.h>
24 
25 namespace lsp
26 {
27     namespace xml
28     {
29 
PushParser()30         PushParser::PushParser()
31         {
32         }
33 
~PushParser()34         PushParser::~PushParser()
35         {
36         }
37 
drop_list(cvector<LSPString> * list)38         void PushParser::drop_list(cvector<LSPString> *list)
39         {
40             for (size_t i=0, n=list->size(); i<n; ++i)
41             {
42                 LSPString *item = list->at(i);
43                 if (item != NULL)
44                     delete item;
45             }
46             list->clear();
47         }
48 
parse_file(IXMLHandler * handler,const char * path,const char * charset)49         status_t PushParser::parse_file(IXMLHandler *handler, const char *path, const char *charset)
50         {
51             IXMLHandler stub;
52             status_t res = sParser.open(path, charset);
53             if (res == STATUS_OK)
54                 res = parse_document((handler != NULL) ? handler : &stub);
55             if (res == STATUS_OK)
56                 res = sParser.close();
57             else
58                 sParser.close();
59             return res;
60         }
61 
parse_file(IXMLHandler * handler,const LSPString * path,const char * charset)62         status_t PushParser::parse_file(IXMLHandler *handler, const LSPString *path, const char *charset)
63         {
64             IXMLHandler stub;
65             status_t res = sParser.open(path, charset);
66             if (res == STATUS_OK)
67                 res = parse_document((handler != NULL) ? handler : &stub);
68             if (res == STATUS_OK)
69                 res = sParser.close();
70             else
71                 sParser.close();
72             return res;
73         }
74 
parse_file(IXMLHandler * handler,const io::Path * path,const char * charset)75         status_t PushParser::parse_file(IXMLHandler *handler, const io::Path *path, const char *charset)
76         {
77             IXMLHandler stub;
78             status_t res = sParser.open(path, charset);
79             if (res == STATUS_OK)
80                 res = parse_document((handler != NULL) ? handler : &stub);
81             if (res == STATUS_OK)
82                 res = sParser.close();
83             else
84                 sParser.close();
85             return res;
86         }
87 
parse_data(IXMLHandler * handler,io::IInStream * is,size_t flags,const char * charset)88         status_t PushParser::parse_data(IXMLHandler *handler, io::IInStream *is, size_t flags, const char *charset)
89         {
90             IXMLHandler stub;
91             status_t res = sParser.wrap(is, flags, charset);
92             if (res == STATUS_OK)
93                 res = parse_document((handler != NULL) ? handler : &stub);
94             if (res == STATUS_OK)
95                 res = sParser.close();
96             else
97                 sParser.close();
98             return res;
99         }
100 
parse_data(IXMLHandler * handler,const char * str,const char * charset)101         status_t PushParser::parse_data(IXMLHandler *handler, const char *str, const char *charset)
102         {
103             IXMLHandler stub;
104             status_t res = sParser.wrap(str, charset);
105             if (res == STATUS_OK)
106                 res = parse_document((handler != NULL) ? handler : &stub);
107             if (res == STATUS_OK)
108                 res = sParser.close();
109             else
110                 sParser.close();
111             return res;
112         }
113 
parse_data(IXMLHandler * handler,const LSPString * str)114         status_t PushParser::parse_data(IXMLHandler *handler, const LSPString *str)
115         {
116             IXMLHandler stub;
117             status_t res = sParser.wrap(str);
118             if (res == STATUS_OK)
119                 res = parse_document((handler != NULL) ? handler : &stub);
120             if (res == STATUS_OK)
121                 res = sParser.close();
122             else
123                 sParser.close();
124             return res;
125         }
126 
parse_data(IXMLHandler * handler,io::IInSequence * seq,size_t flags)127         status_t PushParser::parse_data(IXMLHandler *handler, io::IInSequence *seq, size_t flags)
128         {
129             IXMLHandler stub;
130             status_t res = sParser.wrap(seq, flags);
131             if (res == STATUS_OK)
132                 res = parse_document((handler != NULL) ? handler : &stub);
133             if (res == STATUS_OK)
134                 res = sParser.close();
135             else
136                 sParser.close();
137             return res;
138         }
139 
parse_document(IXMLHandler * handler)140         status_t PushParser::parse_document(IXMLHandler *handler)
141         {
142             status_t token, res, last = -1;
143             LSPString tmp, *ptmp;
144             cvector<LSPString> ctag;
145 
146             do
147             {
148                 // Get next token
149                 if ((token = sParser.read_next()) < 0)
150                 {
151                     res = -token;
152                     break;
153                 }
154 
155                 // Is there tag element pending?
156                 if ((ctag.size() > 0) && ((token != XT_ATTRIBUTE) && (token != XT_ENTITY_RESOLVE)))
157                 {
158                     // Add NULL-terminating element
159                     if (!ctag.add(NULL))
160                         return STATUS_NO_MEM;
161 
162                     // Analyze state
163                     LSPString **atts        = ctag.get_array();
164                     size_t n                = ctag.size();
165                     if (n & 1) // Nubmber of elements should be even
166                     {
167                         res     = STATUS_CORRUPTED;
168                         break;
169                     }
170 
171                     // Call handler
172                     res     = handler->start_element(atts[0], &atts[1]);
173                     drop_list(&ctag);
174                     if (res != STATUS_OK)
175                         break;
176                 }
177 
178                 // Dispatch event
179                 last = token;
180                 switch (token)
181                 {
182                     case XT_START_DOCUMENT:
183                         res = handler->start_document(
184                                 sParser.xml_version(),
185                                 sParser.version(),
186                                 sParser.encoding(),
187                                 sParser.is_standalone()
188                             );
189                         break;
190 
191                     case XT_END_DOCUMENT:
192                         res = handler->end_document();
193                         break;
194 
195                     case XT_CDATA:
196                         res = handler->cdata(sParser.value());
197                         break;
198 
199                     case XT_CHARACTERS:
200                         res = handler->characters(sParser.value());
201                         break;
202 
203                     case XT_COMMENT:
204                         res = handler->comment(sParser.value());
205                         break;
206 
207                     case XT_ENTITY_RESOLVE:
208                         res = handler->resolve(&tmp, sParser.name());
209                         if (res == STATUS_OK)
210                             res = sParser.set_value(&tmp);
211                         tmp.clear();
212                         break;
213 
214                     case XT_START_ELEMENT:
215                         // Create copy of tag name
216                         if ((ptmp = sParser.name()->clone()) == NULL)
217                         {
218                             res = STATUS_NO_MEM;
219                             break;
220                         }
221                         else if (!ctag.add(ptmp))
222                         {
223                             delete ptmp;
224                             res = STATUS_NO_MEM;
225                             break;
226                         }
227                         break;
228 
229                     case XT_ATTRIBUTE:
230                         // Create copy of attribute name
231                         if ((ptmp = sParser.name()->clone()) == NULL)
232                         {
233                             res = STATUS_NO_MEM;
234                             break;
235                         }
236                         else if (!ctag.add(ptmp))
237                         {
238                             delete ptmp;
239                             res = STATUS_NO_MEM;
240                             break;
241                         }
242 
243                         // Create copy of tag value
244                         if ((ptmp = sParser.value()->clone()) == NULL)
245                         {
246                             res = STATUS_NO_MEM;
247                             break;
248                         }
249                         else if (!ctag.add(ptmp))
250                         {
251                             delete ptmp;
252                             res = STATUS_NO_MEM;
253                             break;
254                         }
255                         break;
256 
257                     case XT_END_ELEMENT:
258                         res = handler->end_element(sParser.name());
259                         break;
260 
261                     case XT_PROCESSING_INSTRUCTION:
262                         res = handler->processing(sParser.name(), sParser.value());
263                         break;
264 
265                     case XT_DTD:
266                         res = handler->doctype(sParser.doctype(), sParser.pub_literal(), sParser.sys_literal());
267                         break;
268 
269                     default:
270                         res = STATUS_CORRUPTED;
271                         break;
272                 }
273             } while (res == STATUS_OK);
274 
275             // Drop list
276             drop_list(&ctag);
277 
278             // Return result
279             return ((res == STATUS_EOF) && (last == XT_END_DOCUMENT)) ?  STATUS_OK : res;
280         }
281 
282     } /* namespace xml */
283 } /* namespace lsp */
284