Bug Summary

File:content/textsearch.c
Warning:line 438, column 2
Value stored to 'type' is never read

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 textsearch.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-netsurf -resource-dir /usr/lib/llvm-14/lib/clang/14.0.6 -I . -I include -I build/Linux-monkey -I frontends -I content/handlers -D WITH_JPEG -U WITH_PDF_EXPORT -D LIBICONV_PLUG -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 -I /usr/include/x86_64-linux-gnu -D WITH_CURL -D WITH_OPENSSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D UTF8PROC_EXPORTS -D WITH_UTF8PROC -D WITH_WEBP -I /usr/include/libpng16 -D WITH_PNG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include/ -D WITH_BMP -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_GIF -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSPSL -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSLOG -D NETSURF_UA_FORMAT_STRING="Mozilla/5.0 (%s) NetSurf/%d.%d" -D NETSURF_HOMEPAGE="about:welcome" -D NETSURF_LOG_LEVEL=VERBOSE -D NETSURF_BUILTIN_LOG_FILTER="(level:WARNING || cat:jserrors)" -D NETSURF_BUILTIN_VERBOSE_FILTER="(level:VERBOSE || cat:jserrors)" -D STMTEXPR=1 -D monkey -D nsmonkey -D MONKEY_RESPATH="/var/lib/jenkins/artifacts-x86_64-linux-gnu/share/netsurf/" -D _POSIX_C_SOURCE=200809L -D _XOPEN_SOURCE=700 -D _BSD_SOURCE -D _DEFAULT_SOURCE -D _NETBSD_SOURCE -D DUK_OPT_HAVE_CUSTOM_H -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 -O2 -Wwrite-strings -Wno-unused-parameter -Wno-unused-but-set-variable -std=c99 -fconst-strings -fdebug-compilation-dir=/var/lib/jenkins/workspace/scan-build-netsurf -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-display-progress -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /var/lib/jenkins/workspace/scan-build-netsurf/clangScanBuildReports/2024-12-10-012218-2266689-1 -x c content/textsearch.c
1/*
2 * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
3 * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20/**
21 * \file
22 * Free text search
23 */
24
25#include <stdbool.h>
26#include <stddef.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include "utils/errors.h"
31#include "utils/utils.h"
32#include "utils/ascii.h"
33#include "netsurf/types.h"
34#include "desktop/selection.h"
35
36#include "content/content.h"
37#include "content/content_protected.h"
38#include "content/hlcache.h"
39#include "content/textsearch.h"
40
41/**
42 * search match
43 */
44struct list_entry {
45 /**
46 * previous match
47 */
48 struct list_entry *prev;
49
50 /**
51 * next match
52 */
53 struct list_entry *next;
54
55 /**
56 * start position of match
57 */
58 unsigned start_idx;
59
60 /**
61 * end of match
62 */
63 unsigned end_idx;
64
65 /**
66 * content opaque start pointer
67 */
68 struct box *start_box;
69
70 /**
71 * content opaque end pointer
72 */
73 struct box *end_box;
74
75 /**
76 * content specific selection object
77 */
78 struct selection *sel;
79};
80
81/**
82 * The context for a free text search
83 */
84struct textsearch_context {
85
86 /**
87 * content search was performed upon
88 */
89 struct content *c;
90
91 /**
92 * opaque pointer passed to constructor.
93 */
94 void *gui_p;
95
96 /**
97 * List of matches
98 */
99 struct list_entry *found;
100
101 /**
102 * current selected match
103 */
104 struct list_entry *current; /* first for select all */
105
106 /**
107 * query string search results are for
108 */
109 char *string;
110 bool_Bool prev_case_sens;
111 bool_Bool newsearch;
112};
113
114
115/**
116 * broadcast textsearch message
117 */
118static inline void
119textsearch_broadcast(struct textsearch_context *textsearch,
120 int type,
121 bool_Bool state,
122 const char *string)
123{
124 union content_msg_data msg_data;
125 msg_data.textsearch.type = type;
126 msg_data.textsearch.ctx = textsearch->gui_p;
127 msg_data.textsearch.state = state;
128 msg_data.textsearch.string = string;
129 content_broadcast(textsearch->c, CONTENT_MSG_TEXTSEARCH, &msg_data);
130}
131
132
133/**
134 * Release the memory used by the list of matches,
135 * deleting selection objects too
136 */
137static void free_matches(struct textsearch_context *textsearch)
138{
139 struct list_entry *cur;
140 struct list_entry *nxt;
141
142 cur = textsearch->found->next;
143
144 /*
145 * empty the list before clearing and deleting the selections
146 * because the the clearing may update the toolkit immediately,
147 * causing nested accesses to the list
148 */
149
150 textsearch->found->prev = NULL((void*)0);
151 textsearch->found->next = NULL((void*)0);
152
153 for (; cur; cur = nxt) {
154 nxt = cur->next;
155 if (cur->sel) {
156 selection_destroy(cur->sel);
157 }
158 free(cur);
159 }
160}
161
162
163/**
164 * Specifies whether all matches or just the current match should
165 * be highlighted in the search text.
166 */
167static void search_show_all(bool_Bool all, struct textsearch_context *context)
168{
169 struct list_entry *a;
170
171 for (a = context->found->next; a; a = a->next) {
172 bool_Bool add = true1;
173 if (!all && a != context->current) {
174 add = false0;
175 if (a->sel) {
176 selection_destroy(a->sel);
177 a->sel = NULL((void*)0);
178 }
179 }
180
181 if (add && !a->sel) {
182
183 a->sel = selection_create(context->c);
184 if (a->sel != NULL((void*)0)) {
185 selection_init(a->sel);
186 selection_set_position(a->sel,
187 a->start_idx,
188 a->end_idx);
189 }
190 }
191 }
192}
193
194
195/**
196 * Search for a string in a content.
197 *
198 * \param context The search context.
199 * \param string the string to search for
200 * \param string_len length of search string
201 * \param flags flags to control the search.
202 */
203static nserror
204search_text(struct textsearch_context *context,
205 const char *string,
206 int string_len,
207 search_flags_t flags)
208{
209 struct rect bounds;
210 union content_msg_data msg_data;
211 bool_Bool case_sensitive, forwards, showall;
212 nserror res = NSERROR_OK;
213
214 case_sensitive = ((flags & SEARCH_FLAG_CASE_SENSITIVE) != 0) ?
215 true1 : false0;
216 forwards = ((flags & SEARCH_FLAG_FORWARDS) != 0) ? true1 : false0;
217 showall = ((flags & SEARCH_FLAG_SHOWALL) != 0) ? true1 : false0;
218
219 if (context->c == NULL((void*)0)) {
220 return res;
221 }
222
223 /* check if we need to start a new search or continue an old one */
224 if ((context->newsearch) ||
225 (context->prev_case_sens != case_sensitive)) {
226
227 if (context->string != NULL((void*)0)) {
228 free(context->string);
229 }
230
231 context->current = NULL((void*)0);
232 free_matches(context);
233
234 context->string = malloc(string_len + 1);
235 if (context->string != NULL((void*)0)) {
236 memcpy(context->string, string, string_len);
237 context->string[string_len] = '\0';
238 }
239
240 /* indicate find operation starting */
241 textsearch_broadcast(context, CONTENT_TEXTSEARCH_FIND, true1, NULL((void*)0));
242
243
244 /* call content find handler */
245 res = context->c->handler->textsearch_find(context->c,
246 context,
247 string,
248 string_len,
249 case_sensitive);
250
251 /* indicate find operation finished */
252 textsearch_broadcast(context, CONTENT_TEXTSEARCH_FIND, false0, NULL((void*)0));
253
254 if (res != NSERROR_OK) {
255 free_matches(context);
256 return res;
257 }
258
259 context->prev_case_sens = case_sensitive;
260
261 /* new search, beginning at the top of the page */
262 context->current = context->found->next;
263 context->newsearch = false0;
264
265 } else if (context->current != NULL((void*)0)) {
266 /* continued search in the direction specified */
267 if (forwards) {
268 if (context->current->next)
269 context->current = context->current->next;
270 } else {
271 if (context->current->prev)
272 context->current = context->current->prev;
273 }
274 }
275
276 /* update match state */
277 textsearch_broadcast(context,
278 CONTENT_TEXTSEARCH_MATCH,
279 (context->current != NULL((void*)0)),
280 NULL((void*)0));
281
282 search_show_all(showall, context);
283
284 /* update back state */
285 textsearch_broadcast(context,
286 CONTENT_TEXTSEARCH_BACK,
287 ((context->current != NULL((void*)0)) &&
288 (context->current->prev != NULL((void*)0))),
289 NULL((void*)0));
290
291 /* update forward state */
292 textsearch_broadcast(context,
293 CONTENT_TEXTSEARCH_FORWARD,
294 ((context->current != NULL((void*)0)) &&
295 (context->current->next != NULL((void*)0))),
296 NULL((void*)0));
297
298
299 if (context->current == NULL((void*)0)) {
300 /* no current match */
301 return res;
302 }
303
304 /* call content match bounds handler */
305 res = context->c->handler->textsearch_bounds(context->c,
306 context->current->start_idx,
307 context->current->end_idx,
308 context->current->start_box,
309 context->current->end_box,
310 &bounds);
311 if (res == NSERROR_OK) {
312 msg_data.scroll.area = true1;
313 msg_data.scroll.x0 = bounds.x0;
314 msg_data.scroll.y0 = bounds.y0;
315 msg_data.scroll.x1 = bounds.x1;
316 msg_data.scroll.y1 = bounds.y1;
317 content_broadcast(context->c, CONTENT_MSG_SCROLL, &msg_data);
318 }
319
320 return res;
321}
322
323
324/**
325 * Begins/continues the search process
326 *
327 * \note that this may be called many times for a single search.
328 *
329 * \param context The search context in use.
330 * \param flags The flags forward/back etc
331 * \param string The string to match
332 */
333static nserror
334content_textsearch_step(struct textsearch_context *textsearch,
335 search_flags_t flags,
336 const char *string)
337{
338 int string_len;
339 int i = 0;
340 nserror res = NSERROR_OK;
341
342 assert(textsearch != NULL)((textsearch != ((void*)0)) ? (void) (0) : __assert_fail ("textsearch != NULL"
, "content/textsearch.c", 342, __extension__ __PRETTY_FUNCTION__
))
;
343
344 /* broadcast recent query string */
345 textsearch_broadcast(textsearch,
346 CONTENT_TEXTSEARCH_RECENT,
347 false0,
348 string);
349
350 string_len = strlen(string);
351 for (i = 0; i < string_len; i++) {
352 if (string[i] != '#' && string[i] != '*')
353 break;
354 }
355
356 if (i < string_len) {
357 res = search_text(textsearch, string, string_len, flags);
358 } else {
359 union content_msg_data msg_data;
360
361 free_matches(textsearch);
362
363 /* update match state */
364 textsearch_broadcast(textsearch,
365 CONTENT_TEXTSEARCH_MATCH,
366 true1,
367 NULL((void*)0));
368
369 /* update back state */
370 textsearch_broadcast(textsearch,
371 CONTENT_TEXTSEARCH_BACK,
372 false0,
373 NULL((void*)0));
374
375 /* update forward state */
376 textsearch_broadcast(textsearch,
377 CONTENT_TEXTSEARCH_FORWARD,
378 false0,
379 NULL((void*)0));
380
381 /* clear scroll */
382 msg_data.scroll.area = false0;
383 msg_data.scroll.x0 = 0;
384 msg_data.scroll.y0 = 0;
385 content_broadcast(textsearch->c,
386 CONTENT_MSG_SCROLL,
387 &msg_data);
388 }
389
390 return res;
391}
392
393
394/**
395 * Terminate a search.
396 *
397 * \param c content to clear
398 */
399static nserror content_textsearch__clear(struct content *c)
400{
401 free(c->textsearch.string);
402 c->textsearch.string = NULL((void*)0);
403
404 if (c->textsearch.context != NULL((void*)0)) {
405 content_textsearch_destroy(c->textsearch.context);
406 c->textsearch.context = NULL((void*)0);
407 }
408 return NSERROR_OK;
409}
410
411
412/**
413 * create a search_context
414 *
415 * \param c The content the search_context is connected to
416 * \param context A context pointer passed to the provider routines.
417 * \param search_out A pointer to recive the new text search context
418 * \return NSERROR_OK on success and \a search_out updated else error code
419 */
420static nserror
421content_textsearch_create(struct content *c,
422 void *gui_data,
423 struct textsearch_context **textsearch_out)
424{
425 struct textsearch_context *context;
426 struct list_entry *search_head;
427 content_type type;
428
429 if ((c->handler->textsearch_find == NULL((void*)0)) ||
430 (c->handler->textsearch_bounds == NULL((void*)0))) {
431 /*
432 * content has no free text find handler so searching
433 * is unsupported.
434 */
435 return NSERROR_NOT_IMPLEMENTED;
436 }
437
438 type = c->handler->type();
Value stored to 'type' is never read
439
440 context = malloc(sizeof(struct textsearch_context));
441 if (context == NULL((void*)0)) {
442 return NSERROR_NOMEM;
443 }
444
445 search_head = malloc(sizeof(struct list_entry));
446 if (search_head == NULL((void*)0)) {
447 free(context);
448 return NSERROR_NOMEM;
449 }
450
451 search_head->start_idx = 0;
452 search_head->end_idx = 0;
453 search_head->start_box = NULL((void*)0);
454 search_head->end_box = NULL((void*)0);
455 search_head->sel = NULL((void*)0);
456 search_head->prev = NULL((void*)0);
457 search_head->next = NULL((void*)0);
458
459 context->found = search_head;
460 context->current = NULL((void*)0);
461 context->string = NULL((void*)0);
462 context->prev_case_sens = false0;
463 context->newsearch = true1;
464 context->c = c;
465 context->gui_p = gui_data;
466
467 *textsearch_out = context;
468
469 return NSERROR_OK;
470}
471
472
473/* exported interface, documented in content/textsearch.h */
474const char *
475content_textsearch_find_pattern(const char *string,
476 int s_len,
477 const char *pattern,
478 int p_len,
479 bool_Bool case_sens,
480 unsigned int *m_len)
481{
482 struct { const char *ss, *s, *p; bool_Bool first; } context[16];
483 const char *ep = pattern + p_len;
484 const char *es = string + s_len;
485 const char *p = pattern - 1; /* a virtual '*' before the pattern */
486 const char *ss = string;
487 const char *s = string;
488 bool_Bool first = true1;
489 int top = 0;
490
491 while (p < ep) {
492 bool_Bool matches;
493 if (p < pattern || *p == '*') {
494 char ch;
495
496 /* skip any further asterisks; one is the same as many
497 */
498 do p++; while (p < ep && *p == '*');
499
500 /* if we're at the end of the pattern, yes, it matches
501 */
502 if (p >= ep) break;
503
504 /* anything matches a # so continue matching from
505 here, and stack a context that will try to match
506 the wildcard against the next character */
507
508 ch = *p;
509 if (ch != '#') {
510 /* scan forwards until we find a match for
511 this char */
512 if (!case_sens) ch = ascii_to_upper(ch);
513 while (s < es) {
514 if (case_sens) {
515 if (*s == ch) break;
516 } else if (ascii_to_upper(*s) == ch)
517 break;
518 s++;
519 }
520 }
521
522 if (s < es) {
523 /* remember where we are in case the match
524 fails; we may then resume */
525 if (top < (int)NOF_ELEMENTS(context)(sizeof(context)/sizeof(*(context)))) {
526 context[top].ss = ss;
527 context[top].s = s + 1;
528 context[top].p = p - 1;
529 /* ptr to last asterisk */
530 context[top].first = first;
531 top++;
532 }
533
534 if (first) {
535 ss = s;
536 /* remember first non-'*' char */
537 first = false0;
538 }
539
540 matches = true1;
541 } else {
542 matches = false0;
543 }
544
545 } else if (s < es) {
546 char ch = *p;
547 if (ch == '#')
548 matches = true1;
549 else {
550 if (case_sens)
551 matches = (*s == ch);
552 else
553 matches = (ascii_to_upper(*s) == ascii_to_upper(ch));
554 }
555 if (matches && first) {
556 ss = s; /* remember first non-'*' char */
557 first = false0;
558 }
559 } else {
560 matches = false0;
561 }
562
563 if (matches) {
564 p++; s++;
565 } else {
566 /* doesn't match,
567 * resume with stacked context if we have one */
568 if (--top < 0)
569 return NULL((void*)0); /* no match, give up */
570
571 ss = context[top].ss;
572 s = context[top].s;
573 p = context[top].p;
574 first = context[top].first;
575 }
576 }
577
578 /* end of pattern reached */
579 *m_len = max(s - ss, 1)(((s - ss)>(1))?(s - ss):(1));
580 return ss;
581}
582
583
584/* exported interface, documented in content/textsearch.h */
585nserror
586content_textsearch_add_match(struct textsearch_context *context,
587 unsigned start_idx,
588 unsigned end_idx,
589 struct box *start_box,
590 struct box *end_box)
591{
592 struct list_entry *entry;
593
594 /* found string in box => add to list */
595 entry = calloc(1, sizeof(*entry));
596 if (entry == NULL((void*)0)) {
597 return NSERROR_NOMEM;
598 }
599
600 entry->start_idx = start_idx;
601 entry->end_idx = end_idx;
602 entry->start_box = start_box;
603 entry->end_box = end_box;
604 entry->sel = NULL((void*)0);
605
606 entry->next = NULL((void*)0);
607 entry->prev = context->found->prev;
608
609 if (context->found->prev == NULL((void*)0)) {
610 context->found->next = entry;
611 } else {
612 context->found->prev->next = entry;
613 }
614
615 context->found->prev = entry;
616
617 return NSERROR_OK;
618}
619
620
621/* exported interface, documented in content/textsearch.h */
622bool_Bool
623content_textsearch_ishighlighted(struct textsearch_context *textsearch,
624 unsigned start_offset,
625 unsigned end_offset,
626 unsigned *start_idx,
627 unsigned *end_idx)
628{
629 struct list_entry *cur;
630
631 for (cur = textsearch->found->next; cur != NULL((void*)0); cur = cur->next) {
632 if (cur->sel &&
633 selection_highlighted(cur->sel,
634 start_offset,
635 end_offset,
636 start_idx,
637 end_idx)) {
638 return true1;
639 }
640 }
641
642 return false0;
643}
644
645
646/* exported interface, documented in content/textsearch.h */
647nserror content_textsearch_destroy(struct textsearch_context *textsearch)
648{
649 assert(textsearch != NULL)((textsearch != ((void*)0)) ? (void) (0) : __assert_fail ("textsearch != NULL"
, "content/textsearch.c", 649, __extension__ __PRETTY_FUNCTION__
))
;
650
651 if (textsearch->string != NULL((void*)0)) {
652 /* broadcast recent query string */
653 textsearch_broadcast(textsearch,
654 CONTENT_TEXTSEARCH_RECENT,
655 false0,
656 textsearch->string);
657
658 free(textsearch->string);
659 }
660
661 /* update back state */
662 textsearch_broadcast(textsearch,
663 CONTENT_TEXTSEARCH_BACK,
664 true1,
665 NULL((void*)0));
666
667 /* update forward state */
668 textsearch_broadcast(textsearch,
669 CONTENT_TEXTSEARCH_FORWARD,
670 true1,
671 NULL((void*)0));
672
673 free_matches(textsearch);
674 free(textsearch);
675
676 return NSERROR_OK;
677}
678
679
680/* exported interface, documented in content/content.h */
681nserror
682content_textsearch(struct hlcache_handle *h,
683 void *context,
684 search_flags_t flags,
685 const char *string)
686{
687 struct content *c = hlcache_handle_get_content(h);
688 nserror res;
689
690 assert(c != NULL)((c != ((void*)0)) ? (void) (0) : __assert_fail ("c != NULL",
"content/textsearch.c", 690, __extension__ __PRETTY_FUNCTION__
))
;
691
692 if (string != NULL((void*)0) &&
693 c->textsearch.string != NULL((void*)0) &&
694 c->textsearch.context != NULL((void*)0) &&
695 strcmp(string, c->textsearch.string) == 0) {
696 /* Continue prev. search */
697 content_textsearch_step(c->textsearch.context, flags, string);
698
699 } else if (string != NULL((void*)0)) {
700 /* New search */
701 free(c->textsearch.string);
702 c->textsearch.string = strdup(string);
703 if (c->textsearch.string == NULL((void*)0)) {
704 return NSERROR_NOMEM;
705 }
706
707 if (c->textsearch.context != NULL((void*)0)) {
708 content_textsearch_destroy(c->textsearch.context);
709 c->textsearch.context = NULL((void*)0);
710 }
711
712 res = content_textsearch_create(c,
713 context,
714 &c->textsearch.context);
715 if (res != NSERROR_OK) {
716 return res;
717 }
718
719 content_textsearch_step(c->textsearch.context, flags, string);
720
721 } else {
722 /* Clear search */
723 content_textsearch__clear(c);
724
725 free(c->textsearch.string);
726 c->textsearch.string = NULL((void*)0);
727 }
728
729 return NSERROR_OK;
730}
731
732
733/* exported interface, documented in content/content.h */
734nserror content_textsearch_clear(struct hlcache_handle *h)
735{
736 struct content *c = hlcache_handle_get_content(h);
737 assert(c != 0)((c != 0) ? (void) (0) : __assert_fail ("c != 0", "content/textsearch.c"
, 737, __extension__ __PRETTY_FUNCTION__))
;
738
739 return(content_textsearch__clear(c));
740}