Bug Summary

File:content/fetchers/about/chart.c
Warning:line 356, column 7
Value stored to 'kvlen' 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 chart.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-framebuffer -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_NS_SVG -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -D WITH_NSSPRITE -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 nsframebuffer -D small -D NETSURF_FB_RESPATH="${HOME}/.netsurf/:${NETSURFRES}:/var/lib/jenkins/artifacts-x86_64-linux-gnu/share/netsurf:./frontends/framebuffer/res" -D NETSURF_FB_FONTPATH="/usr/share/fonts/truetype/dejavu:/usr/share/fonts/truetype/msttcorefonts" -D NETSURF_FB_FONT_SANS_SERIF="DejaVuSans.ttf" -D NETSURF_FB_FONT_SANS_SERIF_BOLD="DejaVuSans-Bold.ttf" -D NETSURF_FB_FONT_SANS_SERIF_ITALIC="DejaVuSans-Oblique.ttf" -D NETSURF_FB_FONT_SANS_SERIF_ITALIC_BOLD="DejaVuSans-BoldOblique.ttf" -D NETSURF_FB_FONT_SERIF="DejaVuSerif.ttf" -D NETSURF_FB_FONT_SERIF_BOLD="DejaVuSerif-Bold.ttf" -D NETSURF_FB_FONT_MONOSPACE="DejaVuSansMono.ttf" -D NETSURF_FB_FONT_MONOSPACE_BOLD="DejaVuSansMono-Bold.ttf" -D NETSURF_FB_FONT_CURSIVE="Comic_Sans_MS.ttf" -D NETSURF_FB_FONT_FANTASY="Impact.ttf" -I /var/lib/jenkins/artifacts-x86_64-linux-gnu/include -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-17-120558-2788052-1 -x c content/fetchers/about/chart.c
1/*
2 * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
3 *
4 * This file is part of NetSurf.
5 *
6 * NetSurf is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * NetSurf is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * \file
21 * content generator for the about scheme chart page
22 *
23 * A chart consists of the figure area in which a chart a title and a
24 * key are placed.
25 *
26 *
27 */
28
29#include <stdbool.h>
30#include <stddef.h>
31#include <stdlib.h>
32#include <string.h>
33#include <math.h>
34#include <stdio.h>
35
36#include "utils/config.h"
37#include "netsurf/inttypes.h"
38#include "utils/config.h"
39#include "utils/utils.h"
40#include "utils/errors.h"
41#include "utils/nsurl.h"
42
43#include "private.h"
44#include "chart.h"
45
46/** minimum figure dimension */
47#define FIGURE_MIN_WIDTH150 150
48#define FIGURE_MIN_HEIGHT100 100
49
50enum chart_type {
51 CHART_TYPE_UNKNOWN,
52 CHART_TYPE_PIE,
53};
54
55/* type of chart key */
56enum key_type {
57 CHART_KEY_UNSET,
58 CHART_KEY_NONE,
59 CHART_KEY_LEFT,
60 CHART_KEY_RIGHT,
61 CHART_KEY_TOP,
62 CHART_KEY_BOT,
63 CHART_KEY_END
64};
65
66
67struct chart_label {
68 char *title; /* label title */
69 unsigned int colour; /* colour */
70};
71
72struct chart_series {
73 unsigned int len; /* number of values in the series */
74 float *value; /* array of values */
75};
76
77#define MAX_SERIES4 4
78
79struct chart_data {
80 unsigned int series_len;
81 struct chart_series series[MAX_SERIES4];
82
83 unsigned int label_len; /* number of labels */
84 struct chart_label *label;
85
86};
87
88/**
89 * parameters for a chart figure
90 */
91struct chart_param {
92 enum chart_type type;
93 enum key_type key; /* what type of key to use */
94 unsigned int width; /* width of figure */
95 unsigned int height; /* height of figure */
96 char *title; /* title */
97 struct {
98 unsigned int x;
99 unsigned int y;
100 unsigned int width;
101 unsigned int height;
102 } area; /* chart area within figure */
103 struct chart_data data;
104};
105
106#define DEF_COLOUR_NUM8 8
107/** default colour series */
108static unsigned int colour_series[DEF_COLOUR_NUM8] =
109 {
110 0x00ff00, /* green */
111 0x0000ff, /* blue */
112 0xff0000, /* red */
113 0xffff00, /* yellow */
114 0x00ffff, /* cyan */
115 0xff00ff, /* pink */
116 0x777777, /* grey */
117 0x000000, /* black */
118 };
119
120
121/* ensures there are labels present for every value */
122static nserror ensure_label_count(struct chart_param *chart, unsigned int count)
123{
124 unsigned int lidx;
125 int deltac;
126 struct chart_label *nlabels;
127
128 deltac = count - chart->data.label_len;
129 if (deltac <= 0) {
130 /* there are enough labels */
131 return NSERROR_OK;
132 }
133
134 nlabels = realloc(chart->data.label,
135 count * sizeof(struct chart_label));
136 if (nlabels == NULL((void*)0)) {
137 return NSERROR_NOMEM;
138 }
139 chart->data.label = nlabels;
140
141 for (lidx = chart->data.label_len; lidx < count; lidx++) {
142 chart->data.label[lidx].title = calloc(1, 20);
143 snprintf(chart->data.label[lidx].title, 19, "item %d", lidx + 1);
144 chart->data.label[lidx].colour = colour_series[lidx % DEF_COLOUR_NUM8];
145 }
146
147 chart->data.label_len = count;
148
149 return NSERROR_OK;
150}
151
152/**
153 * extract values for a series
154 */
155static nserror
156extract_series_values(struct chart_param *chart,
157 unsigned int series_num,
158 const char *valstr,
159 size_t valstrlen)
160{
161 nserror res;
162 unsigned int valcur;
163 size_t valstart;/* value start in valstr */
164 size_t vallen; /* value end in valstr */
165 struct chart_series *series;
166
167 series = chart->data.series + series_num;
168
169 /* ensure we do not leak any data in this series */
170 if (series->value != NULL((void*)0)) {
171 free(series->value);
172 }
173
174 /* count how many values present */
175 for (series->len = 1, valstart=0; valstart < valstrlen; valstart++) {
176 if (valstr[valstart] == ',') {
177 series->len++;
178 }
179 }
180
181 /* allocate storage for values */
182 series->value = calloc(series->len, sizeof(float));
183 if (series->value == NULL((void*)0)) {
184 return NSERROR_NOMEM;
185 }
186
187 /* extract values from query string */
188 for (valcur = 0, vallen = 0, valstart = 0;
189 (valstart < valstrlen) && (valcur < series->len);
190 valstart += vallen, valcur++) {
191 /* get query section length */
192 vallen = 0;
193 while (((valstart + vallen) < valstrlen) &&
194 (valstr[valstart + vallen] != ',')) {
195 vallen++;
196 }
197
198 series->value[valcur] = strtof(valstr + valstart, NULL((void*)0));
199 vallen++; /* account for , separator */
200 }
201
202 res = ensure_label_count(chart, series->len);
203
204 return res;
205}
206
207
208/**
209 * extract values for next series
210 */
211static nserror
212extract_next_series_values(struct chart_param *chart,
213 const char *valstr,
214 size_t valstrlen)
215{
216 nserror res;
217
218 if (chart->data.series_len >= MAX_SERIES4) {
219 return NSERROR_NOSPACE;
220 }
221
222 res = extract_series_values(chart,
223 chart->data.series_len,
224 valstr,
225 valstrlen);
226 if (res == NSERROR_OK) {
227 chart->data.series_len++;
228 }
229
230 return res;
231}
232
233
234/**
235 * extract label title
236 */
237static nserror
238extract_series_labels(struct chart_param *chart,
239 const char *valstr,
240 size_t valstrlen)
241{
242 nserror res;
243 unsigned int valcount; /* count of values in valstr */
244 unsigned int valcur;
245 size_t valstart;/* value start in valstr */
246 size_t vallen; /* value end in valstr */
247
248 for (valcount = 1, valstart=0; valstart < valstrlen; valstart++) {
249 if (valstr[valstart] == ',') {
250 valcount++;
251 }
252 }
253
254 res = ensure_label_count(chart, valcount);
255 if (res != NSERROR_OK) {
256 return res;
257 }
258
259
260 for (valcur = 0, vallen = 0, valstart = 0;
261 (valstart < valstrlen) && (valcur < chart->data.label_len);
262 valstart += vallen, valcur++) {
263 /* get query section length */
264 vallen = 0;
265 while (((valstart + vallen) < valstrlen) &&
266 (valstr[valstart + vallen] != ',')) {
267 vallen++;
268 }
269
270 chart->data.label[valcur].title = strndup(valstr + valstart, vallen);
271 vallen++; /* account for , separator */
272 }
273 return NSERROR_OK;
274}
275
276
277/**
278 * extract labels colour
279 */
280static nserror
281extract_series_colours(struct chart_param *chart,
282 const char *valstr,
283 size_t valstrlen)
284{
285 return NSERROR_OK;
286}
287
288/**
289 * process a part of a query
290 */
291static nserror
292process_query_section(const char *str, size_t len, struct chart_param *chart)
293{
294 nserror res = NSERROR_OK;
295
296 if ((len > 6) &&
297 (strncmp(str, "width=", 6) == 0)) {
298 /* figure width */
299 chart->width = strtoul(str + 6, NULL((void*)0), 10);
300 } else if ((len > 7) &&
301 (strncmp(str, "height=", 7) == 0)) {
302 /* figure height */
303 chart->height = strtoul(str + 7, NULL((void*)0), 10);
304 } else if ((len > 8) &&
305 (strncmp(str, "cawidth=", 8) == 0)) {
306 /* chart area width */
307 chart->area.width = strtoul(str + 8, NULL((void*)0), 10);
308 } else if ((len > 9) &&
309 (strncmp(str, "caheight=", 9) == 0)) {
310 /* chart area height */
311 chart->area.height = strtoul(str + 9, NULL((void*)0), 10);
312 } else if ((len > 4) &&
313 (strncmp(str, "key=", 4) == 0)) {
314 /* figure has key */
315 chart->key = strtoul(str + 4, NULL((void*)0), 10);
316 } else if ((len > 6) &&
317 (strncmp(str, "title=", 6) == 0)) {
318 chart->title = strndup(str + 6, len - 6);
319 } else if ((len > 5) &&
320 (strncmp(str, "type=", 5) == 0)) {
321 if (strncmp(str + 5, "pie", len - 5) == 0) {
322 chart->type = CHART_TYPE_PIE;
323 } else {
324 chart->type = CHART_TYPE_UNKNOWN;
325 }
326 } else if ((len > 7) &&
327 (strncmp(str, "values=", 7) == 0)) {
328 res = extract_next_series_values(chart, str + 7, len - 7);
329 } else if ((len > 7) &&
330 (strncmp(str, "labels=", 7) == 0)) {
331 res = extract_series_labels(chart, str + 7, len - 7);
332 } else if ((len > 8) &&
333 (strncmp(str, "colours=", 8) == 0)) {
334 res = extract_series_colours(chart, str + 8, len - 8);
335 }
336
337 return res;
338}
339
340
341
342static nserror
343chart_from_query(struct nsurl *url, struct chart_param *chart)
344{
345 nserror res;
346 char *querystr;
347 size_t querylen;
348 size_t kvstart;/* key value start */
349 size_t kvlen; /* key value end */
350
351 res = nsurl_get(url, NSURL_QUERY, &querystr, &querylen);
352 if (res != NSERROR_OK) {
353 return res;
354 }
355
356 for (kvlen = 0, kvstart = 0; kvstart < querylen; kvstart += kvlen) {
Value stored to 'kvlen' is never read
357 /* get query section length */
358 kvlen = 0;
359 while (((kvstart + kvlen) < querylen) &&
360 (querystr[kvstart + kvlen] != '&')) {
361 kvlen++;
362 }
363
364 res = process_query_section(querystr + kvstart, kvlen, chart);
365 if (res != NSERROR_OK) {
366 break;
367 }
368 kvlen++; /* account for & separator */
369 }
370 free(querystr);
371
372 /* sanity check dimensions */
373 if (chart->width < FIGURE_MIN_WIDTH150) {
374 /* bad width - check height */
375 if (chart->height < FIGURE_MIN_HEIGHT100) {
376 /* both bad set to defaults */
377 chart->width = FIGURE_MIN_WIDTH150;
378 chart->height = FIGURE_MIN_HEIGHT100;
379 } else {
380 /* base width on valid height */
381 chart->width = (chart->height * 3) / 2;
382 }
383 } else {
384 /* good width check height */
385 if (chart->height < FIGURE_MIN_HEIGHT100) {
386 /* base height on valid width */
387 chart->height = (chart->width * 2) / 3;
388 }
389 }
390
391 /* ensure legend type correct */
392 if ((chart->key == CHART_KEY_UNSET) ||
393 (chart->key >= CHART_KEY_END )) {
394 /* default to putting key on right */
395 chart->key = CHART_KEY_RIGHT;
396 }
397
398 return NSERROR_OK;
399}
400
401
402static nserror
403output_pie_legend(struct fetch_about_context *ctx, struct chart_param *chart)
404{
405 nserror res;
406 unsigned int lblidx;
407 unsigned int legend_width;
408 unsigned int legend_height;
409 unsigned int vertical_spacing;
410
411 switch (chart->key) {
412
413 case CHART_KEY_NONE:
414 break;
415 case CHART_KEY_RIGHT:
416 legend_width = chart->width - chart->area.width - chart->area.x;
417 legend_width -= 10; /* margin */
418 legend_height = chart->height;
419 vertical_spacing = legend_height / (chart->data.label_len + 1);
420
421 for(lblidx = 0; lblidx < chart->data.label_len ; lblidx++) {
422 res = fetch_about_ssenddataf(ctx,
423 "<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" fill=\"#%06x\" />",
424 chart->width - legend_width,
425 (vertical_spacing * lblidx) + (vertical_spacing/2),
426 vertical_spacing * 2 / 3,
427 vertical_spacing * 2 / 3,
428 chart->data.label[lblidx].colour);
429 if (res != NSERROR_OK) {
430 return res;
431 }
432 res = fetch_about_ssenddataf(ctx,
433 "<text x=\"%d\" y=\"%d\" fill=\"#%06x\" >%s</text>",
434 chart->width - legend_width + vertical_spacing,
435 vertical_spacing * (lblidx+1),
436 chart->data.label[lblidx].colour,
437 chart->data.label[lblidx].title);
438 if (res != NSERROR_OK) {
439 return res;
440 }
441 }
442 break;
443 default:
444 break;
445 }
446
447 return NSERROR_OK;
448}
449
450static float
451compute_series_total(struct chart_param *chart, unsigned int series)
452{
453 float total;
454 unsigned int curdata;
455
456 for (total = 0, curdata = 0;
457 curdata < chart->data.series[series].len;
458 curdata++) {
459 total += chart->data.series[series].value[curdata];
460 }
461 return total;
462}
463
464/**
465 * render the data as a pie chart svg
466 */
467static bool_Bool
468pie_chart(struct fetch_about_context *ctx, struct chart_param *chart)
469{
470 nserror res;
471 float ra; /* pie a radius */
472 float rb; /* pie b radius */
473 float series_total;
474 unsigned int curdata; /* current data point index */
475 float last_x, last_y;
476 float end_x, end_y;
477 float start;
478 float extent;
479 bool_Bool large;
480 float circle_centre_x, circle_centre_y;
481
482 /* ensure there is data to render */
483 if ((chart->data.series_len < 1) || (chart->data.series[0].len < 2)) {
484 return NSERROR_BAD_PARAMETER;
485 }
486
487 /* get the first series total value */
488 series_total = compute_series_total(chart, 0);
489 if (series_total == 0) {
490 /* dividing by zero is embarasing */
491 return NSERROR_BAD_PARAMETER;
492 }
493
494 /*
495 * need to ensure the chart area is setup correctly
496 *
497 * this is left to each chart type as different charts
498 * have differnt requirements
499 */
500 if ((chart->area.width == 0) || (chart->area.height == 0)) {
501 /*
502 * pie chart defaults to square of smaller of figure
503 * width and height
504 */
505 if (chart->width > chart->height) {
506 chart->area.width = chart->area.height = (chart->height - chart->area.x);
507 } else {
508 chart->area.width = chart->area.height = (chart->width - chart->area.y);
509 }
510 }
511
512 /* content is going to return ok */
513 fetch_about_set_http_code(ctx, 200);
514
515 /* content type */
516 if (fetch_about_send_header(ctx,
517 "Content-Type: image/svg; charset=utf-8")) {
518 goto aborted;
519 }
520
521 /* get the pie charts elipse radii */
522 ra = chart->area.width / 2;
523 rb = chart->area.height / 2;
524
525 /* get the offset to the circle centre */
526 circle_centre_x = chart->area.x + ra;
527 circle_centre_y = chart->area.y + rb;
528
529
530 /* svg header */
531 res = fetch_about_ssenddataf(ctx,
532 "<svg width=\"%u\" height=\"%u\" "
533 "xmlns=\"http://www.w3.org/2000/svg\">\n",
534 chart->width, chart->height);
535 if (res != NSERROR_OK) {
536 goto aborted;
537 }
538
539 /* generate the legend */
540 res = output_pie_legend(ctx, chart);
541 if (res != NSERROR_OK) {
542 goto aborted;
543 }
544
545 /* plot the arcs */
546 start = -M_PI_21.57079632679489661923;
547 last_x = (ra * cos(start));
548 last_y = (rb * sin(start));
549
550 /* iterate over each data point creating a slice o pie */
551 for (curdata=0; curdata < chart->data.series[0].len; curdata++) {
552 extent = ((chart->data.series[0].value[curdata] / series_total) * 2 * M_PI3.14159265358979323846);
553 end_x = (ra * cos(start + extent));
554 end_y = (rb * sin(start + extent));
555
556 if (extent > M_PI3.14159265358979323846) {
557 large = true1;
558 } else {
559 large = false0;
560 }
561
562 res = fetch_about_ssenddataf(
563 ctx,
564 "<path d=\"M %g %g\n"
565 "A %g %g 0 %d 1 %g %g\n"
566 "L %g %g Z\" fill=\"#%06x\" />\n",
567 circle_centre_x + last_x,
568 circle_centre_y + last_y,
569 ra, rb, large?1:0,
570 circle_centre_x + end_x,
571 circle_centre_y + end_y,
572 circle_centre_x,
573 circle_centre_y,
574 chart->data.label[curdata].colour);
575 if (res != NSERROR_OK) {
576 goto aborted;
577 }
578 last_x = end_x;
579 last_y = end_y;
580 start +=extent;
581 }
582
583 res = fetch_about_ssenddataf(ctx, "</svg>\n");
584 if (res != NSERROR_OK) {
585 goto aborted;
586 }
587
588 fetch_about_send_finished(ctx);
589
590 return true1;
591
592 aborted:
593
594 return false0;
595
596}
597
598/**
599 * Handler to generate about scheme chart page.
600 *
601 * generates an svg chart
602 *
603 * \param ctx The fetcher context.
604 * \return true if handled false if aborted.
605 */
606bool_Bool fetch_about_chart_handler(struct fetch_about_context *ctx)
607{
608 nserror res;
609 struct chart_param chart;
610 memset(&chart, 0, sizeof(struct chart_param));
611
612 res = chart_from_query(fetch_about_get_url(ctx), &chart);
613 if (res != NSERROR_OK) {
614 goto aborted;
615 }
616
617 switch (chart.type) {
618 case CHART_TYPE_PIE:
619 return pie_chart(ctx, &chart);
620
621
622 default:
623 break;
624 }
625
626aborted:
627
628 return false0;
629
630}