Line data Source code
1 : /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2 :
3 : /***
4 : This file is part of systemd.
5 :
6 : Copyright 2013 Lennart Poettering
7 :
8 : systemd is free software; you can redistribute it and/or modify it
9 : under the terms of the GNU Lesser General Public License as published by
10 : the Free Software Foundation; either version 2.1 of the License, or
11 : (at your option) any later version.
12 :
13 : systemd is distributed in the hope that it will be useful, but
14 : 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 License
19 : along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 : ***/
21 :
22 : #include <string.h>
23 :
24 : #include "util.h"
25 : #include "xml.h"
26 :
27 : enum {
28 : STATE_NULL,
29 : STATE_TEXT,
30 : STATE_TAG,
31 : STATE_ATTRIBUTE,
32 : };
33 :
34 859 : static void inc_lines(unsigned *line, const char *s, size_t n) {
35 859 : const char *p = s;
36 :
37 859 : if (!line)
38 13 : return;
39 :
40 : for (;;) {
41 : const char *f;
42 :
43 1177 : f = memchr(p, '\n', n);
44 1177 : if (!f)
45 846 : return;
46 :
47 331 : n -= (f - p) + 1;
48 331 : p = f + 1;
49 331 : (*line)++;
50 331 : }
51 : }
52 :
53 : /* We don't actually do real XML here. We only read a simplistic
54 : * subset, that is a bit less strict that XML and lacks all the more
55 : * complex features, like entities, or namespaces. However, we do
56 : * support some HTML5-like simplifications */
57 :
58 896 : int xml_tokenize(const char **p, char **name, void **state, unsigned *line) {
59 : const char *c, *e, *b;
60 : char *ret;
61 : int t;
62 :
63 896 : assert(p);
64 896 : assert(*p);
65 896 : assert(name);
66 896 : assert(state);
67 :
68 896 : t = PTR_TO_INT(*state);
69 896 : c = *p;
70 :
71 896 : if (t == STATE_NULL) {
72 13 : if (line)
73 9 : *line = 1;
74 13 : t = STATE_TEXT;
75 : }
76 :
77 : for (;;) {
78 1106 : if (*c == 0)
79 13 : return XML_END;
80 :
81 1093 : switch (t) {
82 :
83 : case STATE_TEXT: {
84 : int x;
85 :
86 570 : e = strchrnul(c, '<');
87 570 : if (e > c) {
88 : /* More text... */
89 283 : ret = strndup(c, e - c);
90 283 : if (!ret)
91 0 : return -ENOMEM;
92 :
93 283 : inc_lines(line, c, e - c);
94 :
95 283 : *name = ret;
96 283 : *p = e;
97 283 : *state = INT_TO_PTR(STATE_TEXT);
98 :
99 283 : return XML_TEXT;
100 : }
101 :
102 287 : assert(*e == '<');
103 287 : b = c + 1;
104 :
105 287 : if (startswith(b, "!--")) {
106 : /* A comment */
107 40 : e = strstr(b + 3, "-->");
108 40 : if (!e)
109 0 : return -EINVAL;
110 :
111 40 : inc_lines(line, b, e + 3 - b);
112 :
113 40 : c = e + 3;
114 40 : continue;
115 : }
116 :
117 247 : if (*b == '?') {
118 : /* Processing instruction */
119 :
120 5 : e = strstr(b + 1, "?>");
121 5 : if (!e)
122 0 : return -EINVAL;
123 :
124 5 : inc_lines(line, b, e + 2 - b);
125 :
126 5 : c = e + 2;
127 5 : continue;
128 : }
129 :
130 242 : if (*b == '!') {
131 : /* DTD */
132 :
133 9 : e = strchr(b + 1, '>');
134 9 : if (!e)
135 0 : return -EINVAL;
136 :
137 9 : inc_lines(line, b, e + 1 - b);
138 :
139 9 : c = e + 1;
140 9 : continue;
141 : }
142 :
143 233 : if (*b == '/') {
144 : /* A closing tag */
145 78 : x = XML_TAG_CLOSE;
146 78 : b++;
147 : } else
148 155 : x = XML_TAG_OPEN;
149 :
150 233 : e = strpbrk(b, WHITESPACE "/>");
151 233 : if (!e)
152 0 : return -EINVAL;
153 :
154 233 : ret = strndup(b, e - b);
155 233 : if (!ret)
156 0 : return -ENOMEM;
157 :
158 233 : *name = ret;
159 233 : *p = e;
160 233 : *state = INT_TO_PTR(STATE_TAG);
161 :
162 233 : return x;
163 : }
164 :
165 : case STATE_TAG:
166 :
167 378 : b = c + strspn(c, WHITESPACE);
168 378 : if (*b == 0)
169 0 : return -EINVAL;
170 :
171 378 : inc_lines(line, c, b - c);
172 :
173 378 : e = b + strcspn(b, WHITESPACE "=/>");
174 378 : if (e > b) {
175 : /* An attribute */
176 :
177 145 : ret = strndup(b, e - b);
178 145 : if (!ret)
179 0 : return -ENOMEM;
180 :
181 145 : *name = ret;
182 145 : *p = e;
183 145 : *state = INT_TO_PTR(STATE_ATTRIBUTE);
184 :
185 145 : return XML_ATTRIBUTE_NAME;
186 : }
187 :
188 233 : if (startswith(b, "/>")) {
189 : /* An empty tag */
190 :
191 77 : *name = NULL; /* For empty tags we return a NULL name, the caller must be prepared for that */
192 77 : *p = b + 2;
193 77 : *state = INT_TO_PTR(STATE_TEXT);
194 :
195 77 : return XML_TAG_CLOSE_EMPTY;
196 : }
197 :
198 156 : if (*b != '>')
199 0 : return -EINVAL;
200 :
201 156 : c = b + 1;
202 156 : t = STATE_TEXT;
203 156 : continue;
204 :
205 : case STATE_ATTRIBUTE:
206 :
207 145 : if (*c == '=') {
208 145 : c++;
209 :
210 145 : if (*c == '\'' || *c == '\"') {
211 : /* Tag with a quoted value */
212 :
213 144 : e = strchr(c+1, *c);
214 144 : if (!e)
215 0 : return -EINVAL;
216 :
217 144 : inc_lines(line, c, e - c);
218 :
219 144 : ret = strndup(c+1, e - c - 1);
220 144 : if (!ret)
221 0 : return -ENOMEM;
222 :
223 144 : *name = ret;
224 144 : *p = e + 1;
225 144 : *state = INT_TO_PTR(STATE_TAG);
226 :
227 144 : return XML_ATTRIBUTE_VALUE;
228 :
229 : }
230 :
231 : /* Tag with a value without quotes */
232 :
233 1 : b = strpbrk(c, WHITESPACE ">");
234 1 : if (!b)
235 0 : b = c;
236 :
237 1 : ret = strndup(c, b - c);
238 1 : if (!ret)
239 0 : return -ENOMEM;
240 :
241 1 : *name = ret;
242 1 : *p = b;
243 1 : *state = INT_TO_PTR(STATE_TAG);
244 1 : return XML_ATTRIBUTE_VALUE;
245 : }
246 :
247 0 : t = STATE_TAG;
248 0 : continue;
249 : }
250 :
251 210 : }
252 :
253 : assert_not_reached("Bad state");
254 : }
|