Bug Summary

File:parse/language.c
Warning:line 1603, column 6
Access to field 'insensitive' results in a dereference of a null pointer (loaded from field 'name')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name language.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-libcss -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-libcss -resource-dir /usr/lib/llvm-19/lib/clang/19 -D _BSD_SOURCE -D _DEFAULT_SOURCE -I /var/lib/jenkins/workspace/scan-build-libcss/include/ -I /var/lib/jenkins/workspace/scan-build-libcss/src -D _ALIGNED=__attribute__((aligned)) -D STMTEXPR=1 -D DEBUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Og -Wwrite-strings -Wno-error -std=c99 -fconst-strings -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-libcss/clangScanBuildReports/2025-11-17-181508-1371959-1 -x c src/parse/language.c
1/*
2 * This file is part of LibCSS.
3 * Licensed under the MIT License,
4 * http://www.opensource.org/licenses/mit-license.php
5 * Copyright 2008 John-Mark Bell <jmb@netsurf-browser.org>
6 */
7
8#include <assert.h>
9#include <string.h>
10
11#include <parserutils/utils/stack.h>
12
13#include "stylesheet.h"
14#include "lex/lex.h"
15#include "parse/font_face.h"
16#include "parse/important.h"
17#include "parse/language.h"
18#include "parse/mq.h"
19#include "parse/parse.h"
20#include "parse/propstrings.h"
21#include "parse/properties/properties.h"
22#include "parse/properties/utils.h"
23
24#include "utils/parserutilserror.h"
25#include "utils/utils.h"
26
27typedef struct context_entry {
28 css_parser_event type; /**< Type of entry */
29 void *data; /**< Data for context */
30} context_entry;
31
32/* Event handlers */
33static css_error language_handle_event(css_parser_event type,
34 const parserutils_vector *tokens, void *pw);
35static css_error handleStartStylesheet(css_language *c,
36 const parserutils_vector *vector);
37static css_error handleEndStylesheet(css_language *c,
38 const parserutils_vector *vector);
39static css_error handleStartRuleset(css_language *c,
40 const parserutils_vector *vector);
41static css_error handleEndRuleset(css_language *c,
42 const parserutils_vector *vector);
43static css_error handleStartAtRule(css_language *c,
44 const parserutils_vector *vector);
45static css_error handleEndAtRule(css_language *c,
46 const parserutils_vector *vector);
47static css_error handleStartBlock(css_language *c,
48 const parserutils_vector *vector);
49static css_error handleEndBlock(css_language *c,
50 const parserutils_vector *vector);
51static css_error handleBlockContent(css_language *c,
52 const parserutils_vector *vector);
53static css_error handleEndBlockContent(css_language *c,
54 const parserutils_vector *vector);
55static css_error handleDeclaration(css_language *c,
56 const parserutils_vector *vector);
57
58/* At-rule parsing */
59static css_error addNamespace(css_language *c,
60 lwc_string *prefix, lwc_string *uri);
61static css_error lookupNamespace(css_language *c,
62 lwc_string *prefix, lwc_string **uri);
63
64/* Selector list parsing */
65static css_error parseClass(css_language *c,
66 const parserutils_vector *vector, int32_t *ctx,
67 css_selector_detail *specific);
68static css_error parseAttrib(css_language *c,
69 const parserutils_vector *vector, int32_t *ctx,
70 css_selector_detail *specific);
71static css_error parseNth(css_language *c,
72 const parserutils_vector *vector, int32_t *ctx,
73 css_selector_detail_value *value);
74static css_error parsePseudo(css_language *c,
75 const parserutils_vector *vector, int32_t *ctx,
76 bool_Bool in_not, css_selector_detail *specific);
77static css_error parseSpecific(css_language *c,
78 const parserutils_vector *vector, int32_t *ctx,
79 bool_Bool in_not, css_selector_detail *specific);
80static css_error parseAppendSpecific(css_language *c,
81 const parserutils_vector *vector, int32_t *ctx,
82 css_selector **parent);
83static css_error parseSelectorSpecifics(css_language *c,
84 const parserutils_vector *vector, int32_t *ctx,
85 css_selector **parent);
86static css_error parseTypeSelector(css_language *c,
87 const parserutils_vector *vector, int32_t *ctx,
88 css_qname *qname);
89static css_error parseSimpleSelector(css_language *c,
90 const parserutils_vector *vector, int32_t *ctx,
91 css_selector **result);
92static css_error parseCombinator(css_language *c,
93 const parserutils_vector *vector, int32_t *ctx,
94 css_combinator *result);
95static css_error parseSelector(css_language *c,
96 const parserutils_vector *vector, int32_t *ctx,
97 css_selector **result);
98static css_error parseSelectorList(css_language *c,
99 const parserutils_vector *vector, css_rule *rule);
100
101/* Declaration parsing */
102static css_error parseProperty(css_language *c,
103 const css_token *property, const parserutils_vector *vector,
104 int32_t *ctx, css_rule *rule);
105
106/**
107 * Create a CSS language parser
108 *
109 * \param sheet The stylesheet object to parse for
110 * \param parser The core parser object to use
111 * \param language Pointer to location to receive parser object
112 * \return CSS_OK on success,
113 * CSS_BADPARM on bad parameters,
114 * CSS_NOMEM on memory exhaustion
115 */
116css_error css__language_create(css_stylesheet *sheet, css_parser *parser,
117 void **language)
118{
119 css_language *c;
120 css_parser_optparams params;
121 parserutils_error perror;
122 css_error error;
123
124 if (sheet == NULL((void*)0) || parser == NULL((void*)0) || language == NULL((void*)0))
125 return CSS_BADPARM;
126
127 c = malloc(sizeof(css_language));
128 if (c == NULL((void*)0))
129 return CSS_NOMEM;
130
131 perror = parserutils_stack_create(sizeof(context_entry),
132 STACK_CHUNK32, &c->context);
133 if (perror != PARSERUTILS_OK) {
134 free(c);
135 return css_error_from_parserutils_error(perror);
136 }
137
138 params.event_handler.handler = language_handle_event;
139 params.event_handler.pw = c;
140 error = css__parser_setopt(parser, CSS_PARSER_EVENT_HANDLER, &params);
141 if (error != CSS_OK) {
142 parserutils_stack_destroy(c->context);
143 free(c);
144 return error;
145 }
146
147 c->sheet = sheet;
148 c->state = CHARSET_PERMITTED;
149 c->default_namespace = NULL((void*)0);
150 c->namespaces = NULL((void*)0);
151 c->num_namespaces = 0;
152 c->strings = sheet->propstrings;
153
154 *language = c;
155
156 return CSS_OK;
157}
158
159/**
160 * Destroy a CSS language parser
161 *
162 * \param language The parser to destroy
163 * \return CSS_OK on success, appropriate error otherwise
164 */
165css_error css__language_destroy(css_language *language)
166{
167 uint32_t i;
168
169 if (language == NULL((void*)0))
170 return CSS_BADPARM;
171
172 if (language->default_namespace != NULL((void*)0))
173 lwc_string_unref(language->default_namespace){ lwc_string *__lwc_s = (language->default_namespace); ((__lwc_s
!= ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL"
, "src/parse/language.c", 173, __extension__ __PRETTY_FUNCTION__
)); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((
__lwc_s->refcnt == 1) && (__lwc_s->insensitive ==
__lwc_s))) lwc_string_destroy(__lwc_s); }
;
174
175 if (language->namespaces != NULL((void*)0)) {
176 for (i = 0; i < language->num_namespaces; i++) {
177 lwc_string_unref(language->namespaces[i].prefix){ lwc_string *__lwc_s = (language->namespaces[i].prefix); (
(__lwc_s != ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL"
, "src/parse/language.c", 177, __extension__ __PRETTY_FUNCTION__
)); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((
__lwc_s->refcnt == 1) && (__lwc_s->insensitive ==
__lwc_s))) lwc_string_destroy(__lwc_s); }
;
178 lwc_string_unref(language->namespaces[i].uri){ lwc_string *__lwc_s = (language->namespaces[i].uri); ((__lwc_s
!= ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL"
, "src/parse/language.c", 178, __extension__ __PRETTY_FUNCTION__
)); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((
__lwc_s->refcnt == 1) && (__lwc_s->insensitive ==
__lwc_s))) lwc_string_destroy(__lwc_s); }
;
179 }
180
181 free(language->namespaces);
182 }
183
184 parserutils_stack_destroy(language->context);
185
186 free(language);
187
188 return CSS_OK;
189}
190
191/**
192 * Handler for core parser events
193 *
194 * \param type The event type
195 * \param tokens Vector of tokens read since last event, or NULL
196 * \param pw Pointer to handler context
197 * \return CSS_OK on success, CSS_INVALID to indicate parse error,
198 * appropriate error otherwise.
199 */
200css_error language_handle_event(css_parser_event type,
201 const parserutils_vector *tokens, void *pw)
202{
203 css_language *language = (css_language *) pw;
204
205 switch (type) {
206 case CSS_PARSER_START_STYLESHEET:
207 return handleStartStylesheet(language, tokens);
208 case CSS_PARSER_END_STYLESHEET:
209 return handleEndStylesheet(language, tokens);
210 case CSS_PARSER_START_RULESET:
211 return handleStartRuleset(language, tokens);
212 case CSS_PARSER_END_RULESET:
213 return handleEndRuleset(language, tokens);
214 case CSS_PARSER_START_ATRULE:
215 return handleStartAtRule(language, tokens);
216 case CSS_PARSER_END_ATRULE:
217 return handleEndAtRule(language, tokens);
218 case CSS_PARSER_START_BLOCK:
219 return handleStartBlock(language, tokens);
220 case CSS_PARSER_END_BLOCK:
221 return handleEndBlock(language, tokens);
222 case CSS_PARSER_BLOCK_CONTENT:
223 return handleBlockContent(language, tokens);
224 case CSS_PARSER_END_BLOCK_CONTENT:
225 return handleEndBlockContent(language, tokens);
226 case CSS_PARSER_DECLARATION:
227 return handleDeclaration(language, tokens);
228 }
229
230 return CSS_OK;
231}
232
233/******************************************************************************
234 * Parser stages *
235 ******************************************************************************/
236
237css_error handleStartStylesheet(css_language *c,
238 const parserutils_vector *vector)
239{
240 parserutils_error perror;
241 context_entry entry = { CSS_PARSER_START_STYLESHEET, NULL((void*)0) };
242
243 UNUSED(vector)((void)(vector));
244
245 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"src/parse/language.c", 245, __extension__ __PRETTY_FUNCTION__
))
;
246
247 perror = parserutils_stack_push(c->context, (void *) &entry);
248 if (perror != PARSERUTILS_OK) {
249 return css_error_from_parserutils_error(perror);
250 }
251
252 return CSS_OK;
253}
254
255css_error handleEndStylesheet(css_language *c, const parserutils_vector *vector)
256{
257 parserutils_error perror;
258 context_entry *entry;
259
260 UNUSED(vector)((void)(vector));
261
262 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"src/parse/language.c", 262, __extension__ __PRETTY_FUNCTION__
))
;
263
264 entry = parserutils_stack_get_current(c->context);
265 if (entry == NULL((void*)0) || entry->type != CSS_PARSER_START_STYLESHEET)
266 return CSS_INVALID;
267
268 perror = parserutils_stack_pop(c->context, NULL((void*)0));
269 if (perror != PARSERUTILS_OK) {
270 return css_error_from_parserutils_error(perror);
271 }
272
273 return CSS_OK;
274}
275
276css_error handleStartRuleset(css_language *c, const parserutils_vector *vector)
277{
278 parserutils_error perror;
279 css_error error;
280 context_entry entry = { CSS_PARSER_START_RULESET, NULL((void*)0) };
281 context_entry *cur;
282 css_rule *parent_rule = NULL((void*)0);
283 css_rule *rule = NULL((void*)0);
284
285 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"src/parse/language.c", 285, __extension__ __PRETTY_FUNCTION__
))
;
286
287 /* Retrieve parent rule from stack, if any */
288 cur = parserutils_stack_get_current(c->context);
289 if (cur != NULL((void*)0) && cur->type != CSS_PARSER_START_STYLESHEET)
290 parent_rule = cur->data;
291
292 error = css__stylesheet_rule_create(c->sheet, CSS_RULE_SELECTOR, &rule);
293 if (error != CSS_OK)
294 return error;
295
296 if (vector != NULL((void*)0)) {
297 /* Parse selectors, if there are any */
298 error = parseSelectorList(c, vector, rule);
299 if (error != CSS_OK) {
300 css__stylesheet_rule_destroy(c->sheet, rule);
301 return error;
302 }
303 }
304
305 entry.data = rule;
306
307 perror = parserutils_stack_push(c->context, (void *) &entry);
308 if (perror != PARSERUTILS_OK) {
309 css__stylesheet_rule_destroy(c->sheet, rule);
310 return css_error_from_parserutils_error(perror);
311 }
312
313 error = css__stylesheet_add_rule(c->sheet, rule, parent_rule);
314 if (error != CSS_OK) {
315 parserutils_stack_pop(c->context, NULL((void*)0));
316 css__stylesheet_rule_destroy(c->sheet, rule);
317 return error;
318 }
319
320 /* Flag that we've had a valid rule, so @import/@namespace/@charset
321 * have no effect. */
322 c->state = HAD_RULE;
323
324 /* Rule is now owned by the sheet, so no need to destroy it */
325
326 return CSS_OK;
327}
328
329css_error handleEndRuleset(css_language *c, const parserutils_vector *vector)
330{
331 parserutils_error perror;
332 context_entry *entry;
333
334 UNUSED(vector)((void)(vector));
335
336 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"src/parse/language.c", 336, __extension__ __PRETTY_FUNCTION__
))
;
337
338 entry = parserutils_stack_get_current(c->context);
339 if (entry == NULL((void*)0) || entry->type != CSS_PARSER_START_RULESET)
340 return CSS_INVALID;
341
342 perror = parserutils_stack_pop(c->context, NULL((void*)0));
343 if (perror != PARSERUTILS_OK) {
344 return css_error_from_parserutils_error(perror);
345 }
346
347 return CSS_OK;
348}
349
350css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
351{
352 parserutils_error perror;
353 context_entry entry = { CSS_PARSER_START_ATRULE, NULL((void*)0) };
354 const css_token *token = NULL((void*)0);
355 const css_token *atkeyword = NULL((void*)0);
356 int32_t ctx = 0;
357 bool_Bool match = false0;
358 css_rule *rule;
359 css_error error;
360
361 /* vector contains: ATKEYWORD ws any0 */
362
363 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"src/parse/language.c", 363, __extension__ __PRETTY_FUNCTION__
))
;
364
365 atkeyword = parserutils_vector_iterate(vector, &ctx);
366
367 consumeWhitespace(vector, &ctx);
368
369 /* We now have an ATKEYWORD and the context for the start of any0, if
370 * there is one */
371 assert(atkeyword != NULL && atkeyword->type == CSS_TOKEN_ATKEYWORD)((atkeyword != ((void*)0) && atkeyword->type == CSS_TOKEN_ATKEYWORD
) ? (void) (0) : __assert_fail ("atkeyword != NULL && atkeyword->type == CSS_TOKEN_ATKEYWORD"
, "src/parse/language.c", 371, __extension__ __PRETTY_FUNCTION__
))
;
372
373 if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[CHARSET],({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[CHARSET]); _Bool *__lwc_ret = (&match); if (__lwc_str1->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
374 &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[CHARSET]); _Bool *__lwc_ret = (&match); if (__lwc_str1->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok && match) {
375 if (c->state == CHARSET_PERMITTED) {
376 const css_token *charset;
377
378 /* any0 = STRING */
379 if (ctx == 0)
380 return CSS_INVALID;
381
382 charset = parserutils_vector_iterate(vector, &ctx);
383 if (charset == NULL((void*)0) ||
384 charset->type != CSS_TOKEN_STRING)
385 return CSS_INVALID;
386
387 token = parserutils_vector_iterate(vector, &ctx);
388 if (token != NULL((void*)0))
389 return CSS_INVALID;
390
391 error = css__stylesheet_rule_create(c->sheet,
392 CSS_RULE_CHARSET, &rule);
393 if (error != CSS_OK)
394 return error;
395
396 error = css__stylesheet_rule_set_charset(c->sheet, rule,
397 charset->idata);
398 if (error != CSS_OK) {
399 css__stylesheet_rule_destroy(c->sheet, rule);
400 return error;
401 }
402
403 error = css__stylesheet_add_rule(c->sheet, rule, NULL((void*)0));
404 if (error != CSS_OK) {
405 css__stylesheet_rule_destroy(c->sheet, rule);
406 return error;
407 }
408
409 /* Rule is now owned by the sheet,
410 * so no need to destroy it */
411
412 c->state = IMPORT_PERMITTED;
413 } else {
414 return CSS_INVALID;
415 }
416 } else if (lwc_string_caseless_isequal(atkeyword->idata,({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[LIBCSS_IMPORT]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
417 c->strings[LIBCSS_IMPORT], &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[LIBCSS_IMPORT]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok &&
418 match) {
419 if (c->state <= IMPORT_PERMITTED) {
420 lwc_string *url;
421 css_mq_query *media = NULL((void*)0);
422
423 /* any0 = (STRING | URI) ws (media query)? */
424 const css_token *uri =
425 parserutils_vector_iterate(vector, &ctx);
426 if (uri == NULL((void*)0) || (uri->type != CSS_TOKEN_STRING &&
427 uri->type != CSS_TOKEN_URI))
428 return CSS_INVALID;
429
430 consumeWhitespace(vector, &ctx);
431
432 /* Parse media list */
433 error = css__mq_parse_media_list(
434 c->strings, vector, &ctx, &media);
435 if (error == CSS_NOMEM) {
436 return error;
437 } else if (media == NULL((void*)0)) {
438 /* Fall back to default media: "all". */
439 media = calloc(1, sizeof(*media));
440 if (media == NULL((void*)0)) {
441 return CSS_NOMEM;
442 }
443 media->type = CSS_MEDIA_ALL;
444 }
445
446 /* Create rule */
447 error = css__stylesheet_rule_create(c->sheet,
448 CSS_RULE_IMPORT, &rule);
449 if (error != CSS_OK) {
450 css__mq_query_destroy(media);
451 return error;
452 }
453
454 /* Resolve import URI */
455 error = c->sheet->resolve(c->sheet->resolve_pw,
456 c->sheet->url,
457 uri->idata, &url);
458 if (error != CSS_OK) {
459 css__stylesheet_rule_destroy(c->sheet, rule);
460 css__mq_query_destroy(media);
461 return error;
462 }
463
464 /* Inform rule of it */
465 error = css__stylesheet_rule_set_nascent_import(c->sheet,
466 rule, url, media);
467 if (error != CSS_OK) {
468 lwc_string_unref(url){ lwc_string *__lwc_s = (url); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "src/parse/language.c"
, 468, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
469 css__stylesheet_rule_destroy(c->sheet, rule);
470 css__mq_query_destroy(media);
471 return error;
472 }
473
474 /* Inform client of need for import */
475 if (c->sheet->import != NULL((void*)0)) {
476 error = c->sheet->import(c->sheet->import_pw,
477 c->sheet, url);
478 if (error != CSS_OK) {
479 lwc_string_unref(url){ lwc_string *__lwc_s = (url); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "src/parse/language.c"
, 479, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
480 css__stylesheet_rule_destroy(c->sheet,
481 rule);
482 return error;
483 }
484 }
485
486 /* No longer care about url */
487 lwc_string_unref(url){ lwc_string *__lwc_s = (url); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "src/parse/language.c"
, 487, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
--; if ((__lwc_s->refcnt == 0) || ((__lwc_s->refcnt == 1
) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy
(__lwc_s); }
;
488
489 /* Add rule to sheet */
490 error = css__stylesheet_add_rule(c->sheet, rule, NULL((void*)0));
491 if (error != CSS_OK) {
492 css__stylesheet_rule_destroy(c->sheet, rule);
493 return error;
494 }
495
496 /* Rule is now owned by the sheet,
497 * so no need to destroy it */
498
499 c->state = IMPORT_PERMITTED;
500 } else {
501 return CSS_INVALID;
502 }
503 } else if (lwc_string_caseless_isequal(atkeyword->idata,({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[NAMESPACE]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
504 c->strings[NAMESPACE], &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[NAMESPACE]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok &&
505 match) {
506 if (c->state <= NAMESPACE_PERMITTED) {
507 lwc_string *prefix = NULL((void*)0);
508
509 /* any0 = (IDENT ws)? (STRING | URI) ws */
510
511 token = parserutils_vector_iterate(vector, &ctx);
512 if (token == NULL((void*)0))
513 return CSS_INVALID;
514
515 if (token->type == CSS_TOKEN_IDENT) {
516 prefix = token->idata;
517
518 consumeWhitespace(vector, &ctx);
519
520 token = parserutils_vector_iterate(vector,
521 &ctx);
522 }
523
524 if (token == NULL((void*)0) || (token->type != CSS_TOKEN_STRING &&
525 token->type != CSS_TOKEN_URI)) {
526 return CSS_INVALID;
527 }
528
529 consumeWhitespace(vector, &ctx);
530
531 error = addNamespace(c, prefix, token->idata);
532 if (error != CSS_OK)
533 return error;
534
535 c->state = NAMESPACE_PERMITTED;
536
537 /* Namespaces are special, and do not generate rules */
538 return CSS_OK;
539 } else {
540 return CSS_INVALID;
541 }
542 } else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[MEDIA],({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[MEDIA]); _Bool *__lwc_ret = (&match); if (__lwc_str1->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
543 &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[MEDIA]); _Bool *__lwc_ret = (&match); if (__lwc_str1->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok && match) {
544 css_mq_query *media = NULL((void*)0);
545
546 /* any0 = media query */
547 error = css__mq_parse_media_list(
548 c->strings, vector, &ctx, &media);
549 if (error != CSS_OK)
550 return error;
551
552 error = css__stylesheet_rule_create(c->sheet,
553 CSS_RULE_MEDIA, &rule);
554 if (error != CSS_OK) {
555 css__mq_query_destroy(media);
556 return error;
557 }
558
559 error = css__stylesheet_rule_set_media(c->sheet, rule, media);
560 if (error != CSS_OK) {
561 css__stylesheet_rule_destroy(c->sheet, rule);
562 css__mq_query_destroy(media);
563 return error;
564 }
565
566 error = css__stylesheet_add_rule(c->sheet, rule, NULL((void*)0));
567 if (error != CSS_OK) {
568 css__stylesheet_rule_destroy(c->sheet, rule);
569 return error;
570 }
571
572 /* Rule is now owned by the sheet,
573 * so no need to destroy it */
574
575 c->state = HAD_RULE;
576 } else if (lwc_string_caseless_isequal(atkeyword->idata,({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[FONT_FACE]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
577 c->strings[FONT_FACE], &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[FONT_FACE]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok &&
578 match) {
579 error = css__stylesheet_rule_create(c->sheet,
580 CSS_RULE_FONT_FACE, &rule);
581 if (error != CSS_OK)
582 return error;
583
584 consumeWhitespace(vector, &ctx);
585
586 error = css__stylesheet_add_rule(c->sheet, rule, NULL((void*)0));
587 if (error != CSS_OK) {
588 css__stylesheet_rule_destroy(c->sheet, rule);
589 return error;
590 }
591
592 /* Rule is now owned by the sheet,
593 * so no need to destroy it */
594
595 c->state = HAD_RULE;
596 } else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[PAGE],({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[PAGE]); _Bool *__lwc_ret = (&match); if (__lwc_str1->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
597 &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (atkeyword->idata); lwc_string *__lwc_str2 = (c->strings
[PAGE]); _Bool *__lwc_ret = (&match); if (__lwc_str1->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok && match) {
598 const css_token *token;
599
600 /* any0 = (':' IDENT)? ws */
601
602 error = css__stylesheet_rule_create(c->sheet,
603 CSS_RULE_PAGE, &rule);
604 if (error != CSS_OK)
605 return error;
606
607 consumeWhitespace(vector, &ctx);
608
609 token = parserutils_vector_peek(vector, ctx);
610 if (token != NULL((void*)0)) {
611 css_selector *sel = NULL((void*)0);
612
613 error = parseSelector(c, vector, &ctx, &sel);
614 if (error != CSS_OK) {
615 css__stylesheet_rule_destroy(c->sheet, rule);
616 return error;
617 }
618
619 error = css__stylesheet_rule_set_page_selector(c->sheet,
620 rule, sel);
621 if (error != CSS_OK) {
622 css__stylesheet_selector_destroy(c->sheet, sel);
623 css__stylesheet_rule_destroy(c->sheet, rule);
624 return error;
625 }
626 }
627
628 error = css__stylesheet_add_rule(c->sheet, rule, NULL((void*)0));
629 if (error != CSS_OK) {
630 css__stylesheet_rule_destroy(c->sheet, rule);
631 return error;
632 }
633
634 /* Rule is now owned by the sheet,
635 * so no need to destroy it */
636
637 c->state = HAD_RULE;
638 } else {
639 return CSS_INVALID;
640 }
641
642 entry.data = rule;
643
644 perror = parserutils_stack_push(c->context, (void *) &entry);
645 if (perror != PARSERUTILS_OK) {
646 return css_error_from_parserutils_error(perror);
647 }
648
649 return CSS_OK;
650}
651
652css_error handleEndAtRule(css_language *c, const parserutils_vector *vector)
653{
654 parserutils_error perror;
655 context_entry *entry;
656
657 UNUSED(vector)((void)(vector));
658
659 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"src/parse/language.c", 659, __extension__ __PRETTY_FUNCTION__
))
;
660
661 entry = parserutils_stack_get_current(c->context);
662 if (entry == NULL((void*)0) || entry->type != CSS_PARSER_START_ATRULE)
663 return CSS_INVALID;
664
665 perror = parserutils_stack_pop(c->context, NULL((void*)0));
666 if (perror != PARSERUTILS_OK) {
667 return css_error_from_parserutils_error(perror);
668 }
669
670 return CSS_OK;
671}
672
673css_error handleStartBlock(css_language *c, const parserutils_vector *vector)
674{
675 parserutils_error perror;
676 context_entry entry = { CSS_PARSER_START_BLOCK, NULL((void*)0) };
677 context_entry *cur;
678
679 UNUSED(vector)((void)(vector));
680
681 /* If the current item on the stack isn't a block,
682 * then clone its data field. This ensures that the relevant rule
683 * is available when parsing the block contents. */
684 cur = parserutils_stack_get_current(c->context);
685 if (cur != NULL((void*)0) && cur->type != CSS_PARSER_START_BLOCK)
686 entry.data = cur->data;
687
688 perror = parserutils_stack_push(c->context, (void *) &entry);
689 if (perror != PARSERUTILS_OK) {
690 return css_error_from_parserutils_error(perror);
691 }
692
693 return CSS_OK;
694}
695
696css_error handleEndBlock(css_language *c, const parserutils_vector *vector)
697{
698 parserutils_error perror;
699 context_entry *entry;
700 css_rule *rule;
701
702 entry = parserutils_stack_get_current(c->context);
703 if (entry == NULL((void*)0) || entry->type != CSS_PARSER_START_BLOCK)
704 return CSS_INVALID;
705
706 rule = entry->data;
707
708 perror = parserutils_stack_pop(c->context, NULL((void*)0));
709 if (perror != PARSERUTILS_OK) {
710 return css_error_from_parserutils_error(perror);
711 }
712
713 /* If the block we just popped off the stack was associated with a
714 * non-block stack entry, and that entry is not a top-level statement,
715 * then report the end of that entry, too. */
716 if (rule != NULL((void*)0) && rule->ptype != CSS_RULE_PARENT_STYLESHEET) {
717 if (rule->type == CSS_RULE_SELECTOR)
718 return handleEndRuleset(c, vector);
719 }
720
721 return CSS_OK;
722}
723
724css_error handleBlockContent(css_language *c, const parserutils_vector *vector)
725{
726 context_entry *entry;
727 css_rule *rule;
728
729 /* Block content comprises either declarations (if the current block is
730 * associated with @page, @font-face or a selector), or rulesets (if the
731 * current block is associated with @media). */
732
733 entry = parserutils_stack_get_current(c->context);
734 if (entry == NULL((void*)0) || entry->data == NULL((void*)0))
735 return CSS_INVALID;
736
737 rule = entry->data;
738 if (rule == NULL((void*)0) || (rule->type != CSS_RULE_SELECTOR &&
739 rule->type != CSS_RULE_PAGE &&
740 rule->type != CSS_RULE_MEDIA &&
741 rule->type != CSS_RULE_FONT_FACE))
742 return CSS_INVALID;
743
744 if (rule->type == CSS_RULE_MEDIA) {
745 /* Expect rulesets */
746 return handleStartRuleset(c, vector);
747 } else {
748 /* Expect declarations */
749 return handleDeclaration(c, vector);
750 }
751
752 return CSS_OK;
753}
754
755css_error handleEndBlockContent(css_language *c, const parserutils_vector *vector)
756{
757 context_entry *entry;
758 parserutils_error perror;
759 css_error ret;
760
761 /* First we call handleBlockContent() to deal with any intermediate
762 * tokens we have left
763 */
764 ret = handleBlockContent(c, vector);
765
766 /* Our goal here is to ensure that the language parse stack is in the
767 * right state. We've encountered the end of a BlockContent such as
768 * @media ... { ... }
769 * and we need to ensure that the language stack reflects the end of
770 * that block, not any unfinished business within it such as
771 * @media ... { d }
772 */
773
774 entry = parserutils_stack_get_current(c->context);
775 while (entry != NULL((void*)0) && entry->type != CSS_PARSER_START_BLOCK) {
776 perror = parserutils_stack_pop(c->context, NULL((void*)0));
777 if (perror != PARSERUTILS_OK) {
778 return css_error_from_parserutils_error(perror);
779 }
780 entry = parserutils_stack_get_current(c->context);
781 }
782
783 return ret;
784}
785
786css_error handleDeclaration(css_language *c, const parserutils_vector *vector)
787{
788 css_error error;
789 const css_token *token, *ident;
790 int32_t ctx = 0;
791 context_entry *entry;
792 css_rule *rule;
793
794 /* Locations where declarations are permitted:
795 *
796 * + In @page
797 * + In @font-face
798 * + In ruleset
799 */
800 entry = parserutils_stack_get_current(c->context);
801 if (entry == NULL((void*)0) || entry->data == NULL((void*)0))
802 return CSS_INVALID;
803
804 rule = entry->data;
805 if (rule == NULL((void*)0) || (rule->type != CSS_RULE_SELECTOR &&
806 rule->type != CSS_RULE_PAGE &&
807 rule->type != CSS_RULE_FONT_FACE))
808 return CSS_INVALID;
809
810 /* Strip any leading whitespace (can happen if in nested block) */
811 consumeWhitespace(vector, &ctx);
812
813 /* IDENT ws ':' ws value
814 *
815 * In CSS 2.1, value is any1, so '{' or ATKEYWORD => parse error
816 */
817 ident = parserutils_vector_iterate(vector, &ctx);
818 if (ident == NULL((void*)0) || ident->type != CSS_TOKEN_IDENT)
819 return CSS_INVALID;
820
821 consumeWhitespace(vector, &ctx);
822
823 token = parserutils_vector_iterate(vector, &ctx);
824 if (token == NULL((void*)0) || tokenIsChar(token, ':') == false0)
825 return CSS_INVALID;
826
827 consumeWhitespace(vector, &ctx);
828
829 if (rule->type == CSS_RULE_FONT_FACE) {
830 css_rule_font_face * ff_rule = (css_rule_font_face *) rule;
831 error = css__parse_font_descriptor(
832 c, ident, vector, &ctx, ff_rule);
833 } else {
834 error = parseProperty(c, ident, vector, &ctx, rule);
835 }
836 if (error != CSS_OK)
837 return error;
838
839 return CSS_OK;
840}
841
842/******************************************************************************
843 * At-rule parsing functions *
844 ******************************************************************************/
845
846/**
847 * Add a namespace mapping
848 *
849 * \param c Parsing context to add to
850 * \param prefix Namespace prefix, or NULL for default namespace
851 * \param uri Namespace URI
852 * \return CSS_OK on success, CSS_NOMEM on memory exhaustion.
853 */
854css_error addNamespace(css_language *c, lwc_string *prefix, lwc_string *uri)
855{
856 if (prefix == NULL((void*)0)) {
857 /* Replace default namespace */
858 if (c->default_namespace != NULL((void*)0))
859 lwc_string_unref(c->default_namespace){ lwc_string *__lwc_s = (c->default_namespace); ((__lwc_s !=
((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL",
"src/parse/language.c", 859, __extension__ __PRETTY_FUNCTION__
)); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((
__lwc_s->refcnt == 1) && (__lwc_s->insensitive ==
__lwc_s))) lwc_string_destroy(__lwc_s); }
;
860
861 /* Special case: if new namespace uri is "", use NULL */
862 if (lwc_string_length(uri)({((uri != ((void*)0)) ? (void) (0) : __assert_fail ("uri != NULL"
, "src/parse/language.c", 862, __extension__ __PRETTY_FUNCTION__
)); (uri)->len;})
== 0)
863 c->default_namespace = NULL((void*)0);
864 else
865 c->default_namespace = lwc_string_ref(uri)({lwc_string *__lwc_s = (uri); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "src/parse/language.c"
, 865, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
++; __lwc_s;})
;
866 } else {
867 /* Replace, or add mapping */
868 bool_Bool match;
869 uint32_t idx;
870
871 for (idx = 0; idx < c->num_namespaces; idx++) {
872 if (lwc_string_isequal(c->namespaces[idx].prefix,((*(&match) = ((c->namespaces[idx].prefix) == (prefix)
)), lwc_error_ok)
873 prefix, &match)((*(&match) = ((c->namespaces[idx].prefix) == (prefix)
)), lwc_error_ok)
== lwc_error_ok &&
874 match)
875 break;
876 }
877
878 if (idx == c->num_namespaces) {
879 /* Not found, create a new mapping */
880 css_namespace *ns = realloc(c->namespaces,
881 sizeof(css_namespace) *
882 (c->num_namespaces + 1));
883
884 if (ns == NULL((void*)0))
885 return CSS_NOMEM;
886
887 ns[idx].prefix = lwc_string_ref(prefix)({lwc_string *__lwc_s = (prefix); ((__lwc_s != ((void*)0)) ? (
void) (0) : __assert_fail ("__lwc_s != NULL", "src/parse/language.c"
, 887, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
++; __lwc_s;})
;
888 ns[idx].uri = NULL((void*)0);
889
890 c->namespaces = ns;
891 c->num_namespaces++;
892 }
893
894 /* Replace namespace URI */
895 if (c->namespaces[idx].uri != NULL((void*)0))
896 lwc_string_unref(c->namespaces[idx].uri){ lwc_string *__lwc_s = (c->namespaces[idx].uri); ((__lwc_s
!= ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL"
, "src/parse/language.c", 896, __extension__ __PRETTY_FUNCTION__
)); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || ((
__lwc_s->refcnt == 1) && (__lwc_s->insensitive ==
__lwc_s))) lwc_string_destroy(__lwc_s); }
;
897
898 /* Special case: if new namespace uri is "", use NULL */
899 if (lwc_string_length(uri)({((uri != ((void*)0)) ? (void) (0) : __assert_fail ("uri != NULL"
, "src/parse/language.c", 899, __extension__ __PRETTY_FUNCTION__
)); (uri)->len;})
== 0)
900 c->namespaces[idx].uri = NULL((void*)0);
901 else
902 c->namespaces[idx].uri = lwc_string_ref(uri)({lwc_string *__lwc_s = (uri); ((__lwc_s != ((void*)0)) ? (void
) (0) : __assert_fail ("__lwc_s != NULL", "src/parse/language.c"
, 902, __extension__ __PRETTY_FUNCTION__)); __lwc_s->refcnt
++; __lwc_s;})
;
903 }
904
905 return CSS_OK;
906}
907
908/**
909 * Look up a namespace prefix
910 *
911 * \param c Language parser context
912 * \param prefix Namespace prefix to find, or NULL for none
913 * \param uri Pointer to location to receive namespace URI
914 * \return CSS_OK on success, CSS_INVALID if prefix is not found
915 */
916css_error lookupNamespace(css_language *c, lwc_string *prefix, lwc_string **uri)
917{
918 uint32_t idx;
919 bool_Bool match;
920
921 if (prefix == NULL((void*)0)) {
922 *uri = NULL((void*)0);
923 } else {
924 for (idx = 0; idx < c->num_namespaces; idx++) {
925 if (lwc_string_isequal(c->namespaces[idx].prefix,((*(&match) = ((c->namespaces[idx].prefix) == (prefix)
)), lwc_error_ok)
926 prefix, &match)((*(&match) = ((c->namespaces[idx].prefix) == (prefix)
)), lwc_error_ok)
== lwc_error_ok &&
927 match)
928 break;
929 }
930
931 if (idx == c->num_namespaces)
932 return CSS_INVALID;
933
934 *uri = c->namespaces[idx].uri;
935 }
936
937 return CSS_OK;
938}
939
940/******************************************************************************
941 * Selector list parsing functions *
942 ******************************************************************************/
943
944css_error parseClass(css_language *c, const parserutils_vector *vector,
945 int32_t *ctx, css_selector_detail *specific)
946{
947 css_qname qname;
948 css_selector_detail_value detail_value;
949 const css_token *token;
950
951 /* class -> '.' IDENT */
952 token = parserutils_vector_iterate(vector, ctx);
953 if (token == NULL((void*)0) || tokenIsChar(token, '.') == false0)
954 return CSS_INVALID;
955
956 token = parserutils_vector_iterate(vector, ctx);
957 if (token == NULL((void*)0) || token->type != CSS_TOKEN_IDENT)
958 return CSS_INVALID;
959
960 detail_value.string = NULL((void*)0);
961
962 qname.ns = NULL((void*)0);
963 qname.name = token->idata;
964
965 /* Ensure lwc insensitive string is available for class names */
966 if (qname.name->insensitive == NULL((void*)0) &&
967 lwc__intern_caseless_string(qname.name) != lwc_error_ok)
968 return CSS_NOMEM;
969
970 return css__stylesheet_selector_detail_init(c->sheet,
971 CSS_SELECTOR_CLASS, &qname, detail_value,
972 CSS_SELECTOR_DETAIL_VALUE_STRING, false0, specific);
973}
974
975css_error parseAttrib(css_language *c, const parserutils_vector *vector,
976 int32_t *ctx, css_selector_detail *specific)
977{
978 css_qname qname;
979 css_selector_detail_value detail_value;
980 const css_token *token, *value = NULL((void*)0);
981 css_selector_type type = CSS_SELECTOR_ATTRIBUTE;
982 css_error error;
983 lwc_string *prefix = NULL((void*)0);
984
985 /* attrib -> '[' ws namespace_prefix? IDENT ws [
986 * [ '=' |
987 * INCLUDES |
988 * DASHMATCH |
989 * PREFIXMATCH |
990 * SUFFIXMATCH |
991 * SUBSTRINGMATCH
992 * ] ws
993 * [ IDENT | STRING ] ws ]? ']'
994 * namespace_prefix -> [ IDENT | '*' ]? '|'
995 */
996 token = parserutils_vector_iterate(vector, ctx);
997 if (token == NULL((void*)0) || tokenIsChar(token, '[') == false0)
998 return CSS_INVALID;
999
1000 consumeWhitespace(vector, ctx);
1001
1002 token = parserutils_vector_iterate(vector, ctx);
1003 if (token == NULL((void*)0) || (token->type != CSS_TOKEN_IDENT &&
1004 tokenIsChar(token, '*') == false0 &&
1005 tokenIsChar(token, '|') == false0))
1006 return CSS_INVALID;
1007
1008 if (tokenIsChar(token, '|')) {
1009 token = parserutils_vector_iterate(vector, ctx);
1010 } else {
1011 const css_token *temp;
1012
1013 temp = parserutils_vector_peek(vector, *ctx);
1014 if (temp != NULL((void*)0) && tokenIsChar(temp, '|')) {
1015 prefix = token->idata;
1016
1017 parserutils_vector_iterate(vector, ctx);
1018
1019 token = parserutils_vector_iterate(vector, ctx);
1020 }
1021 }
1022
1023 if (token == NULL((void*)0) || token->type != CSS_TOKEN_IDENT)
1024 return CSS_INVALID;
1025
1026 error = lookupNamespace(c, prefix, &qname.ns);
1027 if (error != CSS_OK)
1028 return error;
1029
1030 qname.name = token->idata;
1031
1032 consumeWhitespace(vector, ctx);
1033
1034 token = parserutils_vector_iterate(vector, ctx);
1035 if (token == NULL((void*)0))
1036 return CSS_INVALID;
1037
1038 if (tokenIsChar(token, ']') == false0) {
1039 if (tokenIsChar(token, '='))
1040 type = CSS_SELECTOR_ATTRIBUTE_EQUAL;
1041 else if (token->type == CSS_TOKEN_INCLUDES)
1042 type = CSS_SELECTOR_ATTRIBUTE_INCLUDES;
1043 else if (token->type == CSS_TOKEN_DASHMATCH)
1044 type = CSS_SELECTOR_ATTRIBUTE_DASHMATCH;
1045 else if (token->type == CSS_TOKEN_PREFIXMATCH)
1046 type = CSS_SELECTOR_ATTRIBUTE_PREFIX;
1047 else if (token->type == CSS_TOKEN_SUFFIXMATCH)
1048 type = CSS_SELECTOR_ATTRIBUTE_SUFFIX;
1049 else if (token->type == CSS_TOKEN_SUBSTRINGMATCH)
1050 type = CSS_SELECTOR_ATTRIBUTE_SUBSTRING;
1051 else
1052 return CSS_INVALID;
1053
1054 consumeWhitespace(vector, ctx);
1055
1056 token = parserutils_vector_iterate(vector, ctx);
1057 if (token == NULL((void*)0) || (token->type != CSS_TOKEN_IDENT &&
1058 token->type != CSS_TOKEN_STRING))
1059 return CSS_INVALID;
1060
1061 value = token;
1062
1063 consumeWhitespace(vector, ctx);
1064
1065 token = parserutils_vector_iterate(vector, ctx);
1066 if (token == NULL((void*)0) || tokenIsChar(token, ']') == false0)
1067 return CSS_INVALID;
1068 }
1069
1070 detail_value.string = value != NULL((void*)0) ? value->idata : NULL((void*)0);
1071
1072 return css__stylesheet_selector_detail_init(c->sheet, type,
1073 &qname, detail_value,
1074 CSS_SELECTOR_DETAIL_VALUE_STRING, false0, specific);
1075}
1076
1077css_error parseNth(css_language *c,
1078 const parserutils_vector *vector, int32_t *ctx,
1079 css_selector_detail_value *value)
1080{
1081 const css_token *token;
1082 bool_Bool match;
1083
1084 /* nth -> [ DIMENSION | IDENT ] ws [ [ CHAR ws ]? NUMBER ws ]?
1085 * (e.g. DIMENSION: 2n-1, 2n- 1, 2n -1, 2n - 1)
1086 * (e.g. IDENT: -n-1, -n- 1, -n -1, -n - 1)
1087 * -> NUMBER ws
1088 * -> IDENT(odd) ws
1089 * -> IDENT(even) ws
1090 */
1091
1092 token = parserutils_vector_iterate(vector, ctx);
1093 if (token == NULL((void*)0) || (token->type != CSS_TOKEN_IDENT &&
1094 token->type != CSS_TOKEN_NUMBER &&
1095 token->type != CSS_TOKEN_DIMENSION))
1096 return CSS_INVALID;
1097
1098 if (token->type == CSS_TOKEN_IDENT &&
1099 lwc_string_caseless_isequal(token->idata,({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (token->idata); lwc_string *__lwc_str2 = (c->strings
[ODD]); _Bool *__lwc_ret = (&match); if (__lwc_str1->insensitive
== ((void*)0)) { __lwc_err = lwc__intern_caseless_string(__lwc_str1
); } if (__lwc_err == lwc_error_ok && __lwc_str2->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
1100 c->strings[ODD], &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (token->idata); lwc_string *__lwc_str2 = (c->strings
[ODD]); _Bool *__lwc_ret = (&match); if (__lwc_str1->insensitive
== ((void*)0)) { __lwc_err = lwc__intern_caseless_string(__lwc_str1
); } if (__lwc_err == lwc_error_ok && __lwc_str2->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok &&
1101 match) {
1102 /* Odd */
1103 value->nth.a = 2;
1104 value->nth.b = 1;
1105 } else if (token->type == CSS_TOKEN_IDENT &&
1106 lwc_string_caseless_isequal(token->idata,({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (token->idata); lwc_string *__lwc_str2 = (c->strings
[EVEN]); _Bool *__lwc_ret = (&match); if (__lwc_str1->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
1107 c->strings[EVEN], &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (token->idata); lwc_string *__lwc_str2 = (c->strings
[EVEN]); _Bool *__lwc_ret = (&match); if (__lwc_str1->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok &&
1108 match) {
1109 /* Even */
1110 value->nth.a = 2;
1111 value->nth.b = 0;
1112 } else if (token->type == CSS_TOKEN_NUMBER) {
1113 size_t consumed = 0;
1114 css_fixed val = 0;
1115
1116 val = css__number_from_lwc_string(token->idata,
1117 true1, &consumed);
1118 if (consumed != lwc_string_length(token->idata)({((token->idata != ((void*)0)) ? (void) (0) : __assert_fail
("token->idata != NULL", "src/parse/language.c", 1118, __extension__
__PRETTY_FUNCTION__)); (token->idata)->len;})
)
1119 return CSS_INVALID;
1120
1121 value->nth.a = 0;
1122 value->nth.b = FIXTOINT(val)((val) >> 10);
1123 } else {
1124 /* [ DIMENSION | IDENT ] ws [ [ CHAR ws ]? NUMBER ws ]?
1125 *
1126 * (e.g. DIMENSION: 2n-1, 2n- 1, 2n -1, 2n - 1)
1127 * (e.g. IDENT: n, -n-1, -n- 1, -n -1, -n - 1)
1128 */
1129 size_t consumed = 0, len;
1130 const char *data;
1131 css_fixed a = 0, b = 0;
1132 int sign = 1;
1133 bool_Bool had_sign = false0, had_b = false0;
1134
1135 len = lwc_string_length(token->idata)({((token->idata != ((void*)0)) ? (void) (0) : __assert_fail
("token->idata != NULL", "src/parse/language.c", 1135, __extension__
__PRETTY_FUNCTION__)); (token->idata)->len;})
;
1136 data = lwc_string_data(token->idata)({((token->idata != ((void*)0)) ? (void) (0) : __assert_fail
("token->idata != NULL", "src/parse/language.c", 1136, __extension__
__PRETTY_FUNCTION__)); (const char *)((token->idata)+1);}
)
;
1137
1138 /* Compute a */
1139 if (token->type == CSS_TOKEN_IDENT) {
1140 if (len < 2) {
1141 if (data[0] != 'n' && data[0] != 'N')
1142 return CSS_INVALID;
1143
1144 /* n */
1145 a = INTTOFIX(1)(css_int_to_fixed(1));
1146
1147 data += 1;
1148 len -= 1;
1149 } else {
1150 if (data[0] != '-' ||
1151 (data[1] != 'n' && data[1] != 'N'))
1152 return CSS_INVALID;
1153
1154 /* -n */
1155 a = INTTOFIX(-1)(css_int_to_fixed(-1));
1156
1157 data += 2;
1158 len -= 2;
1159 }
1160
1161 if (len > 0) {
1162 if (data[0] != '-')
1163 return CSS_INVALID;
1164
1165 /* -n- */
1166 sign = -1;
1167 had_sign = true1;
1168
1169 if (len > 1) {
1170 /* Reject additional sign */
1171 if (data[1] == '-' || data[1] == '+')
1172 return CSS_INVALID;
1173
1174 /* -n-b */
1175 b = css__number_from_string(
1176 (const uint8_t *) data + 1,
1177 len - 1,
1178 true1,
1179 &consumed);
1180 if (consumed != len - 1)
1181 return CSS_INVALID;
1182
1183 had_b = true1;
1184 }
1185 }
1186 } else {
1187 /* 2n */
1188 a = css__number_from_lwc_string(token->idata,
1189 true1, &consumed);
1190 if (consumed == 0 || (data[consumed] != 'n' &&
1191 data[consumed] != 'N'))
1192 return CSS_INVALID;
1193
1194 if (++consumed < len) {
1195 if (data[consumed] != '-')
1196 return CSS_INVALID;
1197
1198 /* 2n- */
1199 sign = -1;
1200 had_sign = true1;
1201
1202 if (++consumed < len) {
1203 size_t bstart;
1204
1205 /* Reject additional sign */
1206 if (data[consumed] == '-' ||
1207 data[consumed] == '+')
1208 return CSS_INVALID;
1209
1210 /* 2n-b */
1211 bstart = consumed;
1212
1213 b = css__number_from_string(
1214 (const uint8_t *) data + bstart,
1215 len - bstart,
1216 true1,
1217 &consumed);
1218 if (consumed != len - bstart)
1219 return CSS_INVALID;
1220
1221 had_b = true1;
1222 }
1223 }
1224 }
1225
1226 if (had_b == false0) {
1227 consumeWhitespace(vector, ctx);
1228
1229 /* Look for optional b : [ [ CHAR ws ]? NUMBER ws ]? */
1230 token = parserutils_vector_peek(vector, *ctx);
1231
1232 if (had_sign == false0 && token != NULL((void*)0) &&
1233 (tokenIsChar(token, '-') ||
1234 tokenIsChar(token, '+'))) {
1235 parserutils_vector_iterate(vector, ctx);
1236
1237 had_sign = true1;
1238
1239 if (tokenIsChar(token, '-'))
1240 sign = -1;
1241
1242 consumeWhitespace(vector, ctx);
1243
1244 token = parserutils_vector_peek(vector, *ctx);
1245 }
1246
1247 /* Expect NUMBER */
1248 if (token != NULL((void*)0) && token->type == CSS_TOKEN_NUMBER) {
1249 parserutils_vector_iterate(vector, ctx);
1250
1251 /* If we've already seen a sign, ensure one
1252 * does not occur at the start of this token
1253 */
1254 if (had_sign && lwc_string_length(({((token->idata != ((void*)0)) ? (void) (0) : __assert_fail
("token->idata != NULL", "src/parse/language.c", 1255, __extension__
__PRETTY_FUNCTION__)); (token->idata)->len;})
1255 token->idata)({((token->idata != ((void*)0)) ? (void) (0) : __assert_fail
("token->idata != NULL", "src/parse/language.c", 1255, __extension__
__PRETTY_FUNCTION__)); (token->idata)->len;})
> 0) {
1256 data = lwc_string_data(token->idata)({((token->idata != ((void*)0)) ? (void) (0) : __assert_fail
("token->idata != NULL", "src/parse/language.c", 1256, __extension__
__PRETTY_FUNCTION__)); (const char *)((token->idata)+1);}
)
;
1257
1258 if (data[0] == '-' || data[0] == '+')
1259 return CSS_INVALID;
1260 }
1261
1262 b = css__number_from_lwc_string(token->idata,
1263 true1, &consumed);
1264 if (consumed != lwc_string_length(token->idata)({((token->idata != ((void*)0)) ? (void) (0) : __assert_fail
("token->idata != NULL", "src/parse/language.c", 1264, __extension__
__PRETTY_FUNCTION__)); (token->idata)->len;})
)
1265 return CSS_INVALID;
1266 }
1267 }
1268
1269 value->nth.a = FIXTOINT(a)((a) >> 10);
1270 value->nth.b = FIXTOINT(b)((b) >> 10) * sign;
1271 }
1272
1273 consumeWhitespace(vector, ctx);
1274
1275 return CSS_OK;
1276}
1277
1278css_error parsePseudo(css_language *c, const parserutils_vector *vector,
1279 int32_t *ctx, bool_Bool in_not, css_selector_detail *specific)
1280{
1281 static const struct
1282 {
1283 int index;
1284 css_selector_type type;
1285 } pseudo_lut[] = {
1286 { FIRST_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
1287 { LINK, CSS_SELECTOR_PSEUDO_CLASS },
1288 { VISITED, CSS_SELECTOR_PSEUDO_CLASS },
1289 { HOVER, CSS_SELECTOR_PSEUDO_CLASS },
1290 { ACTIVE, CSS_SELECTOR_PSEUDO_CLASS },
1291 { FOCUS, CSS_SELECTOR_PSEUDO_CLASS },
1292 { LANG, CSS_SELECTOR_PSEUDO_CLASS },
1293 { LEFT, CSS_SELECTOR_PSEUDO_CLASS },
1294 { RIGHT, CSS_SELECTOR_PSEUDO_CLASS },
1295 { FIRST, CSS_SELECTOR_PSEUDO_CLASS },
1296 { ROOT, CSS_SELECTOR_PSEUDO_CLASS },
1297 { NTH_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
1298 { NTH_LAST_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
1299 { NTH_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
1300 { NTH_LAST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
1301 { LAST_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
1302 { FIRST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
1303 { LAST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
1304 { ONLY_CHILD, CSS_SELECTOR_PSEUDO_CLASS },
1305 { ONLY_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS },
1306 { EMPTY, CSS_SELECTOR_PSEUDO_CLASS },
1307 { TARGET, CSS_SELECTOR_PSEUDO_CLASS },
1308 { ENABLED, CSS_SELECTOR_PSEUDO_CLASS },
1309 { DISABLED, CSS_SELECTOR_PSEUDO_CLASS },
1310 { CHECKED, CSS_SELECTOR_PSEUDO_CLASS },
1311 { NOT, CSS_SELECTOR_PSEUDO_CLASS },
1312
1313 { FIRST_LINE, CSS_SELECTOR_PSEUDO_ELEMENT },
1314 { FIRST_LETTER, CSS_SELECTOR_PSEUDO_ELEMENT },
1315 { BEFORE, CSS_SELECTOR_PSEUDO_ELEMENT },
1316 { AFTER, CSS_SELECTOR_PSEUDO_ELEMENT }
1317 };
1318 css_selector_detail_value detail_value;
1319 css_selector_detail_value_type value_type =
1320 CSS_SELECTOR_DETAIL_VALUE_STRING;
1321 css_qname qname;
1322 const css_token *token;
1323 bool_Bool match = false0, require_element = false0, negate = false0;
1324 uint32_t lut_idx;
1325 css_selector_type type = CSS_SELECTOR_PSEUDO_CLASS;/* GCC's braindead */
1326 css_error error;
1327
1328 /* pseudo -> ':' ':'? [ IDENT | FUNCTION ws any1 ws ')' ] */
1329
1330 detail_value.string = NULL((void*)0);
1331
1332 token = parserutils_vector_iterate(vector, ctx);
1333 if (token == NULL((void*)0) || tokenIsChar(token, ':') == false0)
1334 return CSS_INVALID;
1335
1336 /* Optional second colon before pseudo element names */
1337 token = parserutils_vector_iterate(vector, ctx);
1338 if (token != NULL((void*)0) && tokenIsChar(token, ':')) {
1339 /* If present, we require a pseudo element */
1340 require_element = true1;
1341
1342 /* Consume subsequent token */
1343 token = parserutils_vector_iterate(vector, ctx);
1344 }
1345
1346 /* Expect IDENT or FUNCTION */
1347 if (token == NULL((void*)0) || (token->type != CSS_TOKEN_IDENT &&
1348 token->type != CSS_TOKEN_FUNCTION))
1349 return CSS_INVALID;
1350
1351 qname.ns = NULL((void*)0);
1352 qname.name = token->idata;
1353
1354 /* Search lut for selector type */
1355 for (lut_idx = 0; lut_idx < N_ELEMENTS(pseudo_lut)(sizeof((pseudo_lut)) / sizeof((pseudo_lut)[0])); lut_idx++) {
1356 if ((lwc_string_caseless_isequal(qname.name,({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (qname.name); lwc_string *__lwc_str2 = (c->strings[pseudo_lut
[lut_idx].index]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
1357 c->strings[pseudo_lut[lut_idx].index],({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (qname.name); lwc_string *__lwc_str2 = (c->strings[pseudo_lut
[lut_idx].index]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
1358 &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (qname.name); lwc_string *__lwc_str2 = (c->strings[pseudo_lut
[lut_idx].index]); _Bool *__lwc_ret = (&match); if (__lwc_str1
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str1); } if (__lwc_err == lwc_error_ok && __lwc_str2
->insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok) && match) {
1359 type = pseudo_lut[lut_idx].type;
1360 break;
1361 }
1362 }
1363
1364 /* Not found: invalid */
1365 if (lut_idx == N_ELEMENTS(pseudo_lut)(sizeof((pseudo_lut)) / sizeof((pseudo_lut)[0])))
1366 return CSS_INVALID;
1367
1368 /* Required a pseudo element, but didn't find one: invalid */
1369 if (require_element && type != CSS_SELECTOR_PSEUDO_ELEMENT)
1370 return CSS_INVALID;
1371
1372 /* :not() and pseudo elements are not permitted in :not() */
1373 if (in_not && (type == CSS_SELECTOR_PSEUDO_ELEMENT ||
1374 pseudo_lut[lut_idx].index == NOT))
1375 return CSS_INVALID;
1376
1377 if (token->type == CSS_TOKEN_FUNCTION) {
1378 int fun_type = pseudo_lut[lut_idx].index;
1379
1380 consumeWhitespace(vector, ctx);
1381
1382 if (fun_type == LANG) {
1383 /* IDENT */
1384 token = parserutils_vector_iterate(vector, ctx);
1385 if (token == NULL((void*)0) || token->type != CSS_TOKEN_IDENT)
1386 return CSS_INVALID;
1387
1388 detail_value.string = token->idata;
1389 value_type = CSS_SELECTOR_DETAIL_VALUE_STRING;
1390
1391 consumeWhitespace(vector, ctx);
1392 } else if (fun_type == NTH_CHILD ||
1393 fun_type == NTH_LAST_CHILD ||
1394 fun_type == NTH_OF_TYPE ||
1395 fun_type == NTH_LAST_OF_TYPE) {
1396 /* an + b */
1397 error = parseNth(c, vector, ctx, &detail_value);
1398 if (error != CSS_OK)
1399 return error;
1400
1401 value_type = CSS_SELECTOR_DETAIL_VALUE_NTH;
1402 } else if (fun_type == NOT) {
1403 /* type_selector | specific */
1404 token = parserutils_vector_peek(vector, *ctx);
1405 if (token == NULL((void*)0))
1406 return CSS_INVALID;
1407
1408 if (token->type == CSS_TOKEN_IDENT ||
1409 tokenIsChar(token, '*') ||
1410 tokenIsChar(token, '|')) {
1411 /* Have type selector */
1412 error = parseTypeSelector(c, vector, ctx,
1413 &qname);
1414 if (error != CSS_OK)
1415 return error;
1416
1417 type = CSS_SELECTOR_ELEMENT;
1418
1419 /* Ensure lwc insensitive string is available
1420 * for element names */
1421 if (qname.name->insensitive == NULL((void*)0) &&
1422 lwc__intern_caseless_string(
1423 qname.name) != lwc_error_ok)
1424 return CSS_NOMEM;
1425
1426 detail_value.string = NULL((void*)0);
1427 value_type = CSS_SELECTOR_DETAIL_VALUE_STRING;
1428 } else {
1429 /* specific */
1430 css_selector_detail det;
1431
1432 error = parseSpecific(c, vector, ctx, true1,
1433 &det);
1434 if (error != CSS_OK)
1435 return error;
1436
1437 qname = det.qname;
1438 type = det.type;
1439 detail_value = det.value;
1440 value_type = det.value_type;
1441 }
1442
1443 negate = true1;
1444
1445 consumeWhitespace(vector, ctx);
1446 }
1447
1448 token = parserutils_vector_iterate(vector, ctx);
1449 if (token == NULL((void*)0) || tokenIsChar(token, ')') == false0)
1450 return CSS_INVALID;
1451 }
1452
1453 return css__stylesheet_selector_detail_init(c->sheet,
1454 type, &qname, detail_value, value_type,
1455 negate, specific);
1456}
1457
1458css_error parseSpecific(css_language *c,
1459 const parserutils_vector *vector, int32_t *ctx,
1460 bool_Bool in_not, css_selector_detail *specific)
1461{
1462 css_error error;
1463 const css_token *token;
1464
1465 /* specific -> [ HASH | class | attrib | pseudo ] */
1466
1467 token = parserutils_vector_peek(vector, *ctx);
1468 if (token == NULL((void*)0))
1469 return CSS_INVALID;
1470
1471 if (token->type == CSS_TOKEN_HASH) {
1472 css_qname qname;
1473 css_selector_detail_value detail_value;
1474
1475 detail_value.string = NULL((void*)0);
1476
1477 qname.ns = NULL((void*)0);
1478 qname.name = token->idata;
1479
1480 /* Ensure lwc insensitive string is available for id names */
1481 if (qname.name->insensitive == NULL((void*)0) &&
1482 lwc__intern_caseless_string(
1483 qname.name) != lwc_error_ok)
1484 return CSS_NOMEM;
1485
1486 error = css__stylesheet_selector_detail_init(c->sheet,
1487 CSS_SELECTOR_ID, &qname, detail_value,
1488 CSS_SELECTOR_DETAIL_VALUE_STRING, false0,
1489 specific);
1490 if (error != CSS_OK)
1491 return error;
1492
1493 parserutils_vector_iterate(vector, ctx);
1494 } else if (tokenIsChar(token, '.')) {
1495 error = parseClass(c, vector, ctx, specific);
1496 if (error != CSS_OK)
1497 return error;
1498 } else if (tokenIsChar(token, '[')) {
1499 error = parseAttrib(c, vector, ctx, specific);
1500 if (error != CSS_OK)
1501 return error;
1502 } else if (tokenIsChar(token, ':')) {
1503 error = parsePseudo(c, vector, ctx, in_not, specific);
1504 if (error != CSS_OK)
1505 return error;
1506 } else {
1507 return CSS_INVALID;
1508 }
1509
1510 return CSS_OK;
1511}
1512
1513css_error parseAppendSpecific(css_language *c,
1514 const parserutils_vector *vector, int32_t *ctx,
1515 css_selector **parent)
1516{
1517 css_error error;
1518 css_selector_detail specific;
1519
1520 error = parseSpecific(c, vector, ctx, false0, &specific);
1521 if (error != CSS_OK)
1522 return error;
1523
1524 return css__stylesheet_selector_append_specific(c->sheet, parent,
1525 &specific);
1526}
1527
1528css_error parseSelectorSpecifics(css_language *c,
1529 const parserutils_vector *vector, int32_t *ctx,
1530 css_selector **parent)
1531{
1532 css_error error;
1533 const css_token *token;
1534
1535 /* specifics -> specific* */
1536 while ((token = parserutils_vector_peek(vector, *ctx)) != NULL((void*)0) &&
1537 token->type != CSS_TOKEN_S &&
1538 tokenIsChar(token, '+') == false0 &&
1539 tokenIsChar(token, '>') == false0 &&
1540 tokenIsChar(token, '~') == false0 &&
1541 tokenIsChar(token, ',') == false0) {
1542 error = parseAppendSpecific(c, vector, ctx, parent);
1543 if (error != CSS_OK)
1544 return error;
1545 }
1546
1547 return CSS_OK;
1548}
1549
1550css_error parseTypeSelector(css_language *c, const parserutils_vector *vector,
1
[debug] analyzing from parseTypeSelector
1551 int32_t *ctx, css_qname *qname)
1552{
1553 const css_token *token;
1554 css_error error;
1555 lwc_string *prefix = NULL((void*)0);
2
'prefix' initialized to a null pointer value
1556
1557 /* type_selector -> namespace_prefix? element_name
1558 * namespace_prefix -> [ IDENT | '*' ]? '|'
1559 * element_name -> IDENT | '*'
1560 */
1561
1562 token = parserutils_vector_peek(vector, *ctx);
1563 if (token == NULL((void*)0))
3
Assuming 'token' is not equal to NULL
4
Taking false branch
1564 return CSS_INVALID;
1565
1566 if (tokenIsChar(token, '|') == false0) {
5
Assuming the condition is false
1567 prefix = token->idata;
1568
1569 parserutils_vector_iterate(vector, ctx);
1570
1571 token = parserutils_vector_peek(vector, *ctx);
1572 }
1573
1574 if (token
5.1
'token' is not equal to NULL
!= NULL((void*)0) && tokenIsChar(token, '|')) {
6
Assuming the condition is false
7
Taking false branch
1575 /* Have namespace prefix */
1576 parserutils_vector_iterate(vector, ctx);
1577
1578 /* Expect element_name */
1579 token = parserutils_vector_iterate(vector, ctx);
1580
1581 if (token == NULL((void*)0) || (token->type != CSS_TOKEN_IDENT &&
1582 tokenIsChar(token, '*') == false0)) {
1583 return CSS_INVALID;
1584 }
1585
1586 error = lookupNamespace(c, prefix, &qname->ns);
1587 if (error != CSS_OK)
1588 return error;
1589
1590 qname->name = token->idata;
1591 } else {
1592 /* No namespace prefix */
1593 if (c->default_namespace == NULL((void*)0)) {
8
Assuming field 'default_namespace' is not equal to NULL
9
Taking false branch
1594 qname->ns = c->strings[UNIVERSAL];
1595 } else {
1596 qname->ns = c->default_namespace;
1597 }
1598
1599 qname->name = prefix;
10
Null pointer value stored to field 'name'
1600 }
1601
1602 /* Ensure lwc insensitive string is available for element names */
1603 if (qname->name->insensitive == NULL((void*)0) &&
11
Access to field 'insensitive' results in a dereference of a null pointer (loaded from field 'name')
1604 lwc__intern_caseless_string(
1605 qname->name) != lwc_error_ok)
1606 return CSS_NOMEM;
1607
1608 return CSS_OK;
1609}
1610
1611css_error parseSimpleSelector(css_language *c,
1612 const parserutils_vector *vector, int32_t *ctx,
1613 css_selector **result)
1614{
1615 int32_t orig_ctx = *ctx;
1616 css_error error;
1617 const css_token *token;
1618 css_selector *selector;
1619 css_qname qname;
1620
1621 /* simple_selector -> type_selector specifics
1622 * -> specific specifics
1623 */
1624
1625 token = parserutils_vector_peek(vector, *ctx);
1626 if (token == NULL((void*)0))
1627 return CSS_INVALID;
1628
1629 if (token->type == CSS_TOKEN_IDENT || tokenIsChar(token, '*') ||
1630 tokenIsChar(token, '|')) {
1631 /* Have type selector */
1632 error = parseTypeSelector(c, vector, ctx, &qname);
1633 if (error != CSS_OK) {
1634 *ctx = orig_ctx;
1635 return error;
1636 }
1637
1638 error = css__stylesheet_selector_create(c->sheet,
1639 &qname, &selector);
1640 if (error != CSS_OK) {
1641 *ctx = orig_ctx;
1642 return error;
1643 }
1644 } else {
1645 /* Universal selector */
1646 if (c->default_namespace == NULL((void*)0))
1647 qname.ns = c->strings[UNIVERSAL];
1648 else
1649 qname.ns = c->default_namespace;
1650
1651 qname.name = c->strings[UNIVERSAL];
1652
1653 error = css__stylesheet_selector_create(c->sheet,
1654 &qname, &selector);
1655 if (error != CSS_OK)
1656 return error;
1657
1658 /* Ensure we have at least one specific selector */
1659 error = parseAppendSpecific(c, vector, ctx, &selector);
1660 if (error != CSS_OK) {
1661 css__stylesheet_selector_destroy(c->sheet, selector);
1662 return error;
1663 }
1664 }
1665
1666 error = parseSelectorSpecifics(c, vector, ctx, &selector);
1667 if (error != CSS_OK) {
1668 css__stylesheet_selector_destroy(c->sheet, selector);
1669 return error;
1670 }
1671
1672 *result = selector;
1673
1674 return CSS_OK;
1675}
1676
1677css_error parseCombinator(css_language *c, const parserutils_vector *vector,
1678 int32_t *ctx, css_combinator *result)
1679{
1680 const css_token *token;
1681 css_combinator comb = CSS_COMBINATOR_NONE;
1682
1683 /* combinator -> ws '+' ws | ws '>' ws | ws '~' ws | ws1 */
1684
1685 UNUSED(c)((void)(c));
1686
1687 while ((token = parserutils_vector_peek(vector, *ctx)) != NULL((void*)0)) {
1688 if (tokenIsChar(token, '+'))
1689 comb = CSS_COMBINATOR_SIBLING;
1690 else if (tokenIsChar(token, '>'))
1691 comb = CSS_COMBINATOR_PARENT;
1692 else if (tokenIsChar(token, '~'))
1693 comb = CSS_COMBINATOR_GENERIC_SIBLING;
1694 else if (token->type == CSS_TOKEN_S)
1695 comb = CSS_COMBINATOR_ANCESTOR;
1696 else
1697 break;
1698
1699 parserutils_vector_iterate(vector, ctx);
1700
1701 /* If we've seen a '+', '>', or '~', we're done. */
1702 if (comb != CSS_COMBINATOR_ANCESTOR)
1703 break;
1704 }
1705
1706 /* No valid combinator found */
1707 if (comb == CSS_COMBINATOR_NONE)
1708 return CSS_INVALID;
1709
1710 /* Consume any trailing whitespace */
1711 consumeWhitespace(vector, ctx);
1712
1713 *result = comb;
1714
1715 return CSS_OK;
1716}
1717
1718css_error parseSelector(css_language *c, const parserutils_vector *vector,
1719 int32_t *ctx, css_selector **result)
1720{
1721 css_error error;
1722 const css_token *token = NULL((void*)0);
1723 css_selector *selector = NULL((void*)0);
1724
1725 /* selector -> simple_selector [ combinator simple_selector ]* ws
1726 *
1727 * Note, however, that, as combinator can be wholly whitespace,
1728 * there's an ambiguity as to whether "ws" has been reached. We
1729 * resolve this by attempting to extract a combinator, then
1730 * recovering when we detect that we've reached the end of the
1731 * selector.
1732 */
1733
1734 error = parseSimpleSelector(c, vector, ctx, &selector);
1735 if (error != CSS_OK)
1736 return error;
1737
1738 while ((token = parserutils_vector_peek(vector, *ctx)) != NULL((void*)0) &&
1739 tokenIsChar(token, ',') == false0) {
1740 css_combinator comb = CSS_COMBINATOR_NONE;
1741 css_selector *other = NULL((void*)0);
1742
1743 error = parseCombinator(c, vector, ctx, &comb);
1744 if (error != CSS_OK) {
1745 css__stylesheet_selector_destroy(c->sheet, selector);
1746 return error;
1747 }
1748
1749 /* In the case of "html , body { ... }", the whitespace after
1750 * "html" and "body" will be considered an ancestor combinator.
1751 * This clearly is not the case, however. Therefore, as a
1752 * special case, if we've got an ancestor combinator and there
1753 * are no further tokens, or if the next token is a comma,
1754 * we ignore the supposed combinator and continue. */
1755 if (comb == CSS_COMBINATOR_ANCESTOR &&
1756 ((token = parserutils_vector_peek(vector,
1757 *ctx)) == NULL((void*)0) ||
1758 tokenIsChar(token, ',')))
1759 continue;
1760
1761 error = parseSimpleSelector(c, vector, ctx, &other);
1762 if (error != CSS_OK) {
1763 css__stylesheet_selector_destroy(c->sheet, selector);
1764 return error;
1765 }
1766
1767 error = css__stylesheet_selector_combine(c->sheet,
1768 comb, selector, other);
1769 if (error != CSS_OK) {
1770 css__stylesheet_selector_destroy(c->sheet, selector);
1771 css__stylesheet_selector_destroy(c->sheet, other);
1772 return error;
1773 }
1774
1775 selector = other;
1776 }
1777
1778 *result = selector;
1779
1780 return CSS_OK;
1781}
1782
1783css_error parseSelectorList(css_language *c, const parserutils_vector *vector,
1784 css_rule *rule)
1785{
1786 css_error error;
1787 const css_token *token = NULL((void*)0);
1788 css_selector *selector = NULL((void*)0);
1789 int32_t ctx = 0;
1790
1791 /* Strip any leading whitespace (can happen if in nested block) */
1792 consumeWhitespace(vector, &ctx);
1793
1794 /* selector_list -> selector [ ',' ws selector ]* */
1795
1796 error = parseSelector(c, vector, &ctx, &selector);
1797 if (error != CSS_OK) {
1798 if (selector != NULL((void*)0))
1799 css__stylesheet_selector_destroy(c->sheet, selector);
1800 return error;
1801 }
1802
1803 assert(selector != NULL)((selector != ((void*)0)) ? (void) (0) : __assert_fail ("selector != NULL"
, "src/parse/language.c", 1803, __extension__ __PRETTY_FUNCTION__
))
;
1804
1805 error = css__stylesheet_rule_add_selector(c->sheet, rule, selector);
1806 if (error != CSS_OK) {
1807 css__stylesheet_selector_destroy(c->sheet, selector);
1808 return error;
1809 }
1810
1811 while (parserutils_vector_peek(vector, ctx) != NULL((void*)0)) {
1812 token = parserutils_vector_iterate(vector, &ctx);
1813 if (tokenIsChar(token, ',') == false0)
1814 return CSS_INVALID;
1815
1816 consumeWhitespace(vector, &ctx);
1817
1818 selector = NULL((void*)0);
1819
1820 error = parseSelector(c, vector, &ctx, &selector);
1821 if (error != CSS_OK) {
1822 if (selector != NULL((void*)0)) {
1823 css__stylesheet_selector_destroy(c->sheet,
1824 selector);
1825 }
1826 return error;
1827 }
1828
1829 assert(selector != NULL)((selector != ((void*)0)) ? (void) (0) : __assert_fail ("selector != NULL"
, "src/parse/language.c", 1829, __extension__ __PRETTY_FUNCTION__
))
;
1830
1831 error = css__stylesheet_rule_add_selector(c->sheet, rule,
1832 selector);
1833 if (error != CSS_OK) {
1834 css__stylesheet_selector_destroy(c->sheet, selector);
1835 return error;
1836 }
1837 }
1838
1839 return CSS_OK;
1840}
1841
1842/******************************************************************************
1843 * Property parsing functions *
1844 ******************************************************************************/
1845
1846css_error parseProperty(css_language *c, const css_token *property,
1847 const parserutils_vector *vector, int32_t *ctx, css_rule *rule)
1848{
1849 css_error error;
1850 css_prop_handler handler = NULL((void*)0);
1851 int i = 0;
1852 uint8_t flags = 0;
1853 css_style *style = NULL((void*)0);
1854 const css_token *token;
1855
1856 /* Find property index */
1857 /** \todo improve on this linear search */
1858 for (i = FIRST_PROP; i <= LAST_PROP; i++) {
1859 bool_Bool match = false0;
1860
1861 if (lwc_string_caseless_isequal(property->idata, c->strings[i],({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (property->idata); lwc_string *__lwc_str2 = (c->strings
[i]); _Bool *__lwc_ret = (&match); if (__lwc_str1->insensitive
== ((void*)0)) { __lwc_err = lwc__intern_caseless_string(__lwc_str1
); } if (__lwc_err == lwc_error_ok && __lwc_str2->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
1862 &match)({ lwc_error __lwc_err = lwc_error_ok; lwc_string *__lwc_str1
= (property->idata); lwc_string *__lwc_str2 = (c->strings
[i]); _Bool *__lwc_ret = (&match); if (__lwc_str1->insensitive
== ((void*)0)) { __lwc_err = lwc__intern_caseless_string(__lwc_str1
); } if (__lwc_err == lwc_error_ok && __lwc_str2->
insensitive == ((void*)0)) { __lwc_err = lwc__intern_caseless_string
(__lwc_str2); } if (__lwc_err == lwc_error_ok) *__lwc_ret = (
__lwc_str1->insensitive == __lwc_str2->insensitive); __lwc_err
; })
== lwc_error_ok && match)
1863 break;
1864 }
1865 if (i == LAST_PROP + 1)
1866 return CSS_INVALID;
1867
1868 /* Get handler */
1869 handler = property_handlers[i - FIRST_PROP];
1870 assert(handler != NULL)((handler != ((void*)0)) ? (void) (0) : __assert_fail ("handler != NULL"
, "src/parse/language.c", 1870, __extension__ __PRETTY_FUNCTION__
))
;
1871
1872 /* allocate style */
1873 error = css__stylesheet_style_create(c->sheet, &style);
1874 if (error != CSS_OK)
1875 return error;
1876
1877 assert (style != NULL)((style != ((void*)0)) ? (void) (0) : __assert_fail ("style != NULL"
, "src/parse/language.c", 1877, __extension__ __PRETTY_FUNCTION__
))
;
1878
1879 /* Call the handler */
1880 error = handler(c, vector, ctx, style);
1881 if (error != CSS_OK) {
1882 css__stylesheet_style_destroy(style);
1883 return error;
1884 }
1885
1886 /* Determine if this declaration is important or not */
1887 error = css__parse_important(c, vector, ctx, &flags);
1888 if (error != CSS_OK) {
1889 css__stylesheet_style_destroy(style);
1890 return error;
1891 }
1892
1893 /* Ensure that we've exhausted all the input */
1894 consumeWhitespace(vector, ctx);
1895 token = parserutils_vector_iterate(vector, ctx);
1896 if (token != NULL((void*)0)) {
1897 /* Trailing junk, so discard declaration */
1898 css__stylesheet_style_destroy(style);
1899 return CSS_INVALID;
1900 }
1901
1902 /* If it's important, then mark the style appropriately */
1903 if (flags != 0)
1904 css__make_style_important(style);
1905
1906 /* Append style to rule */
1907 error = css__stylesheet_rule_append_style(c->sheet, rule, style);
1908 if (error != CSS_OK) {
1909 css__stylesheet_style_destroy(style);
1910 return error;
1911 }
1912
1913 /* Style owned or destroyed by stylesheet, so forget about it */
1914
1915 return CSS_OK;
1916}
1917