Bug Summary

File:content/fetchers/about/chart.c
Warning:line 260, column 19
Although the value stored to 'vallen' is used in the enclosing expression, the value is never actually read from 'vallen'

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-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-09-27-095240-3006188-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;
Although the value stored to 'vallen' is used in the enclosing expression, the value is never actually read from 'vallen'
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) {
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}