Bug Summary

File:tokenlist.c
Warning:line 178, column 6
Although the value stored to 'n_entries' is used in the enclosing expression, the value is never actually read from 'n_entries'

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 tokenlist.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -fcoverage-compilation-dir=/var/lib/jenkins/workspace/scan-build-libdom -resource-dir /usr/lib/llvm-14/lib/clang/14.0.6 -D _BSD_SOURCE -D _DEFAULT_SOURCE -I /var/lib/jenkins/workspace/scan-build-libdom/include/ -I /var/lib/jenkins/workspace/scan-build-libdom/src -I /var/lib/jenkins/workspace/scan-build-libdom/binding -D _ALIGNED=__attribute__((aligned)) -D STMTEXPR=1 -D DEBUG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -internal-isystem /usr/lib/llvm-14/lib/clang/14.0.6/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../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 -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-libdom -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-libdom/clangScanBuildReports/2024-12-27-083150-3299567-1 -x c src/core/tokenlist.c
1/*
2 * This file is part of libdom.
3 * Licensed under the MIT License,
4 * http://www.opensource.org/licenses/mit-license.php
5 * Copyright 2022 Daniel Silverstone <dsilvers@digital-scurf.org>
6 */
7
8#include <assert.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include <dom/core/element.h>
13#include <dom/core/nodelist.h>
14#include <dom/core/tokenlist.h>
15#include <dom/core/string.h>
16#include <dom/events/event.h>
17#include <dom/events/event_target.h>
18#include <dom/events/event_listener.h>
19#include <dom/events/mutation_event.h>
20
21#include "core/element.h"
22#include "core/document.h"
23
24#include "utils/utils.h"
25
26#define DOM_TOKENLIST_GROW_INCREMENT4 4
27
28struct dom_tokenlist {
29 uint32_t refcnt;
30 dom_element *ele;
31 dom_string *attr;
32 dom_event_listener *listener;
33 dom_string *last_set;
34 bool_Bool needs_parse;
35 /* Parsed content, for optimal access */
36 dom_string **entries;
37 uint32_t len;
38 uint32_t alloc;
39};
40
41/* Handle a DOMAttrModified event which might be to do with our attribute */
42
43static void _dom_tokenlist_handle_attrmodified(dom_event *evt, void *pw)
44{
45 dom_mutation_event *mutevt = (dom_mutation_event *)evt;
46 dom_tokenlist *list = (dom_tokenlist *)pw;
47 dom_exception exc;
48 dom_string *value;
49
50 {
51 dom_event_target *target;
52 exc = dom_event_get_target(evt, &target)_dom_event_get_target((dom_event *) (evt), (dom_event_target *
*) (&target))
;
53 if (exc != DOM_NO_ERR)
54 return;
55 dom_node_unref(target)dom_node_unref((dom_node *) (target));
56 if (target != (dom_event_target *)list->ele)
57 return;
58 }
59
60 {
61 dom_string *attr;
62 exc = dom_mutation_event_get_attr_name(mutevt, &attr)_dom_mutation_event_get_attr_name((dom_mutation_event *) (mutevt
), (dom_string **) (&attr))
;
63 if (exc != DOM_NO_ERR)
64 return;
65 if (!dom_string_isequal(attr, list->attr)) {
66 dom_string_unref(attr);
67 return;
68 }
69 dom_string_unref(attr);
70 }
71
72 /* At this point we know that this is a mutation of our attribute on our
73 * node */
74
75 exc = dom_mutation_event_get_new_value(mutevt, &value)_dom_mutation_event_get_new_value((dom_mutation_event *) (mutevt
), (dom_string **) (&value))
;
76 if (exc != DOM_NO_ERR)
77 return;
78
79 if (list->last_set != NULL((void*)0) &&
80 dom_string_isequal(list->last_set, value)) {
81 /* We've just seen the mutation event for one of our own set
82 * operations */
83 dom_string_unref(value);
84 return;
85 }
86
87 /* Mark that we need to re-parse the tokenlist on the next request */
88 list->needs_parse = true1;
89
90 dom_string_unref(value);
91}
92
93static dom_exception _dom_tokenlist_make_room(dom_tokenlist *list)
94{
95 if (list->len == list->alloc) {
96 uint32_t new_alloc = list->alloc + DOM_TOKENLIST_GROW_INCREMENT4;
97 dom_string **new_entries = realloc(
98 list->entries, new_alloc * sizeof(dom_string *));
99 if (new_entries == NULL((void*)0))
100 return DOM_NO_MEM_ERR;
101 list->alloc = new_alloc;
102 list->entries = new_entries;
103 }
104
105 return DOM_NO_ERR;
106}
107
108static dom_exception _dom_tokenlist_reparse(dom_tokenlist *list)
109{
110 dom_exception exc;
111 dom_string *value;
112 const char *pos;
113 uint32_t remaining, check;
114 uint32_t n_entries = 0;
115 dom_string *temp;
116 bool_Bool found;
117
118 if (!list->needs_parse)
119 return DOM_NO_ERR;
120
121 /* Clean down the current entries */
122 while (list->len-- > 0)
123 dom_string_unref(list->entries[list->len]);
124 list->len = 0;
125
126 /* Get the "new" attribute value */
127 exc = dom_element_get_attribute(list->ele, list->attr, &value)dom_element_get_attribute( (dom_element *) (list->ele), (list
->attr), (&value))
;
128 if (exc != DOM_NO_ERR)
129 return exc;
130
131 /* If there is no value, we're an empty list and we're done */
132 if (value == NULL((void*)0)) {
133 list->needs_parse = false0;
134 return DOM_NO_ERR;
135 }
136
137 /* OK, there's something here to do, so let's do it... */
138
139 /* Count number of entries */
140 for (pos = dom_string_data(value), remaining = dom_string_length(value);
141 remaining > 0;) {
142 if (*pos != ' ') {
143 while (*pos != ' ' && remaining > 0) {
144 remaining--;
145 pos++;
146 }
147 n_entries++;
148 } else {
149 while (*pos == ' ' && remaining > 0) {
150 remaining--;
151 pos++;
152 }
153 }
154 }
155
156 /* If there are no entries (all whitespace) just bail here */
157 if (n_entries == 0) {
158 list->needs_parse = false0;
159 dom_string_unref(value);
160 return DOM_NO_ERR;
161 }
162
163 /* If we need more room, reallocate the buffer */
164 if (list->alloc < n_entries) {
165 dom_string **new_alloc = realloc(
166 list->entries, n_entries * sizeof(dom_string *));
167 if (new_alloc == NULL((void*)0)) {
168 dom_string_unref(value);
169 return DOM_NO_MEM_ERR;
170 }
171 list->entries = new_alloc;
172 list->alloc = n_entries;
173 }
174
175 /* And now parse those entries into the buffer */
176 for (pos = dom_string_data(value),
177 remaining = dom_string_length(value),
178 n_entries = 0;
Although the value stored to 'n_entries' is used in the enclosing expression, the value is never actually read from 'n_entries'
179 remaining > 0;) {
180 if (*pos != ' ') {
181 const char *s = pos;
182 while (*pos != ' ' && remaining > 0) {
183 pos++;
184 remaining--;
185 }
186 exc = dom_string_create_interned((const uint8_t *)s,
187 pos - s,
188 &temp);
189 if (exc != DOM_NO_ERR) {
190 dom_string_unref(value);
191 return exc;
192 }
193 found = false0;
194 for (check = 0; check < list->len; check++) {
195 if (dom_string_isequal(temp,
196 list->entries[check])) {
197 found = true1;
198 break;
199 }
200 }
201 if (found == true1) {
202 dom_string_unref(temp);
203 } else {
204 list->entries[list->len] = temp;
205 list->len++;
206 }
207 } else {
208 while (*pos == ' ' && remaining > 0) {
209 pos++;
210 remaining--;
211 }
212 }
213 }
214
215 dom_string_unref(value);
216 list->needs_parse = false0;
217
218 return DOM_NO_ERR;
219}
220
221static dom_exception _dom_tokenlist_reify(dom_tokenlist *list)
222{
223 dom_exception exc;
224 uint32_t nchars = 0, n;
225 char *buffer, *next;
226 dom_string *output;
227
228 if (list->len == 0) {
229 if (list->last_set != NULL((void*)0)) {
230 dom_string_unref(list->last_set);
231 }
232 list->last_set = dom_string_ref(
233 list->ele->base.owner->_memo_empty);
234 return dom_element_set_attribute(list->ele,dom_element_set_attribute( (dom_element *) (list->ele), (list
->attr), (list->last_set))
235 list->attr,dom_element_set_attribute( (dom_element *) (list->ele), (list
->attr), (list->last_set))
236 list->last_set)dom_element_set_attribute( (dom_element *) (list->ele), (list
->attr), (list->last_set))
;
237 }
238
239 for (n = 0; n < list->len; ++n)
240 nchars += dom_string_length(list->entries[n]);
241
242 buffer = calloc(1, nchars + list->len);
243 if (buffer == NULL((void*)0))
244 return DOM_NO_MEM_ERR;
245
246 for (next = buffer, n = 0; n < list->len; ++n) {
247 uint32_t slen = dom_string_length(list->entries[n]);
248 memcpy(next, dom_string_data(list->entries[n]), slen);
249 next[slen] = ' ';
250 next += slen + 1;
251 }
252
253 exc = dom_string_create_interned((const uint8_t *)buffer,
254 nchars + list->len - 1,
255 &output);
256 free(buffer);
257 if (exc != DOM_NO_ERR)
258 return exc;
259
260 if (list->last_set != NULL((void*)0)) {
261 dom_string_unref(list->last_set);
262 }
263 list->last_set = output;
264
265 return dom_element_set_attribute(list->ele, list->attr, list->last_set)dom_element_set_attribute( (dom_element *) (list->ele), (list
->attr), (list->last_set))
;
266}
267
268/**********************************************************************************/
269
270/**
271 * Create a tokenlist
272 *
273 * \param ele The element which owns the tokenlist attribute
274 * \param attr The name of the attribute we are treating as a tokenlist
275 * \param list The tokenlist output which is set on success
276 * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
277 *
278 * The returned list will already be referenced, so the client need not
279 * do so explicitly. The client must unref the list once finished with it.
280 *
281 * This list will take its own references to ::ele and ::attr
282 */
283dom_exception
284dom_tokenlist_create(dom_element *ele, dom_string *attr, dom_tokenlist **list)
285{
286 dom_tokenlist *l;
287 dom_exception exc;
288
289 l = calloc(1, sizeof(dom_tokenlist));
290 if (l == NULL((void*)0))
291 return DOM_NO_MEM_ERR;
292
293 l->refcnt = 1;
294 l->ele = (dom_element *)dom_node_ref(ele)dom_node_ref((dom_node *) (ele));
295 l->attr = dom_string_ref(attr);
296 l->needs_parse = true1;
297
298 exc = dom_event_listener_create(_dom_tokenlist_handle_attrmodified,
299 l,
300 &l->listener);
301 if (exc != DOM_NO_ERR)
302 goto fail;
303
304 exc = dom_event_target_add_event_listener(dom_event_target_add_event_listener((dom_event_target *) (ele
), (dom_string *) (ele->base.owner->_memo_domattrmodified
), (struct dom_event_listener *) (l->listener), (_Bool) (0
))
305 ele,dom_event_target_add_event_listener((dom_event_target *) (ele
), (dom_string *) (ele->base.owner->_memo_domattrmodified
), (struct dom_event_listener *) (l->listener), (_Bool) (0
))
306 ele->base.owner->_memo_domattrmodified,dom_event_target_add_event_listener((dom_event_target *) (ele
), (dom_string *) (ele->base.owner->_memo_domattrmodified
), (struct dom_event_listener *) (l->listener), (_Bool) (0
))
307 l->listener,dom_event_target_add_event_listener((dom_event_target *) (ele
), (dom_string *) (ele->base.owner->_memo_domattrmodified
), (struct dom_event_listener *) (l->listener), (_Bool) (0
))
308 false)dom_event_target_add_event_listener((dom_event_target *) (ele
), (dom_string *) (ele->base.owner->_memo_domattrmodified
), (struct dom_event_listener *) (l->listener), (_Bool) (0
))
;
309
310 if (exc != DOM_NO_ERR)
311 goto fail;
312
313 *list = l;
314
315 return DOM_NO_ERR;
316
317fail:
318 if (l->listener != NULL((void*)0))
319 dom_event_listener_unref(l->listener);
320 dom_node_unref(l->ele)dom_node_unref((dom_node *) (l->ele));
321 dom_string_unref(l->attr);
322 free(l);
323 return exc;
324}
325
326/**
327 * Claim a ref on a tokenlist
328 *
329 * \param list The tokenlist to claim a ref on
330 */
331void dom_tokenlist_ref(dom_tokenlist *list)
332{
333 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 333, __extension__ __PRETTY_FUNCTION__
))
;
334 list->refcnt++;
335}
336
337/**
338 * Release a ref on a tokenlist
339 *
340 * \param list The list to release the reference of
341 *
342 * If you release the last ref, this cleans up the tokenlist
343 */
344void dom_tokenlist_unref(dom_tokenlist *list)
345{
346 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 346, __extension__ __PRETTY_FUNCTION__
))
;
347
348 if (--list->refcnt > 0)
349 return;
350
351 if (list->alloc > 0) {
352 while (list->len-- > 0)
353 dom_string_unref(list->entries[list->len]);
354 free(list->entries);
355 }
356
357 dom_event_target_remove_event_listener(dom_event_target_remove_event_listener( (dom_event_target *) (
list->ele), (dom_string *) (list->ele->base.owner->
_memo_domattrmodified), (struct dom_event_listener *) (list->
listener), (_Bool) (0))
358 list->ele,dom_event_target_remove_event_listener( (dom_event_target *) (
list->ele), (dom_string *) (list->ele->base.owner->
_memo_domattrmodified), (struct dom_event_listener *) (list->
listener), (_Bool) (0))
359 list->ele->base.owner->_memo_domattrmodified,dom_event_target_remove_event_listener( (dom_event_target *) (
list->ele), (dom_string *) (list->ele->base.owner->
_memo_domattrmodified), (struct dom_event_listener *) (list->
listener), (_Bool) (0))
360 list->listener,dom_event_target_remove_event_listener( (dom_event_target *) (
list->ele), (dom_string *) (list->ele->base.owner->
_memo_domattrmodified), (struct dom_event_listener *) (list->
listener), (_Bool) (0))
361 false)dom_event_target_remove_event_listener( (dom_event_target *) (
list->ele), (dom_string *) (list->ele->base.owner->
_memo_domattrmodified), (struct dom_event_listener *) (list->
listener), (_Bool) (0))
;
362
363 dom_event_listener_unref(list->listener);
364
365 if (list->last_set != NULL((void*)0))
366 dom_string_unref(list->last_set);
367
368 dom_string_unref(list->attr);
369 dom_node_unref(list->ele)dom_node_unref((dom_node *) (list->ele));
370
371 free(list);
372}
373
374/**
375 * Get the length of the tokenlist
376 *
377 * \param list The list to get the length of
378 * \param length Length of the list outputs here
379 * \return DOM_NO_ERR on success, otherwise the failure code
380 */
381dom_exception dom_tokenlist_get_length(dom_tokenlist *list, uint32_t *length)
382{
383 dom_exception exc;
384 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 384, __extension__ __PRETTY_FUNCTION__
))
;
385
386 exc = _dom_tokenlist_reparse(list);
387 if (exc != DOM_NO_ERR)
388 return exc;
389
390 *length = list->len;
391
392 return DOM_NO_ERR;
393}
394
395/**
396 * Get a particular item from the tokenlist
397 *
398 * \param list The list to retrieve the item from
399 * \param index The index of the item to retrieve
400 * \param value The value of the item returns here
401 * \return DOM_NO_ERR on success, otherwise the failure code
402 */
403dom_exception
404_dom_tokenlist_item(dom_tokenlist *list, uint32_t index, dom_string **value)
405{
406 dom_exception exc;
407 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 407, __extension__ __PRETTY_FUNCTION__
))
;
408
409 exc = _dom_tokenlist_reparse(list);
410 if (exc != DOM_NO_ERR)
411 return exc;
412
413 if (index >= list->len) {
414 *value = NULL((void*)0);
415 return DOM_NO_ERR;
416 }
417
418 *value = dom_string_ref(list->entries[index]);
419 return DOM_NO_ERR;
420}
421
422/**
423 * Retrieve the value of the tokenlist as a string
424 *
425 * \param list The list to retrieve the value of
426 * \param value The value of the list returns here
427 * \return DOM_NO_ERR on success, otherwise the failure code
428 */
429dom_exception dom_tokenlist_get_value(dom_tokenlist *list, dom_string **value)
430{
431 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 431, __extension__ __PRETTY_FUNCTION__
))
;
432
433 return dom_element_get_attribute(list->ele, list->attr, value)dom_element_get_attribute( (dom_element *) (list->ele), (list
->attr), (value))
;
434}
435
436/**
437 * Set the value of the tokenlist as a string
438 *
439 * \param list The list to set the value of
440 * \param value The value to set
441 * \return DOM_NO_ERR on success, otherwise the failure code
442 *
443 */
444dom_exception dom_tokenlist_set_value(dom_tokenlist *list, dom_string *value)
445{
446 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 446, __extension__ __PRETTY_FUNCTION__
))
;
447
448 return dom_element_set_attribute(list->ele, list->attr, value)dom_element_set_attribute( (dom_element *) (list->ele), (list
->attr), (value))
;
449}
450
451/**
452 * Check if the given value is in the tokenlist
453 *
454 * \param list The list to scan for the given value
455 * \param value The value to look for in the token list
456 * \param contains This will be set based on whether or not the value is present
457 * \return DOM_NO_ERR on success, otherwise the failure code
458 */
459dom_exception
460dom_tokenlist_contains(dom_tokenlist *list, dom_string *value, bool_Bool *contains)
461{
462 dom_exception exc;
463 uint32_t n;
464
465 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 465, __extension__ __PRETTY_FUNCTION__
))
;
466
467 exc = _dom_tokenlist_reparse(list);
468 if (exc != DOM_NO_ERR)
469 return exc;
470
471 *contains = false0;
472
473 for (n = 0; n < list->len; n++) {
474 if (dom_string_isequal(value, list->entries[n])) {
475 *contains = true1;
476 break;
477 }
478 }
479
480 return DOM_NO_ERR;
481}
482
483/**
484 * Add the given value to the tokenlist
485 *
486 * \param list The list to add to
487 * \param value The value to add
488 * \return DOM_NO_ERR on success, otherwise the failure code
489 */
490dom_exception dom_tokenlist_add(dom_tokenlist *list, dom_string *value)
491{
492 dom_exception exc;
493 bool_Bool present = false0;
494
495 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 495, __extension__ __PRETTY_FUNCTION__
))
;
496
497 exc = dom_tokenlist_contains(list, value, &present);
498 if (exc != DOM_NO_ERR)
499 return exc;
500
501 if (present == true1)
502 return DOM_NO_ERR;
503
504 exc = _dom_tokenlist_make_room(list);
505 if (exc != DOM_NO_ERR)
506 return exc;
507
508 list->entries[list->len++] = dom_string_ref(value);
509
510 exc = _dom_tokenlist_reify(list);
511
512 return exc;
513}
514
515/**
516 * Remove the given value from the tokenlist
517 *
518 * \param list The list to remove from
519 * \param value The value to remove
520 * \return DOM_NO_ERR on success, otherwise the failure code
521 */
522dom_exception dom_tokenlist_remove(dom_tokenlist *list, dom_string *value)
523{
524 dom_exception exc;
525 uint32_t n, m;
526
527 assert(list != NULL)((list != ((void*)0)) ? (void) (0) : __assert_fail ("list != NULL"
, "src/core/tokenlist.c", 527, __extension__ __PRETTY_FUNCTION__
))
;
528
529 exc = _dom_tokenlist_reparse(list);
530 if (exc != DOM_NO_ERR)
531 return false0;
532
533 for (n = 0; n < list->len; ++n) {
534 if (dom_string_isequal(value, list->entries[n])) {
535 dom_string_unref(list->entries[n]);
536 for (m = n + 1; m < list->len; ++m) {
537 list->entries[m - 1] = list->entries[m];
538 }
539 list->len--;
540 break;
541 }
542 }
543
544 exc = _dom_tokenlist_reify(list);
545
546 return exc;
547}