]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/xml.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / shared / xml.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <stddef.h>
5 #include <string.h>
6
7 #include "macro.h"
8 #include "string-util.h"
9 #include "xml.h"
10
11 enum {
12 STATE_NULL,
13 STATE_TEXT,
14 STATE_TAG,
15 STATE_ATTRIBUTE,
16 };
17
18 static void inc_lines(unsigned *line, const char *s, size_t n) {
19 const char *p = s;
20
21 if (!line)
22 return;
23
24 for (;;) {
25 const char *f;
26
27 f = memchr(p, '\n', n);
28 if (!f)
29 return;
30
31 n -= (f - p) + 1;
32 p = f + 1;
33 (*line)++;
34 }
35 }
36
37 /* We don't actually do real XML here. We only read a simplistic
38 * subset, that is a bit less strict that XML and lacks all the more
39 * complex features, like entities, or namespaces. However, we do
40 * support some HTML5-like simplifications */
41
42 int xml_tokenize(const char **p, char **name, void **state, unsigned *line) {
43 const char *c, *e, *b;
44 char *ret;
45 int t;
46
47 assert(p);
48 assert(*p);
49 assert(name);
50 assert(state);
51
52 t = PTR_TO_INT(*state);
53 c = *p;
54
55 if (t == STATE_NULL) {
56 if (line)
57 *line = 1;
58 t = STATE_TEXT;
59 }
60
61 for (;;) {
62 if (*c == 0)
63 return XML_END;
64
65 switch (t) {
66
67 case STATE_TEXT: {
68 int x;
69
70 e = strchrnul(c, '<');
71 if (e > c) {
72 /* More text... */
73 ret = strndup(c, e - c);
74 if (!ret)
75 return -ENOMEM;
76
77 inc_lines(line, c, e - c);
78
79 *name = ret;
80 *p = e;
81 *state = INT_TO_PTR(STATE_TEXT);
82
83 return XML_TEXT;
84 }
85
86 assert(*e == '<');
87 b = c + 1;
88
89 if (startswith(b, "!--")) {
90 /* A comment */
91 e = strstr(b + 3, "-->");
92 if (!e)
93 return -EINVAL;
94
95 inc_lines(line, b, e + 3 - b);
96
97 c = e + 3;
98 continue;
99 }
100
101 if (*b == '?') {
102 /* Processing instruction */
103
104 e = strstr(b + 1, "?>");
105 if (!e)
106 return -EINVAL;
107
108 inc_lines(line, b, e + 2 - b);
109
110 c = e + 2;
111 continue;
112 }
113
114 if (*b == '!') {
115 /* DTD */
116
117 e = strchr(b + 1, '>');
118 if (!e)
119 return -EINVAL;
120
121 inc_lines(line, b, e + 1 - b);
122
123 c = e + 1;
124 continue;
125 }
126
127 if (*b == '/') {
128 /* A closing tag */
129 x = XML_TAG_CLOSE;
130 b++;
131 } else
132 x = XML_TAG_OPEN;
133
134 e = strpbrk(b, WHITESPACE "/>");
135 if (!e)
136 return -EINVAL;
137
138 ret = strndup(b, e - b);
139 if (!ret)
140 return -ENOMEM;
141
142 *name = ret;
143 *p = e;
144 *state = INT_TO_PTR(STATE_TAG);
145
146 return x;
147 }
148
149 case STATE_TAG:
150
151 b = c + strspn(c, WHITESPACE);
152 if (*b == 0)
153 return -EINVAL;
154
155 inc_lines(line, c, b - c);
156
157 e = b + strcspn(b, WHITESPACE "=/>");
158 if (e > b) {
159 /* An attribute */
160
161 ret = strndup(b, e - b);
162 if (!ret)
163 return -ENOMEM;
164
165 *name = ret;
166 *p = e;
167 *state = INT_TO_PTR(STATE_ATTRIBUTE);
168
169 return XML_ATTRIBUTE_NAME;
170 }
171
172 if (startswith(b, "/>")) {
173 /* An empty tag */
174
175 *name = NULL; /* For empty tags we return a NULL name, the caller must be prepared for that */
176 *p = b + 2;
177 *state = INT_TO_PTR(STATE_TEXT);
178
179 return XML_TAG_CLOSE_EMPTY;
180 }
181
182 if (*b != '>')
183 return -EINVAL;
184
185 c = b + 1;
186 t = STATE_TEXT;
187 continue;
188
189 case STATE_ATTRIBUTE:
190
191 if (*c == '=') {
192 c++;
193
194 if (IN_SET(*c, '\'', '"')) {
195 /* Tag with a quoted value */
196
197 e = strchr(c+1, *c);
198 if (!e)
199 return -EINVAL;
200
201 inc_lines(line, c, e - c);
202
203 ret = strndup(c+1, e - c - 1);
204 if (!ret)
205 return -ENOMEM;
206
207 *name = ret;
208 *p = e + 1;
209 *state = INT_TO_PTR(STATE_TAG);
210
211 return XML_ATTRIBUTE_VALUE;
212
213 }
214
215 /* Tag with a value without quotes */
216
217 b = strpbrk(c, WHITESPACE ">");
218 if (!b)
219 b = c;
220
221 ret = strndup(c, b - c);
222 if (!ret)
223 return -ENOMEM;
224
225 *name = ret;
226 *p = b;
227 *state = INT_TO_PTR(STATE_TAG);
228 return XML_ATTRIBUTE_VALUE;
229 }
230
231 t = STATE_TAG;
232 continue;
233 }
234
235 }
236
237 assert_not_reached("Bad state");
238 }