Bug Summary

File:renderer.c
Warning:line 218, column 34
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'current_source')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name renderer.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-eagerly-assume -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 -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/local/clang-7.0.0/lib/clang/7.0.0 -D HAVE_CONFIG_H -I . -I /home/travis/build/blogc/blogc -internal-isystem /usr/local/include -internal-isystem /usr/local/clang-7.0.0/lib/clang/7.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -fdebug-compilation-dir /home/travis/build/blogc/blogc/build -ferror-limit 19 -fmessage-length 0 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -o /home/travis/build/blogc/blogc/build/reports/2018-12-20-003316-8439-1 -x c /home/travis/build/blogc/blogc/src/blogc/renderer.c -faddrsig
1/*
2 * blogc: A blog compiler.
3 * Copyright (C) 2014-2018 Rafael G. Martins <rafael@rafaelmartins.eng.br>
4 *
5 * This program can be distributed under the terms of the BSD License.
6 * See the file LICENSE.
7 */
8
9#include <stdbool.h>
10#include <stddef.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include "datetime-parser.h"
15#include "template-parser.h"
16#include "renderer.h"
17#include "../common/error.h"
18#include "../common/utils.h"
19
20
21const char*
22blogc_get_variable(const char *name, bc_trie_t *global, bc_trie_t *local)
23{
24 const char *rv = NULL((void*)0);
25 if (local != NULL((void*)0)) {
26 rv = bc_trie_lookup(local, name);
27 if (rv != NULL((void*)0))
28 return rv;
29 }
30 if (global != NULL((void*)0))
31 rv = bc_trie_lookup(global, name);
32 return rv;
33}
34
35
36char*
37blogc_format_date(const char *date, bc_trie_t *global, bc_trie_t *local)
38{
39 const char *date_format = blogc_get_variable("DATE_FORMAT", global, local);
40 if (date == NULL((void*)0))
41 return NULL((void*)0);
42 if (date_format == NULL((void*)0))
43 return bc_strdup(date);
44
45 bc_error_t *err = NULL((void*)0);
46 char *rv = blogc_convert_datetime(date, date_format, &err);
47 if (err != NULL((void*)0)) {
48 bc_error_print(err, "blogc");
49 bc_error_free(err);
50 return bc_strdup(date);
51 }
52 return rv;
53}
54
55
56char*
57blogc_format_variable(const char *name, bc_trie_t *global, bc_trie_t *local,
58 bc_slist_t *foreach_var)
59{
60 // if used asked for a variable that exists, just return it right away
61 const char *value = blogc_get_variable(name, global, local);
62 if (value != NULL((void*)0))
63 return bc_strdup(value);
64
65 // do the same for special variable 'FOREACH_ITEM'
66 if (0 == strcmp(name, "FOREACH_ITEM")) {
67 if (foreach_var != NULL((void*)0) && foreach_var->data != NULL((void*)0)) {
68 return bc_strdup(foreach_var->data);
69 }
70 return NULL((void*)0);
71 }
72
73 char *var = bc_strdup(name);
74
75 size_t i;
76 size_t last = strlen(var);
77
78 long int len = -1;
79
80 // just walk till the last '_'
81 for (i = last - 1; i > 0 && var[i] >= '0' && var[i] <= '9'; i--);
82
83 if (var[i] == '_' && (i + 1) < last) { // var ends with '_[0-9]+'
84 char *endptr;
85 len = strtol(var + i + 1, &endptr, 10);
86 if (*endptr != '\0') {
87 fprintf(stderrstderr, "warning: invalid variable size for '%s', "
88 "ignoring.\n", var);
89 len = -1;
90 }
91 else {
92 var[i] = '\0';
93 }
94 }
95
96 bool_Bool must_format = false0;
97
98 if (bc_str_ends_with(var, "_FORMATTED")) {
99 var[strlen(var) - 10] = '\0';
100 must_format = true1;
101 }
102
103 if ((0 == strcmp(var, "FOREACH_ITEM")) &&
104 (foreach_var != NULL((void*)0) && foreach_var->data != NULL((void*)0)))
105 value = foreach_var->data;
106 else
107 value = blogc_get_variable(var, global, local);
108
109 if (value == NULL((void*)0)) {
110 free(var);
111 return NULL((void*)0);
112 }
113
114 char *rv = NULL((void*)0);
115
116 if (must_format) {
117 if (bc_str_starts_with(name, "DATE_")) {
118 rv = blogc_format_date(value, global, local);
119 }
120 else {
121 fprintf(stderrstderr, "warning: no formatter found for '%s', "
122 "ignoring.\n", var);
123 rv = bc_strdup(value);
124 }
125 }
126 else {
127 rv = bc_strdup(value);
128 }
129
130 free(var);
131
132 if (len > 0) {
133 char *tmp = bc_strndup(rv, len);
134 free(rv);
135 rv = tmp;
136 }
137
138 return rv;
139}
140
141
142bc_slist_t*
143blogc_split_list_variable(const char *name, bc_trie_t *global, bc_trie_t *local)
144{
145 const char *value = blogc_get_variable(name, global, local);
146 if (value == NULL((void*)0))
147 return NULL((void*)0);
148
149 bc_slist_t *rv = NULL((void*)0);
150
151 char **tmp = bc_str_split(value, ' ', 0);
152 for (size_t i = 0; tmp[i] != NULL((void*)0); i++) {
153 if (tmp[i][0] != '\0') // ignore empty strings
154 rv = bc_slist_append(rv, tmp[i]);
155 else
156 free(tmp[i]);
157 }
158 free(tmp);
159
160 return rv;
161}
162
163
164char*
165blogc_render(bc_slist_t *tmpl, bc_slist_t *sources, bc_trie_t *config, bool_Bool listing)
166{
167 if (tmpl == NULL((void*)0))
1
Assuming 'tmpl' is not equal to NULL
2
Taking false branch
168 return NULL((void*)0);
169
170 bc_slist_t *current_source = NULL((void*)0);
171 bc_slist_t *listing_start = NULL((void*)0);
172
173 bc_string_t *str = bc_string_new();
174
175 bc_trie_t *tmp_source = NULL((void*)0);
176 char *config_value = NULL((void*)0);
177 char *defined = NULL((void*)0);
178
179 size_t if_count = 0;
180
181 bc_slist_t *foreach_var = NULL((void*)0);
182 bc_slist_t *foreach_var_start = NULL((void*)0);
183 bc_slist_t *foreach_start = NULL((void*)0);
184
185 bool_Bool if_not = false0;
186 bool_Bool inside_block = false0;
187 bool_Bool evaluate = false0;
188 bool_Bool valid_else = false0;
189
190 int cmp = 0;
191
192 bc_slist_t *tmp = tmpl;
193 while (tmp != NULL((void*)0)) {
3
Loop condition is true. Entering loop body
17
Assuming 'tmp' is not equal to NULL
18
Loop condition is true. Entering loop body
194 blogc_template_node_t *node = tmp->data;
195
196 switch (node->type) {
4
Control jumps to 'case BLOGC_TEMPLATE_NODE_BLOCK:' at line 203
19
Control jumps to 'case BLOGC_TEMPLATE_NODE_BLOCK:' at line 203
197
198 case BLOGC_TEMPLATE_NODE_CONTENT:
199 if (node->data[0] != NULL((void*)0))
200 bc_string_append(str, node->data[0]);
201 break;
202
203 case BLOGC_TEMPLATE_NODE_BLOCK:
204 inside_block = true1;
205 if_count = 0;
206 if (0 == strcmp("entry", node->data[0])) {
5
Assuming the condition is false
6
Taking false branch
20
Taking true branch
207 if (listing) {
21
Assuming 'listing' is 0
22
Taking false branch
208
209 // we can just skip anything and walk until the next
210 // 'endblock'
211 while (node->type != BLOGC_TEMPLATE_NODE_ENDBLOCK) {
212 tmp = tmp->next;
213 node = tmp->data;
214 }
215 break;
216 }
217 current_source = sources;
23
Null pointer value stored to 'current_source'
218 tmp_source = current_source->data;
24
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'current_source')
219 }
220 else if ((0 == strcmp("listing", node->data[0])) ||
7
Assuming the condition is false
9
Taking false branch
221 (0 == strcmp("listing_once", node->data[0]))) {
8
Assuming the condition is false
222 if (!listing) {
223
224 // we can just skip anything and walk until the next
225 // 'endblock'
226 while (node->type != BLOGC_TEMPLATE_NODE_ENDBLOCK) {
227 tmp = tmp->next;
228 node = tmp->data;
229 }
230 break;
231 }
232 }
233 if (0 == strcmp("listing", node->data[0])) {
10
Taking true branch
234 if (sources == NULL((void*)0)) {
11
Assuming 'sources' is equal to NULL
12
Taking true branch
235
236 // we can just skip anything and walk until the next
237 // 'endblock'
238 while (node->type != BLOGC_TEMPLATE_NODE_ENDBLOCK) {
13
Loop condition is true. Entering loop body
14
Assuming the condition is false
15
Loop condition is false. Execution continues on line 242
239 tmp = tmp->next;
240 node = tmp->data;
241 }
242 break;
16
Execution continues on line 452
243 }
244 if (current_source == NULL((void*)0)) {
245 listing_start = tmp;
246 current_source = sources;
247 }
248 tmp_source = current_source->data;
249 }
250 break;
251
252 case BLOGC_TEMPLATE_NODE_VARIABLE:
253 if (node->data[0] != NULL((void*)0)) {
254 config_value = blogc_format_variable(node->data[0],
255 config, inside_block ? tmp_source : NULL((void*)0), foreach_var);
256 if (config_value != NULL((void*)0)) {
257 bc_string_append(str, config_value);
258 free(config_value);
259 config_value = NULL((void*)0);
260 break;
261 }
262 }
263 break;
264
265 case BLOGC_TEMPLATE_NODE_ENDBLOCK:
266 inside_block = false0;
267 if (listing_start != NULL((void*)0) && current_source != NULL((void*)0)) {
268 current_source = current_source->next;
269 if (current_source != NULL((void*)0)) {
270 tmp = listing_start;
271 continue;
272 }
273 else
274 listing_start = NULL((void*)0);
275 }
276 break;
277
278 case BLOGC_TEMPLATE_NODE_IFNDEF:
279 if_not = true1;
280
281 case BLOGC_TEMPLATE_NODE_IF:
282 case BLOGC_TEMPLATE_NODE_IFDEF:
283 if_count = 0;
284 defined = NULL((void*)0);
285 if (node->data[0] != NULL((void*)0))
286 defined = blogc_format_variable(node->data[0], config,
287 inside_block ? tmp_source : NULL((void*)0), foreach_var);
288 evaluate = false0;
289 if (node->op != 0) {
290 // Strings that start with a '"' are actually strings, the
291 // others are meant to be looked up as a second variable
292 // check.
293 char *defined2 = NULL((void*)0);
294 if (node->data[1] != NULL((void*)0)) {
295 if ((strlen(node->data[1]) >= 2) &&
296 (node->data[1][0] == '"') &&
297 (node->data[1][strlen(node->data[1]) - 1] == '"'))
298 {
299 defined2 = bc_strndup(node->data[1] + 1,
300 strlen(node->data[1]) - 2);
301 }
302 else {
303 defined2 = blogc_format_variable(node->data[1],
304 config, inside_block ? tmp_source : NULL((void*)0),
305 foreach_var);
306 }
307 }
308
309 if (defined != NULL((void*)0) && defined2 != NULL((void*)0)) {
310 cmp = strcmp(defined, defined2);
311 if (cmp != 0 && node->op & BLOGC_TEMPLATE_OP_NEQ)
312 evaluate = true1;
313 else if (cmp == 0 && node->op & BLOGC_TEMPLATE_OP_EQ)
314 evaluate = true1;
315 else if (cmp < 0 && node->op & BLOGC_TEMPLATE_OP_LT)
316 evaluate = true1;
317 else if (cmp > 0 && node->op & BLOGC_TEMPLATE_OP_GT)
318 evaluate = true1;
319 }
320
321 free(defined2);
322 }
323 else {
324 if (if_not && defined == NULL((void*)0))
325 evaluate = true1;
326 if (!if_not && defined != NULL((void*)0))
327 evaluate = true1;
328 }
329 if (!evaluate) {
330
331 // at this point we can just skip anything, counting the
332 // number of 'if's, to know how many 'endif's we need to
333 // skip as well.
334 while (1) {
335 tmp = tmp->next;
336 node = tmp->data;
337 if ((node->type == BLOGC_TEMPLATE_NODE_IF) ||
338 (node->type == BLOGC_TEMPLATE_NODE_IFDEF) ||
339 (node->type == BLOGC_TEMPLATE_NODE_IFNDEF))
340 {
341 if_count++;
342 continue;
343 }
344 if ((node->type == BLOGC_TEMPLATE_NODE_ELSE) &&
345 (if_count == 0))
346 {
347 // this is somewhat complex. only an else statement
348 // right after a non evaluated block should be considered
349 // valid, because all the inner conditionals were just
350 // skipped, and all the outter conditionals evaluated
351 // to true.
352 valid_else = true1;
353 break;
354 }
355 if (node->type == BLOGC_TEMPLATE_NODE_ENDIF) {
356 if (if_count > 0) {
357 if_count--;
358 continue;
359 }
360 break;
361 }
362 }
363 }
364 else {
365 valid_else = false0;
366 }
367 free(defined);
368 defined = NULL((void*)0);
369 if_not = false0;
370 break;
371
372 case BLOGC_TEMPLATE_NODE_ELSE:
373 if_count = 0;
374 if (!valid_else) {
375
376 // at this point we can just skip anything, counting the
377 // number of 'if's, to know how many 'endif's we need to
378 // skip as well.
379 while (1) {
380 tmp = tmp->next;
381 node = tmp->data;
382 if ((node->type == BLOGC_TEMPLATE_NODE_IF) ||
383 (node->type == BLOGC_TEMPLATE_NODE_IFDEF) ||
384 (node->type == BLOGC_TEMPLATE_NODE_IFNDEF))
385 {
386 if_count++;
387 continue;
388 }
389 // no need to handle else statements here, because every
390 // if should have an endif.
391 if (node->type == BLOGC_TEMPLATE_NODE_ENDIF) {
392 if (if_count > 0) {
393 if_count--;
394 continue;
395 }
396 break;
397 }
398 }
399 }
400 valid_else = false0;
401 break;
402
403 case BLOGC_TEMPLATE_NODE_ENDIF:
404 // any endif statement should invalidate valid_else, to avoid
405 // propagation to outter conditionals.
406 valid_else = false0;
407 if (if_count > 0)
408 if_count--;
409 break;
410
411 case BLOGC_TEMPLATE_NODE_FOREACH:
412 if (foreach_var_start == NULL((void*)0)) {
413 if (node->data[0] != NULL((void*)0))
414 foreach_var_start = blogc_split_list_variable(node->data[0],
415 config, inside_block ? tmp_source : NULL((void*)0));
416
417 if (foreach_var_start != NULL((void*)0)) {
418 foreach_var = foreach_var_start;
419 foreach_start = tmp;
420 }
421 else {
422
423 // we can just skip anything and walk until the next
424 // 'endforeach'
425 while (node->type != BLOGC_TEMPLATE_NODE_ENDFOREACH) {
426 tmp = tmp->next;
427 node = tmp->data;
428 }
429 break;
430 }
431 }
432
433 if (foreach_var == NULL((void*)0)) {
434 foreach_start = tmp;
435 foreach_var = foreach_var_start;
436 }
437 break;
438
439 case BLOGC_TEMPLATE_NODE_ENDFOREACH:
440 if (foreach_start != NULL((void*)0) && foreach_var != NULL((void*)0)) {
441 foreach_var = foreach_var->next;
442 if (foreach_var != NULL((void*)0)) {
443 tmp = foreach_start;
444 continue;
445 }
446 }
447 foreach_start = NULL((void*)0);
448 bc_slist_free_full(foreach_var_start, free);
449 foreach_var_start = NULL((void*)0);
450 break;
451 }
452 tmp = tmp->next;
453 }
454
455 // no need to free temporary variables here. the template parser makes sure
456 // that templates are sane and statements are closed.
457
458 return bc_string_free(str, false0);
459}