Bug Summary

File:lwan-template.c
Location:line 208, column 17
Description:Potential leak of memory pointed to by 'tab'

Annotated Source Code

1/*
2 * lwan - simple web server
3 * Copyright (c) 2012 Leandro A. F. Pereira <leandro@hardinfo.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or any later version.
9 *
10 * This program 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19/*
20 * Ideas from Mustache logic-less templates: http://mustache.github.com/
21 * Lexer+parser implemented using ideas from Rob Pike's talk "Lexical Scanning
22 * in Go" (https://www.youtube.com/watch?v=HxaD_trXwRE).
23 */
24
25#define _GNU_SOURCE
26#include <ctype.h>
27#include <errno(*__errno_location ()).h>
28#include <fcntl.h>
29#include <limits.h>
30#include <stdarg.h>
31#include <stdbool.h>
32#include <stdint.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <sys/mman.h>
37#include <sys/stat.h>
38#include <unistd.h>
39
40#include "hash.h"
41#include "int-to-str.h"
42#include "list.h"
43#include "lwan-template.h"
44#include "strbuf.h"
45#include "reallocarray.h"
46
47enum action {
48 TPL_ACTION_APPEND,
49 TPL_ACTION_APPEND_CHAR,
50 TPL_ACTION_VARIABLE,
51 TPL_ACTION_VARIABLE_STR,
52 TPL_ACTION_VARIABLE_STR_ESCAPE,
53 TPL_ACTION_START_ITER,
54 TPL_ACTION_END_ITER,
55 TPL_ACTION_IF_VARIABLE_NOT_EMPTY,
56 TPL_ACTION_END_IF_VARIABLE_NOT_EMPTY,
57 TPL_ACTION_APPLY_TPL,
58 TPL_ACTION_LAST
59};
60
61enum flags {
62 FLAGS_ALL = -1,
63 FLAGS_NEGATE = 1<<0,
64 FLAGS_QUOTE = 1<<1
65};
66
67enum item_type {
68 ITEM_ERROR,
69 ITEM_EOF,
70 ITEM_IDENTIFIER,
71 ITEM_LEFT_META,
72 ITEM_HASH,
73 ITEM_RIGHT_META,
74 ITEM_TEXT,
75 ITEM_SLASH,
76 ITEM_QUESTION_MARK,
77 ITEM_HAT,
78 ITEM_GREATER_THAN,
79 ITEM_OPEN_CURLY_BRACE,
80 ITEM_CLOSE_CURLY_BRACE,
81 TOTAL_ITEMS
82};
83
84static const char *item_type_str[TOTAL_ITEMS] = {
85 [ITEM_ERROR] = "ERROR",
86 [ITEM_EOF] = "EOF",
87 [ITEM_IDENTIFIER] = "IDENTIFIER",
88 [ITEM_LEFT_META] = "LEFT_META",
89 [ITEM_HASH] = "HASH",
90 [ITEM_RIGHT_META] = "RIGHT_META",
91 [ITEM_TEXT] = "TEXT",
92 [ITEM_SLASH] = "SLASH",
93 [ITEM_QUESTION_MARK] = "QUESTION_MARK",
94 [ITEM_HAT] = "HAT",
95 [ITEM_GREATER_THAN] = "GREATER_THAN",
96 [ITEM_OPEN_CURLY_BRACE] = "ITEM_OPEN_CURLY_BRACE",
97 [ITEM_CLOSE_CURLY_BRACE] = "ITEM_CLOSE_CURLY_BRACE"
98};
99
100struct chunk {
101 enum action action;
102 void *data;
103 enum flags flags;
104};
105
106struct lwan_tpl_t_ {
107 struct chunk *chunks;
108 size_t minimum_size;
109};
110
111struct symtab {
112 struct hash *hash;
113 struct symtab *next;
114};
115
116struct item {
117 enum item_type type;
118 struct {
119 const char *value;
120 size_t len;
121 } value;
122};
123
124struct lexer {
125 void *(*state)(struct lexer *);
126 const char *start, *pos, *end;
127
128 struct {
129 struct item items[4];
130 size_t first;
131 size_t last;
132 size_t population;
133 } ring_buffer;
134};
135
136struct parser {
137 lwan_tpl_t *tpl;
138 struct symtab *symtab;
139 struct lexer lexer;
140 enum flags flags;
141 struct list_head stack;
142 struct {
143 struct chunk *data;
144 size_t used, reserved;
145 } chunks;
146};
147
148struct stacked_item {
149 struct list_node stack;
150 struct item item;
151};
152
153struct chunk_descriptor {
154 struct chunk *chunk;
155 lwan_var_descriptor_t *descriptor;
156};
157
158static const size_t array_increment_step = 16;
159
160static const char left_meta[] = "{{";
161static const char right_meta[] = "}}";
162
163static void *lex_inside_action(struct lexer *lexer);
164static void *lex_identifier(struct lexer *lexer);
165static void *lex_left_meta(struct lexer *lexer);
166static void *lex_right_meta(struct lexer *lexer);
167static void *lex_text(struct lexer *lexer);
168
169static void *parser_meta(struct parser *parser, struct item *item);
170static void *parser_text(struct parser *parser, struct item *item);
171static void *parser_iter(struct parser *parser, struct item *item);
172static void *parser_slash(struct parser *parser, struct item *item);
173static void *parser_end_iter(struct parser *parser, struct item *item);
174static void *parser_end_var_not_empty(struct parser *parser, struct item *item);
175static void *parser_slash(struct parser *parser, struct item *item);
176static void *parser_iter(struct parser *parser, struct item *item);
177static void *parser_negate_iter(struct parser *parser, struct item *item);
178static void *parser_meta(struct parser *parser, struct item *item);
179static void *parser_text(struct parser *parser, struct item *item);
180
181static void *error_vitem(struct item *item, const char *msg, va_list ap)
182 __attribute__((format(printf, 2, 0)));
183static void *error_item(struct item *item, const char *msg, ...)
184 __attribute__((format(printf, 2, 3)));
185static void *lex_error(struct lexer *lexer, const char *msg, ...)
186 __attribute__((format(printf, 2, 3)));
187
188static lwan_var_descriptor_t *
189symtab_lookup(struct parser *parser, const char *var_name)
190{
191 for (struct symtab *tab = parser->symtab; tab; tab = tab->next) {
192 lwan_var_descriptor_t *var = hash_find(tab->hash, var_name);
193 if (var)
194 return var;
195 }
196
197 return NULL((void*)0);
198}
199
200static int
201symtab_push(struct parser *parser, const lwan_var_descriptor_t *descriptor)
202{
203 struct symtab *tab = malloc(sizeof(*tab));
6
Memory is allocated
204
205 if (!tab)
7
Assuming 'tab' is non-null
8
Taking false branch
206 return -errno(*__errno_location ());
207 if (!descriptor)
9
Assuming 'descriptor' is null
10
Taking true branch
208 return -ENODEV19;
11
Within the expansion of the macro 'ENODEV':
a
Potential leak of memory pointed to by 'tab'
209
210 tab->hash = hash_str_new(NULL((void*)0), NULL((void*)0));
211 if (!tab->hash) {
212 free(tab);
213 return -ENOMEM12;
214 }
215
216 tab->next = parser->symtab;
217 parser->symtab = tab;
218
219 for (; descriptor->name; descriptor++)
220 hash_add(parser->symtab->hash, descriptor->name, descriptor);
221
222 return 0;
223}
224
225static void
226symtab_pop(struct parser *parser)
227{
228 struct symtab *tab = parser->symtab;
229
230 assert(tab)((tab) ? (void) (0) : __assert_fail ("tab", "/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 230, __PRETTY_FUNCTION__))
;
231
232 hash_free(tab->hash);
233 parser->symtab = tab->next;
234 free(tab);
235}
236
237static void emit_item(struct lexer *lexer, struct item *item)
238{
239 lexer->ring_buffer.items[lexer->ring_buffer.last] = *item;
240 lexer->ring_buffer.last = (lexer->ring_buffer.last + 1) % N_ELEMENTS(lexer->ring_buffer.items)(sizeof(lexer->ring_buffer.items) / sizeof(lexer->ring_buffer
.items[0]))
;
241 lexer->ring_buffer.population++;
242
243 lexer->start = lexer->pos;
244}
245
246static bool_Bool consume_item(struct lexer *lexer, struct item **item)
247{
248 if (!lexer->ring_buffer.population)
249 return false0;
250
251 *item = &lexer->ring_buffer.items[lexer->ring_buffer.first];
252 lexer->ring_buffer.first = (lexer->ring_buffer.first + 1) % N_ELEMENTS(lexer->ring_buffer.items)(sizeof(lexer->ring_buffer.items) / sizeof(lexer->ring_buffer
.items[0]))
;
253 lexer->ring_buffer.population--;
254
255 return true1;
256}
257
258static void emit(struct lexer *lexer, enum item_type item_type)
259{
260 struct item item = {
261 .type = item_type,
262 .value = {
263 .value = lexer->start,
264 .len = (size_t)(lexer->pos - lexer->start)
265 }
266 };
267 emit_item(lexer, &item);
268}
269
270static int next(struct lexer *lexer)
271{
272 if (lexer->pos >= lexer->end)
273 return EOF(-1);
274 int r = *lexer->pos;
275 lexer->pos++;
276 return r;
277}
278
279static void ignore(struct lexer *lexer)
280{
281 lexer->start = lexer->pos;
282}
283
284static void backup(struct lexer *lexer)
285{
286 lexer->pos--;
287}
288
289static void *error_vitem(struct item *item, const char *msg, va_list ap)
290{
291 int r;
292
293 item->type = ITEM_ERROR;
294
295 r = vasprintf((char **)&item->value.value, msg, ap);
296 if (r < 0) {
297 item->value.value = strdup(strerror(errno(*__errno_location ())));
298 if (!item->value.value)
299 return NULL((void*)0);
300
301 item->value.len = strlen(item->value.value);
302 } else {
303 item->value.len = (size_t)r;
304 }
305
306 return NULL((void*)0);
307}
308
309static void *error_item(struct item *item, const char *msg, ...)
310{
311 void *ret;
312 va_list ap;
313
314 va_start(ap, msg)__builtin_va_start(ap, msg);
315 ret = error_vitem(item, msg, ap);
316 va_end(ap)__builtin_va_end(ap);
317
318 return ret;
319}
320
321static void *lex_error(struct lexer *lexer, const char *msg, ...)
322{
323 struct item item;
324 va_list ap;
325
326 va_start(ap, msg)__builtin_va_start(ap, msg);
327 error_vitem(&item, msg, ap);
328 va_end(ap)__builtin_va_end(ap);
329
330 emit_item(lexer, &item);
331 return NULL((void*)0);
332}
333
334static bool_Bool isident(int ch)
335{
336 return isalnum(ch)((*__ctype_b_loc ())[(int) ((ch))] & (unsigned short int)
_ISalnum)
|| ch == '_' || ch == '.';
337}
338
339static void *lex_identifier(struct lexer *lexer)
340{
341 while (isident(next(lexer)))
342 ;
343 backup(lexer);
344 emit(lexer, ITEM_IDENTIFIER);
345 return lex_inside_action;
346}
347
348static void *lex_quoted_identifier(struct lexer *lexer)
349{
350 int r;
351
352 emit(lexer, ITEM_OPEN_CURLY_BRACE);
353 lex_identifier(lexer);
354
355 r = next(lexer);
356 if (r != '}')
357 return lex_error(lexer, "expecting `}', found `%c'", r);
358
359 emit(lexer, ITEM_CLOSE_CURLY_BRACE);
360 return lex_inside_action;
361}
362
363static void *lex_comment(struct lexer *lexer)
364{
365 size_t brackets = strlen(left_meta);
366
367 assert(strlen(left_meta) == strlen(right_meta))((strlen(left_meta) == strlen(right_meta)) ? (void) (0) : __assert_fail
("strlen(left_meta) == strlen(right_meta)", "/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 367, __PRETTY_FUNCTION__))
;
368
369 do {
370 int r = next(lexer);
371 if (r == '{')
372 brackets++;
373 else if (r == '}')
374 brackets--;
375 else if (r == EOF(-1))
376 return lex_error(lexer, "unexpected EOF while scanning comment end");
377 } while (brackets);
378
379 ignore(lexer);
380 return lex_text;
381}
382
383static void *lex_inside_action(struct lexer *lexer)
384{
385 while (true1) {
386 int r;
387
388 if (!strncmp(lexer->pos, right_meta, strlen(right_meta)))
389 return lex_right_meta;
390
391 r = next(lexer);
392 if (r == EOF(-1))
393 return lex_error(lexer, "unexpected EOF while scanning action");
394 if (r == '\n')
395 return lex_error(lexer, "actions cannot span multiple lines");
396
397 if (isspace(r)((*__ctype_b_loc ())[(int) ((r))] & (unsigned short int) _ISspace
)
) {
398 ignore(lexer);
399 } else if (r == '#') {
400 emit(lexer, ITEM_HASH);
401 } else if (r == '/') {
402 emit(lexer, ITEM_SLASH);
403 } else if (r == '?') {
404 emit(lexer, ITEM_QUESTION_MARK);
405 } else if (r == '^') {
406 emit(lexer, ITEM_HAT);
407 } else if (r == '>') {
408 emit(lexer, ITEM_GREATER_THAN);
409 } else if (r == '{') {
410 return lex_quoted_identifier;
411 } else if (isalnum(r)((*__ctype_b_loc ())[(int) ((r))] & (unsigned short int) _ISalnum
)
|| r == '_') {
412 backup(lexer);
413 return lex_identifier;
414 } else {
415 return lex_error(lexer, "unexpected character: %c", r);
416 }
417 }
418}
419
420static void *lex_left_meta(struct lexer *lexer)
421{
422 lexer->pos += strlen(left_meta);
423 int r = next(lexer);
424 if (r == '!')
425 return lex_comment;
426 backup(lexer);
427
428 emit(lexer, ITEM_LEFT_META);
429 return lex_inside_action;
430}
431
432static void *lex_right_meta(struct lexer *lexer)
433{
434 lexer->pos += strlen(right_meta);
435 emit(lexer, ITEM_RIGHT_META);
436 return lex_text;
437}
438
439static void *lex_text(struct lexer *lexer)
440{
441 do {
442 if (!strncmp(lexer->pos, left_meta, strlen(left_meta))) {
443 if (lexer->pos > lexer->start)
444 emit(lexer, ITEM_TEXT);
445 return lex_left_meta;
446 }
447 if (!strncmp(lexer->pos, right_meta, strlen(right_meta)))
448 return lex_error(lexer, "unexpected action close sequence");
449 } while (next(lexer) != EOF(-1));
450 if (lexer->pos > lexer->start)
451 emit(lexer, ITEM_TEXT);
452 emit(lexer, ITEM_EOF);
453 return NULL((void*)0);
454}
455
456static bool_Bool lex_next(struct lexer *lexer, struct item **item)
457{
458 while (lexer->state) {
459 if (consume_item(lexer, item))
460 return true1;
461 lexer->state = lexer->state(lexer);
462 }
463
464 return consume_item(lexer, item);
465}
466
467static void lex_init(struct lexer *lexer, const char *input)
468{
469 lexer->state = lex_text;
470 lexer->pos = lexer->start = input;
471 lexer->end = rawmemchr(input, '\0');
472}
473
474static void *unexpected_lexeme(struct item *item)
475{
476 return error_item(item, "unexpected lexeme: %s [%.*s]",
477 item_type_str[item->type], (int)item->value.len, item->value.value);
478}
479
480static void *unexpected_lexeme_or_lex_error(struct item *item, struct item *lex_error)
481{
482 if (lex_error && (lex_error->type == ITEM_ERROR || lex_error->type == ITEM_EOF)) {
483 *item = *lex_error;
484 return NULL((void*)0);
485 }
486
487 return unexpected_lexeme(item);
488}
489
490static bool_Bool parser_next_is(struct parser *parser, enum item_type type)
491{
492 struct item *item;
493 return lex_next(&parser->lexer, &item) ? item->type == type : false0;
494}
495
496static void parser_push_item(struct parser *parser, struct item *item)
497{
498 struct stacked_item *stacked_item = malloc(sizeof(*stacked_item));
499 if (!stacked_item)
500 lwan_status_critical_perror("Could not push parser item")lwan_status_critical_perror_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 500, __FUNCTION__, "Could not push parser item")
;
501
502 stacked_item->item = *item;
503 list_add(&parser->stack, &stacked_item->stack);
504}
505
506static void emit_chunk(struct parser *parser, enum action action,
507 enum flags flags, void *data)
508{
509 struct chunk *chunk;
510
511 if (parser->chunks.used >= parser->chunks.reserved) {
512 parser->chunks.reserved += array_increment_step;
513
514 chunk = reallocarray(parser->chunks.data,
515 parser->chunks.reserved, sizeof(struct chunk));
516 if (!chunk)
517 lwan_status_critical_perror("Could not emit template chunk")lwan_status_critical_perror_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 517, __FUNCTION__, "Could not emit template chunk")
;
518
519 parser->chunks.data = chunk;
520 }
521
522 chunk = &parser->chunks.data[parser->chunks.used++];
523 chunk->action = action;
524 chunk->flags = flags;
525 chunk->data = data;
526}
527
528static bool_Bool parser_stack_top_matches(struct parser *parser, struct item *item, enum item_type type)
529{
530 if (list_empty(&parser->stack)) {
531 error_item(item, "unexpected {{/%.*s}}", (int)item->value.len, item->value.value);
532 return false0;
533 }
534
535 struct stacked_item *stacked_item = (struct stacked_item *)parser->stack.n.next;
536 bool_Bool matches = (stacked_item->item.type == type
537 && item->value.len == stacked_item->item.value.len
538 && !memcmp(stacked_item->item.value.value, item->value.value, item->value.len));
539 if (matches) {
540 list_del(&stacked_item->stack);
541 free(stacked_item);
542 return true1;
543 }
544
545 error_item(item, "expecting %s `%.*s' but found `%.*s'",
546 item_type_str[stacked_item->item.type],
547 (int)stacked_item->item.value.len, stacked_item->item.value.value,
548 (int)item->value.len, item->value.value);
549 return false0;
550}
551
552static void *parser_end_iter(struct parser *parser, struct item *item)
553{
554 struct chunk *iter;
555 lwan_var_descriptor_t *symbol;
556 ssize_t idx;
557
558 if (!parser_stack_top_matches(parser, item, ITEM_IDENTIFIER))
559 return NULL((void*)0);
560
561 symbol = symtab_lookup(parser, strndupa(item->value.value, item->value.len)(__extension__ ({ const char *__old = (item->value.value);
size_t __len = strnlen (__old, (item->value.len)); char *
__new = (char *) __builtin_alloca (__len + 1); __new[__len] =
'\0'; (char *) memcpy (__new, __old, __len); }))
);
562 if (!symbol) {
563 return error_item(item, "Unknown variable: %.*s", (int)item->value.len,
564 item->value.value);
565 }
566
567 if (!parser->chunks.used)
568 return error_item(item, "No chunks were emitted but parsing end iter");
569 for (idx = (ssize_t)parser->chunks.used - 1; idx >= 0; idx--) {
570 iter = &parser->chunks.data[idx];
571
572 if (iter->action != TPL_ACTION_START_ITER)
573 continue;
574 if (iter->data == symbol) {
575 emit_chunk(parser, TPL_ACTION_END_ITER, 0, iter);
576 symtab_pop(parser);
577 return parser_text;
578 }
579 }
580
581 return error_item(item, "Could not find {{#%.*s}}", (int)item->value.len, item->value.value);
582}
583
584static void *parser_end_var_not_empty(struct parser *parser, struct item *item)
585{
586 struct chunk *iter;
587 lwan_var_descriptor_t *symbol;
588 ssize_t idx;
589
590 if (!parser_next_is(parser, ITEM_RIGHT_META))
591 return unexpected_lexeme(item);
592 if (!parser_stack_top_matches(parser, item, ITEM_IDENTIFIER))
593 return NULL((void*)0);
594
595 symbol = symtab_lookup(parser, strndupa(item->value.value, item->value.len)(__extension__ ({ const char *__old = (item->value.value);
size_t __len = strnlen (__old, (item->value.len)); char *
__new = (char *) __builtin_alloca (__len + 1); __new[__len] =
'\0'; (char *) memcpy (__new, __old, __len); }))
);
596 if (!symbol) {
597 return error_item(item, "Unknown variable: %.*s", (int)item->value.len,
598 item->value.value);
599 }
600
601 for (idx = (ssize_t)parser->chunks.used - 1; idx >= 0; idx--) {
602 iter = &parser->chunks.data[idx];
603 if (iter->action != TPL_ACTION_IF_VARIABLE_NOT_EMPTY)
604 continue;
605 if (iter->data == symbol) {
606 emit_chunk(parser, TPL_ACTION_END_IF_VARIABLE_NOT_EMPTY, 0, symbol);
607 return parser_text;
608 }
609 }
610
611 return error_item(item, "Could not find {{%.*s?}}", (int)item->value.len, item->value.value);
612}
613
614static void *parser_slash(struct parser *parser, struct item *item)
615{
616 if (item->type == ITEM_IDENTIFIER) {
617 struct item *next = NULL((void*)0);
618
619 if (!lex_next(&parser->lexer, &next))
620 return unexpected_lexeme_or_lex_error(item, next);
621
622 if (next->type == ITEM_RIGHT_META)
623 return parser_end_iter(parser, item);
624
625 if (next->type == ITEM_QUESTION_MARK)
626 return parser_end_var_not_empty(parser, item);
627
628 return unexpected_lexeme_or_lex_error(item, next);
629 }
630
631 return unexpected_lexeme(item);
632}
633
634static void *parser_iter(struct parser *parser, struct item *item)
635{
636 if (item->type == ITEM_IDENTIFIER) {
1
Taking true branch
637 enum flags negate = parser->flags & FLAGS_NEGATE;
638 const char *symname = strndupa(item->value.value, item->value.len)(__extension__ ({ const char *__old = (item->value.value);
size_t __len = strnlen (__old, (item->value.len)); char *
__new = (char *) __builtin_alloca (__len + 1); __new[__len] =
'\0'; (char *) memcpy (__new, __old, __len); }))
;
639 lwan_var_descriptor_t *symbol = symtab_lookup(parser, symname);
640 if (!symbol) {
2
Assuming 'symbol' is non-null
3
Taking false branch
641 return error_item(item, "Unknown variable: %.*s", (int)item->value.len,
642 item->value.value);
643 }
644
645 if (!parser_next_is(parser, ITEM_RIGHT_META))
4
Taking false branch
646 return error_item(item, "expecting `}}'");
647
648 int r = symtab_push(parser, symbol->list_desc);
5
Calling 'symtab_push'
649 if (r < 0) {
650 if (r == -ENODEV19)
651 return error_item(item, "Couldn't find descriptor for variable `%s'", symname);
652 return error_item(item, "Could not push symbol table (out of memory)");
653 }
654
655 emit_chunk(parser, TPL_ACTION_START_ITER, negate, symbol);
656
657 parser_push_item(parser, item);
658 parser->flags &= ~FLAGS_NEGATE;
659 return parser_text;
660 }
661
662 return unexpected_lexeme(item);
663}
664
665static void *parser_negate_iter(struct parser *parser, struct item *item)
666{
667 if (item->type != ITEM_HASH)
668 return unexpected_lexeme(item);
669
670 parser->flags ^= FLAGS_NEGATE;
671 return parser_iter;
672}
673
674static void *parse_identifier(struct parser *parser, struct item *item)
675{
676 struct item *next = NULL((void*)0);
677
678 if (!lex_next(&parser->lexer, &next)) {
679 if (next)
680 *item = *next;
681 return NULL((void*)0);
682 }
683
684 if (parser->flags & FLAGS_QUOTE) {
685 if (next->type != ITEM_CLOSE_CURLY_BRACE)
686 return error_item(item, "Expecting closing brace");
687 if (!lex_next(&parser->lexer, &next))
688 return unexpected_lexeme_or_lex_error(item, next);
689 }
690
691 if (next->type == ITEM_RIGHT_META) {
692 lwan_var_descriptor_t *symbol = symtab_lookup(parser, strndupa(item->value.value, item->value.len)(__extension__ ({ const char *__old = (item->value.value);
size_t __len = strnlen (__old, (item->value.len)); char *
__new = (char *) __builtin_alloca (__len + 1); __new[__len] =
'\0'; (char *) memcpy (__new, __old, __len); }))
);
693 if (!symbol) {
694 return error_item(item, "Unknown variable: %.*s", (int)item->value.len,
695 item->value.value);
696 }
697
698 emit_chunk(parser, TPL_ACTION_VARIABLE, parser->flags, symbol);
699
700 parser->flags &= ~FLAGS_QUOTE;
701 parser->tpl->minimum_size += item->value.len + 1;
702 return parser_text;
703 }
704
705 if (next->type == ITEM_QUESTION_MARK) {
706 lwan_var_descriptor_t *symbol = symtab_lookup(parser, strndupa(item->value.value, item->value.len)(__extension__ ({ const char *__old = (item->value.value);
size_t __len = strnlen (__old, (item->value.len)); char *
__new = (char *) __builtin_alloca (__len + 1); __new[__len] =
'\0'; (char *) memcpy (__new, __old, __len); }))
);
707 if (!symbol) {
708 return error_item(item, "Unknown variable: %.*s", (int)item->value.len,
709 item->value.value);
710 }
711
712 if (!parser_next_is(parser, ITEM_RIGHT_META))
713 return unexpected_lexeme_or_lex_error(item, next);
714
715 emit_chunk(parser, TPL_ACTION_IF_VARIABLE_NOT_EMPTY, 0, symbol);
716 parser_push_item(parser, item);
717
718 return parser_text;
719 }
720
721 return unexpected_lexeme_or_lex_error(item, next);
722}
723
724static void *parser_meta(struct parser *parser, struct item *item)
725{
726 if (item->type == ITEM_OPEN_CURLY_BRACE) {
727 parser->flags |= FLAGS_QUOTE;
728 return parser_meta;
729 }
730
731 if (item->type == ITEM_IDENTIFIER)
732 return parse_identifier(parser, item);
733
734 if (item->type == ITEM_GREATER_THAN)
735 return error_item(item, "Template inclusion not supported yet");
736
737 if (item->type == ITEM_HASH)
738 return parser_iter;
739
740 if (item->type == ITEM_HAT)
741 return parser_negate_iter;
742
743 if (item->type == ITEM_SLASH)
744 return parser_slash;
745
746 return unexpected_lexeme(item);
747}
748
749static void *parser_text(struct parser *parser, struct item *item)
750{
751 if (item->type == ITEM_LEFT_META)
752 return parser_meta;
753 if (item->type == ITEM_TEXT) {
754 if (item->value.len == 1) {
755 emit_chunk(parser, TPL_ACTION_APPEND_CHAR, 0, (void *)(uintptr_t)*item->value.value);
756 } else {
757 strbuf_t *buf = strbuf_new_with_size(item->value.len);
758 if (!buf)
759 return error_item(item, "Out of memory");
760 strbuf_set(buf, item->value.value, item->value.len);
761 emit_chunk(parser, TPL_ACTION_APPEND, 0, buf);
762 }
763 parser->tpl->minimum_size += item->value.len;
764 return parser_text;
765 }
766 if (item->type == ITEM_EOF) {
767 emit_chunk(parser, TPL_ACTION_LAST, 0, NULL((void*)0));
768 return NULL((void*)0);
769 }
770
771 return unexpected_lexeme(item);
772}
773
774void
775lwan_append_int_to_strbuf(strbuf_t *buf, void *ptr)
776{
777 char convertbuf[INT_TO_STR_BUFFER_SIZE(3 * sizeof(size_t) + 1)];
778 size_t len;
779 char *converted;
780
781 converted = int_to_string(*(int *)ptr, convertbuf, &len);
782 strbuf_append_str(buf, converted, len);
783}
784
785bool_Bool
786lwan_tpl_int_is_empty(void *ptr)
787{
788 return (*(int *)ptr) == 0;
789}
790
791void
792lwan_append_double_to_strbuf(strbuf_t *buf, void *ptr)
793{
794 strbuf_append_printf(buf, "%f", *(double *)ptr);
795}
796
797bool_Bool
798lwan_tpl_double_is_empty(void *ptr)
799{
800 return (*(double *)ptr) == 0.0f;
801}
802
803void
804lwan_append_str_to_strbuf(strbuf_t *buf, void *ptr)
805{
806 const char *str = *(char **)ptr;
807
808 if (LIKELY(str)__builtin_expect((!!(str)), (1)))
809 strbuf_append_str(buf, str, 0);
810}
811
812void
813lwan_append_str_escaped_to_strbuf(strbuf_t *buf, void *ptr)
814{
815 if (UNLIKELY(!ptr)__builtin_expect(((!ptr)), (0)))
816 return;
817
818 const char *str = *(char **)ptr;
819 if (UNLIKELY(!str)__builtin_expect(((!str)), (0)))
820 return;
821
822 for (const char *p = str; *p; p++) {
823 if (*p == '<')
824 strbuf_append_str(buf, "&lt;", 4);
825 else if (*p == '>')
826 strbuf_append_str(buf, "&gt;", 4);
827 else if (*p == '&')
828 strbuf_append_str(buf, "&amp;", 5);
829 else if (*p == '"')
830 strbuf_append_str(buf, "&quot;", 6);
831 else if (*p == '\'')
832 strbuf_append_str(buf, "&#x27;", 6);
833 else if (*p == '/')
834 strbuf_append_str(buf, "&#x2f;", 6);
835 else
836 strbuf_append_char(buf, *p);
837 }
838}
839
840bool_Bool
841lwan_tpl_str_is_empty(void *ptr)
842{
843 if (UNLIKELY(!ptr)__builtin_expect(((!ptr)), (0)))
844 return true1;
845
846 const char *str = *(const char **)ptr;
847 return LIKELY(str)__builtin_expect((!!(str)), (1)) && *str;
848}
849
850static void
851free_chunk(struct chunk *chunk)
852{
853 if (!chunk)
854 return;
855
856 switch (chunk->action) {
857 case TPL_ACTION_LAST:
858 case TPL_ACTION_APPEND_CHAR:
859 case TPL_ACTION_VARIABLE:
860 case TPL_ACTION_VARIABLE_STR:
861 case TPL_ACTION_VARIABLE_STR_ESCAPE:
862 case TPL_ACTION_END_IF_VARIABLE_NOT_EMPTY:
863 case TPL_ACTION_END_ITER:
864 /* do nothing */
865 break;
866 case TPL_ACTION_IF_VARIABLE_NOT_EMPTY:
867 case TPL_ACTION_START_ITER:
868 free(chunk->data);
869 break;
870 case TPL_ACTION_APPEND:
871 strbuf_free(chunk->data);
872 break;
873 case TPL_ACTION_APPLY_TPL:
874 lwan_tpl_free(chunk->data);
875 break;
876 }
877}
878
879void
880lwan_tpl_free(lwan_tpl_t *tpl)
881{
882 struct chunk *iter;
883
884 if (!tpl)
885 return;
886
887 for (iter = tpl->chunks; iter->action != TPL_ACTION_LAST; iter++)
888 free_chunk(iter);
889 free(tpl->chunks);
890 free(tpl);
891}
892
893static bool_Bool
894post_process_template(struct parser *parser)
895{
896 size_t idx;
897 struct chunk *prev_chunk, *resized;
898
899#define CHUNK_IDX(c) (size_t)(ptrdiff_t)((c) - parser->chunks.data)
900
901 for (idx = 0; idx < parser->chunks.used; idx++) {
902 struct chunk *chunk = &parser->chunks.data[idx];
903
904 if (chunk->action == TPL_ACTION_IF_VARIABLE_NOT_EMPTY) {
905 for (prev_chunk = chunk; ; chunk++) {
906 if (chunk->action == TPL_ACTION_LAST)
907 break;
908 if (chunk->action == TPL_ACTION_END_IF_VARIABLE_NOT_EMPTY
909 && chunk->data == prev_chunk->data)
910 break;
911 }
912
913 struct chunk_descriptor *cd = malloc(sizeof(*cd));
914 if (!cd)
915 lwan_status_critical_perror("malloc")lwan_status_critical_perror_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 915, __FUNCTION__, "malloc")
;
916
917 cd->descriptor = prev_chunk->data;
918 cd->chunk = chunk;
919 prev_chunk->data = cd;
920
921 idx = CHUNK_IDX(prev_chunk) + 1;
922 } else if (chunk->action == TPL_ACTION_START_ITER) {
923 enum flags flags = chunk->flags;
924
925 for (prev_chunk = chunk; ; chunk++) {
926 if (chunk->action == TPL_ACTION_LAST)
927 break;
928 if (chunk->action == TPL_ACTION_END_ITER && chunk->data == prev_chunk) {
929 chunk->flags |= flags;
930 break;
931 }
932 }
933
934 struct chunk_descriptor *cd = malloc(sizeof(*cd));
935 if (!cd)
936 lwan_status_critical_perror("malloc")lwan_status_critical_perror_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 936, __FUNCTION__, "malloc")
;
937
938 cd->descriptor = prev_chunk->data;
939 prev_chunk->data = cd;
940
941 if (!chunk || chunk->action == TPL_ACTION_LAST)
942 cd->chunk = chunk;
943 else
944 cd->chunk = chunk + 1;
945
946 idx = CHUNK_IDX(prev_chunk) + 1;
947 } else if (chunk->action == TPL_ACTION_VARIABLE) {
948 lwan_var_descriptor_t *descriptor = chunk->data;
949 bool_Bool escape = chunk->flags & FLAGS_QUOTE;
950
951 if (descriptor->append_to_strbuf == lwan_append_str_to_strbuf) {
952 if (escape)
953 chunk->action = TPL_ACTION_VARIABLE_STR_ESCAPE;
954 else
955 chunk->action = TPL_ACTION_VARIABLE_STR;
956 chunk->data = (void *)descriptor->offset;
957 } else if (escape) {
958 lwan_status_error("Variable must be string to be escaped")lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 958, __FUNCTION__, "Variable must be string to be escaped")
;
959 return false0;
960 } else if (!descriptor->append_to_strbuf) {
961 lwan_status_error("Invalid variable descriptor")lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 961, __FUNCTION__, "Invalid variable descriptor")
;
962 return false0;
963 }
964 } else if (chunk->action == TPL_ACTION_LAST) {
965 break;
966 }
967 }
968
969 if (parser->chunks.reserved != parser->chunks.used) {
970 lwan_status_debug("Template parsing done, reallocating array from %zu to %zu elements",lwan_status_debug_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 971, __FUNCTION__, "Template parsing done, reallocating array from %zu to %zu elements"
, parser->chunks.reserved, parser->chunks.used)
971 parser->chunks.reserved, parser->chunks.used)lwan_status_debug_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 971, __FUNCTION__, "Template parsing done, reallocating array from %zu to %zu elements"
, parser->chunks.reserved, parser->chunks.used)
;
972 resized = reallocarray(parser->chunks.data, parser->chunks.used, sizeof(struct chunk));
973 if (resized)
974 parser->chunks.data = resized;
975 }
976
977 return true1;
978
979#undef CHUNK_IDX
980}
981
982static bool_Bool parse_string(lwan_tpl_t *tpl, const char *string, const lwan_var_descriptor_t *descriptor)
983{
984 struct parser parser = {
985 .tpl = tpl,
986 .symtab = NULL((void*)0),
987 .chunks = { .used = 0, .reserved = array_increment_step }
988 };
989 void *(*state)(struct parser *parser, struct item *item) = parser_text;
990 struct item *item = NULL((void*)0);
991 bool_Bool success = true1;
992
993 if (symtab_push(&parser, descriptor) < 0)
994 return false0;
995
996 parser.chunks.data = reallocarray(NULL((void*)0), parser.chunks.reserved, sizeof(struct chunk));
997 if (!parser.chunks.data) {
998 symtab_pop(&parser);
999 return false0;
1000 }
1001
1002 lex_init(&parser.lexer, string);
1003 list_head_init(&parser.stack);
1004
1005 while (state && lex_next(&parser.lexer, &item))
1006 state = state(&parser, item);
1007
1008 if (!state && item->type == ITEM_ERROR && item->value.value) {
1009 lwan_status_error("Parser error: %.*s", (int)item->value.len, item->value.value)lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 1009, __FUNCTION__, "Parser error: %.*s", (int)item->value
.len, item->value.value)
;
1010 free((char *)item->value.value);
1011
1012 success = false0;
1013 }
1014
1015 if (!list_empty(&parser.stack)) {
1016 struct stacked_item *stacked, *stacked_next;
1017
1018 list_for_each_safe(&parser.stack, stacked, stacked_next, stack)for (stacked = list_node_to_off_((&parser.stack)->n.next
, ((__builtin_offsetof(typeof(*stacked), stack) + ((typeof(stacked
->stack) *)0 != (struct list_node *)0)))), stacked_next = list_node_to_off_
(list_node_from_off_(stacked, ((__builtin_offsetof(typeof(*stacked
), stack) + ((typeof(stacked->stack) *)0 != (struct list_node
*)0))))->next, ((__builtin_offsetof(typeof(*stacked), stack
) + ((typeof(stacked->stack) *)0 != (struct list_node *)0)
))); list_node_from_off_(stacked, ((__builtin_offsetof(typeof
(*stacked), stack) + ((typeof(stacked->stack) *)0 != (struct
list_node *)0)))) != &(&parser.stack)->n; stacked
= stacked_next, stacked_next = list_node_to_off_(list_node_from_off_
(stacked, ((__builtin_offsetof(typeof(*stacked), stack) + ((typeof
(stacked->stack) *)0 != (struct list_node *)0))))->next
, ((__builtin_offsetof(typeof(*stacked), stack) + ((typeof(stacked
->stack) *)0 != (struct list_node *)0)))))
{
1019 lwan_status_error("Parser error: EOF while looking for matching {{/%.*s}}",lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 1020, __FUNCTION__, "Parser error: EOF while looking for matching {{/%.*s}}"
, (int)stacked->item.value.len, stacked->item.value.value
)
1020 (int)stacked->item.value.len, stacked->item.value.value)lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 1020, __FUNCTION__, "Parser error: EOF while looking for matching {{/%.*s}}"
, (int)stacked->item.value.len, stacked->item.value.value
)
;
1021 list_del(&stacked->stack);
1022 free(stacked);
1023 }
1024
1025 success = false0;
1026 }
1027
1028 symtab_pop(&parser);
1029 if (parser.symtab) {
1030 lwan_status_error("Parser error: Symbol table not empty when finishing parser")lwan_status_error_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 1030, __FUNCTION__, "Parser error: Symbol table not empty when finishing parser"
)
;
1031
1032 while (parser.symtab)
1033 symtab_pop(&parser);
1034
1035 success = false0;
1036 }
1037
1038 if (success)
1039 success = post_process_template(&parser);
1040
1041 tpl->chunks = parser.chunks.data;
1042
1043 if (!success) {
1044 /* Emit a TPL_ACTION_LAST chunk so that lwan_tpl_free() knows when to stop */
1045 emit_chunk(&parser, TPL_ACTION_LAST, 0, NULL((void*)0));
1046 }
1047
1048 return success;
1049}
1050
1051lwan_tpl_t *
1052lwan_tpl_compile_string(const char *string, const lwan_var_descriptor_t *descriptor)
1053{
1054 lwan_tpl_t *tpl;
1055
1056 tpl = malloc(sizeof(*tpl));
1057 if (tpl) {
1058 if (parse_string(tpl, string, descriptor))
1059 return tpl;
1060 }
1061
1062 lwan_tpl_free(tpl);
1063 return NULL((void*)0);
1064}
1065
1066lwan_tpl_t *
1067lwan_tpl_compile_file(const char *filename, const lwan_var_descriptor_t *descriptor)
1068{
1069 int fd;
1070 struct stat st;
1071 char *mapped;
1072 lwan_tpl_t *tpl = NULL((void*)0);
1073
1074 fd = open(filename, O_RDONLY00 | O_CLOEXEC02000000);
1075 if (fd < 0)
1076 goto end;
1077
1078 if (fstat(fd, &st) < 0)
1079 goto close_file;
1080
1081 mapped = mmap(NULL((void*)0), (size_t)st.st_size, PROT_READ0x1, MAP_SHARED0x01, fd, 0);
1082 if (mapped == MAP_FAILED((void *) -1))
1083 goto close_file;
1084
1085 tpl = lwan_tpl_compile_string(mapped, descriptor);
1086
1087 if (munmap(mapped, (size_t)st.st_size) < 0)
1088 lwan_status_perror("munmap")lwan_status_perror_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 1088, __FUNCTION__, "munmap")
;
1089
1090close_file:
1091 close(fd);
1092end:
1093 return tpl;
1094}
1095
1096static struct chunk *
1097apply_until(lwan_tpl_t *tpl, struct chunk *chunks, strbuf_t *buf, void *variables,
1098 void *until_data)
1099{
1100 static const void *const dispatch_table[] = {
1101 [TPL_ACTION_APPEND] = &&action_append,
1102 [TPL_ACTION_APPEND_CHAR] = &&action_append_char,
1103 [TPL_ACTION_VARIABLE] = &&action_variable,
1104 [TPL_ACTION_VARIABLE_STR] = &&action_variable_str,
1105 [TPL_ACTION_VARIABLE_STR_ESCAPE] = &&action_variable_str_escape,
1106 [TPL_ACTION_IF_VARIABLE_NOT_EMPTY] = &&action_if_variable_not_empty,
1107 [TPL_ACTION_END_IF_VARIABLE_NOT_EMPTY] = &&action_end_if_variable_not_empty,
1108 [TPL_ACTION_APPLY_TPL] = &&action_apply_tpl,
1109 [TPL_ACTION_START_ITER] = &&action_start_iter,
1110 [TPL_ACTION_END_ITER] = &&action_end_iter,
1111 [TPL_ACTION_LAST] = &&finalize
1112 };
1113 coro_switcher_t switcher;
1114 coro_t *coro = NULL((void*)0);
1115 struct chunk *chunk = chunks;
1116
1117 if (UNLIKELY(!chunk)__builtin_expect(((!chunk)), (0)))
1118 return NULL((void*)0);
1119
1120#define DISPATCH() do { goto *dispatch_table[chunk->action]; } while(false0)
1121#define NEXT_ACTION() do { chunk++; DISPATCH(); } while(false0)
1122
1123 DISPATCH();
1124
1125action_append:
1126 strbuf_append_str(buf, strbuf_get_buffer(chunk->data)(((strbuf_t *)(chunk->data))->value.buffer),
1127 strbuf_get_length(chunk->data)(((strbuf_t *)(chunk->data))->len.buffer));
1128 NEXT_ACTION();
1129
1130action_append_char:
1131 strbuf_append_char(buf, (char)(uintptr_t)chunk->data);
1132 NEXT_ACTION();
1133
1134action_variable: {
1135 lwan_var_descriptor_t *descriptor = chunk->data;
1136 descriptor->append_to_strbuf(buf, (char *)variables + descriptor->offset);
1137 NEXT_ACTION();
1138 }
1139
1140action_variable_str:
1141 lwan_append_str_to_strbuf(buf, (char *)variables + (uintptr_t)chunk->data);
1142 NEXT_ACTION();
1143
1144action_variable_str_escape:
1145 lwan_append_str_escaped_to_strbuf(buf, (char *)variables + (uintptr_t)chunk->data);
1146 NEXT_ACTION();
1147
1148action_if_variable_not_empty: {
1149 struct chunk_descriptor *cd = chunk->data;
1150 bool_Bool empty = cd->descriptor->get_is_empty((char *)variables + cd->descriptor->offset);
1151 if (chunk->flags & FLAGS_NEGATE)
1152 empty = !empty;
1153 if (empty) {
1154 chunk = cd->chunk;
1155 } else {
1156 chunk = apply_until(tpl, chunk + 1, buf, variables, cd->chunk);
1157 }
1158 NEXT_ACTION();
1159 }
1160
1161action_end_if_variable_not_empty:
1162 if (LIKELY(until_data == chunk)__builtin_expect((!!(until_data == chunk)), (1)))
1163 goto finalize;
1164 NEXT_ACTION();
1165
1166action_apply_tpl: {
1167 strbuf_t *tmp = lwan_tpl_apply(chunk->data, variables);
1168 strbuf_append_str(buf, strbuf_get_buffer(tmp)(((strbuf_t *)(tmp))->value.buffer), strbuf_get_length(tmp)(((strbuf_t *)(tmp))->len.buffer));
1169 strbuf_free(tmp);
1170 NEXT_ACTION();
1171 }
1172
1173action_start_iter:
1174 if (UNLIKELY(coro != NULL)__builtin_expect(((coro != ((void*)0))), (0))) {
1175 lwan_status_warning("Coroutine is not NULL when starting iteration")lwan_status_warning_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 1175, __FUNCTION__, "Coroutine is not NULL when starting iteration"
)
;
1176 NEXT_ACTION();
1177 }
1178
1179 struct chunk_descriptor *cd = chunk->data;
1180 coro = coro_new(&switcher, cd->descriptor->generator, variables);
1181
1182 bool_Bool resumed = coro_resume_value(coro, 0);
1183 enum flags negate = chunk->flags & FLAGS_NEGATE;
1184 if (negate)
1185 resumed = !resumed;
1186 if (!resumed) {
1187 chunk = cd->chunk;
1188
1189 if (negate)
1190 coro_resume_value(coro, 1);
1191
1192 coro_free(coro);
1193 coro = NULL((void*)0);
1194
1195 if (negate)
1196 DISPATCH();
1197 NEXT_ACTION();
1198 }
1199
1200 chunk = apply_until(tpl, chunk + 1, buf, variables, chunk);
1201 DISPATCH();
1202
1203action_end_iter:
1204 if (until_data == chunk->data)
1205 goto finalize;
1206
1207 if (UNLIKELY(!coro)__builtin_expect(((!coro)), (0))) {
1208 if (!chunk->flags)
1209 lwan_status_warning("Coroutine is NULL when finishing iteration")lwan_status_warning_debug("/home/buildbot/lwan-slave/clang-analyze/build/common/lwan-template.c"
, 1209, __FUNCTION__, "Coroutine is NULL when finishing iteration"
)
;
1210 NEXT_ACTION();
1211 }
1212
1213 if (!coro_resume_value(coro, 0)) {
1214 coro_free(coro);
1215 coro = NULL((void*)0);
1216 NEXT_ACTION();
1217 }
1218
1219 chunk = apply_until(tpl, ((struct chunk *)chunk->data) + 1, buf, variables, chunk->data);
1220 DISPATCH();
1221
1222finalize:
1223 return chunk;
1224#undef DISPATCH
1225#undef NEXT_ACTION
1226}
1227
1228strbuf_t *
1229lwan_tpl_apply_with_buffer(lwan_tpl_t *tpl, strbuf_t *buf, void *variables)
1230{
1231 if (UNLIKELY(!strbuf_reset_length(buf))__builtin_expect(((!strbuf_reset_length(buf))), (0)))
1232 return NULL((void*)0);
1233
1234 if (UNLIKELY(!strbuf_grow_to(buf, tpl->minimum_size))__builtin_expect(((!strbuf_grow_to(buf, tpl->minimum_size)
)), (0))
)
1235 return NULL((void*)0);
1236
1237 apply_until(tpl, tpl->chunks, buf, variables, NULL((void*)0));
1238
1239 return buf;
1240}
1241
1242strbuf_t *
1243lwan_tpl_apply(lwan_tpl_t *tpl, void *variables)
1244{
1245 strbuf_t *buf = strbuf_new_with_size(tpl->minimum_size);
1246 return lwan_tpl_apply_with_buffer(tpl, buf, variables);
1247}
1248
1249#ifdef TEMPLATE_TEST
1250
1251struct test_struct {
1252 int some_int;
1253 char *a_string;
1254};
1255
1256int main(int argc, char *argv[])
1257{
1258 if (argc < 2) {
1259 printf("Usage: %s file.tpl\n", argv[0]);
1260 return 1;
1261 }
1262
1263 printf("*** Compiling template...\n");
1264 lwan_var_descriptor_t desc[] = {
1265 TPL_VAR_INT(struct test_struct, some_int){ .name = "some_int", .offset = __builtin_offsetof(struct test_struct
, some_int), .append_to_strbuf = lwan_append_int_to_strbuf, .
get_is_empty = lwan_tpl_int_is_empty }
,
1266 TPL_VAR_STR(struct test_struct, a_string){ .name = "a_string", .offset = __builtin_offsetof(struct test_struct
, a_string), .append_to_strbuf = lwan_append_str_to_strbuf, .
get_is_empty = lwan_tpl_str_is_empty }
,
1267 TPL_VAR_SENTINEL{ ((void*)0), 0, ((void*)0), ((void*)0), ((void*)0), ((void*)
0) }
1268 };
1269 lwan_tpl_t *tpl = lwan_tpl_compile_file(argv[1], desc);
1270 if (!tpl)
1271 return 1;
1272
1273 printf("*** Applying template 100000 times...\n");
1274 for (size_t i = 0; i < 100000; i++) {
1275 strbuf_t *applied = lwan_tpl_apply(tpl, &(struct test_struct) {
1276 .some_int = 42,
1277 .a_string = "some string"
1278 });
1279 strbuf_free(applied);
1280 }
1281
1282 lwan_tpl_free(tpl);
1283 return 0;
1284}
1285
1286#endif /* TEMPLATE_TEST */