2 * Copyright (c) 2022 Omar Polo <op@openbsd.org>
3 * Copyright (c) 2007-2016 Reyk Floeter <reyk@openbsd.org>
4 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
6 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
7 * Copyright (c) 2001 Markus Friedl. All rights reserved.
8 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
9 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 #include <sys/queue.h>
37 #include "got_compat.h"
40 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
43 TAILQ_HEAD
(files
, file
) files
= TAILQ_HEAD_INITIALIZER
(files
);
45 TAILQ_ENTRY
(file
) entry
;
50 unsigned char *ungetbuf
;
55 int parse
(FILE *, const char *);
56 struct file
*pushfile
(const char *, int);
60 int yyerror(const char *, ...
)
61 __attribute__
((__format__
(printf
, 1, 2)))
62 __attribute__
((__nonnull__
(1)));
63 int kw_cmp
(const void *, const void *);
71 void printq
(const char *);
80 static int lastline
= -1;
91 %token DEFINE ELSE END ERROR FINALLY FOR IF INCLUDE PRINTF
92 %token RENDER TQFOREACH UNSAFE URLESCAPE WHILE
93 %token
<v.
string> STRING
94 %type
<v.
string> string nstring
95 %type
<v.
string> stringy
103 | grammar
error { file
->errors
++; }
106 include
: INCLUDE STRING
{
109 if
((nfile
= pushfile
($2, 0)) == NULL
) {
110 yyerror("failed to include file %s", $2);
121 verbatim
: '!' verbatim1
'!' {
123 /* TODO: check template status and exit in case */
128 verbatim1
: /* empty */
132 fprintf
(fp
, "%s\n", $2);
138 verbatims
: /* empty */
144 fprintf
(fp
, "if ((tp_ret = tp_write(tp, ");
146 fprintf
(fp
, ", %zu)) == -1) goto err;\n",
153 block
: define body end
{
155 fputs
("return tp_ret;\n", fp
);
159 | define body finally end
{
160 fputs
("return tp_ret;\n", fp
);
166 define
: '{' DEFINE
string '}' {
170 fprintf
(fp
, "int\n%s\n{\n", $3);
171 fputs
("int tp_ret = 0;\n", fp
);
183 special
: '{' RENDER
string '}' {
185 fprintf
(fp
, "if ((tp_ret = %s) == -1) goto err;\n",
190 | if body endif
{ fputs
("}\n", fp
); }
192 |
'{' string '|' UNSAFE
'}' {
195 "if ((tp_ret = tp_writes(tp, %s)) == -1)\n",
197 fputs
("goto err;\n", fp
);
200 |
'{' string '|' URLESCAPE
'}' {
203 "if ((tp_ret = tp_urlescape(tp, %s)) == -1)\n",
205 fputs
("goto err;\n", fp
);
211 "if ((tp_ret = tp_htmlescape(tp, %s)) == -1)\n",
213 fputs
("goto err;\n", fp
);
218 printf
: '{' PRINTF
{
220 fprintf
(fp
, "if (asprintf(&tp->tp_tmp, ");
222 fputs
(") == -1)\n", fp
);
223 fputs
("goto err;\n", fp
);
224 fputs
("if ((tp_ret = tp_htmlescape(tp, tp->tp_tmp)) "
226 fputs
("goto err;\n", fp
);
227 fputs
("free(tp->tp_tmp);\n", fp
);
228 fputs
("tp->tp_tmp = NULL;\n", fp
);
232 printfargs
: /* empty */
233 | printfargs STRING
{
234 fprintf
(fp
, " %s", $2);
239 if
: '{' IF stringy
'}' {
241 fprintf
(fp
, "if (%s) {\n", $3);
251 elsif
: '{' ELSE IF stringy
'}' {
253 fprintf
(fp
, "} else if (%s) {\n", $4);
258 else
: '{' ELSE
'}' {
260 fputs
("} else {\n", fp
);
264 loop
: '{' FOR stringy
'}' {
265 fprintf
(fp
, "for (%s) {\n", $3);
270 |
'{' TQFOREACH STRING STRING STRING
'}' {
271 fprintf
(fp
, "TAILQ_FOREACH(%s, %s, %s) {\n",
279 |
'{' WHILE stringy
'}' {
280 fprintf
(fp
, "while (%s) {\n", $3);
290 finally
: '{' FINALLY
'}' {
296 nstring
: STRING nstring
{
297 if
(asprintf
(&$$
, "%s%s", $1, $2) == -1)
305 string : STRING
string {
306 if
(asprintf
(&$$
, "%s %s", $1, $2) == -1)
316 if
(asprintf
(&$$
, "%s %s", $1, $2) == -1)
322 if
(asprintf
(&$$
, "|%s", $2) == -1)
336 yyerror(const char *fmt
, ...
)
343 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
344 err
(1, "yyerror vasprintf");
346 fprintf
(stderr
, "%s:%d: %s\n", file
->name
, yylval.lineno
, msg
);
352 kw_cmp
(const void *k
, const void *e
)
354 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
360 /* this has to be sorted always */
361 static const struct keywords keywords
[] = {
362 { "define", DEFINE
},
365 { "finally", FINALLY
},
368 { "include", INCLUDE
},
369 { "printf", PRINTF
},
370 { "render", RENDER
},
371 { "tailq-foreach", TQFOREACH
},
372 { "unsafe", UNSAFE
},
373 { "urlescape", URLESCAPE
},
376 const struct keywords
*p
;
378 p
= bsearch
(s
, keywords
, nitems
(keywords
), sizeof
(keywords
[0]),
387 #define START_EXPAND 1
388 #define DONE_EXPAND 2
390 static int expanding
;
398 if
(file
->ungetpos
> 0)
399 c
= file
->ungetbuf
[--file
->ungetpos
];
401 c
= getc
(file
->stream
);
403 if
(c
== START_EXPAND
)
405 else if
(c
== DONE_EXPAND
)
419 if
((c
= igetc
()) == EOF
) {
420 yyerror("reached end of filewhile parsing "
422 if
(file
== topfile || popfile
() == EOF
)
430 if
(c
== '\t' || c
== ' ') {
431 /* Compress blanks to a sigle space. */
433 c
= getc
(file
->stream
);
434 } while
(c
== '\t' || c
== ' ');
435 ungetc
(c
, file
->stream
);
441 * Fake EOL when hit EOF for the first time. This gets line
442 * count right if last line in included file is syntactically
443 * invalid and has no newline.
445 if
(file
->eof_reached
== 0) {
446 file
->eof_reached
= 1;
450 if
(file
== topfile || popfile
() == EOF
)
464 if
(file
->ungetpos
>= file
->ungetsize
) {
465 void *p
= reallocarray
(file
->ungetbuf
, file
->ungetsize
, 2);
467 err
(1, "reallocarray");
469 file
->ungetsize
*= 2;
471 file
->ungetbuf
[file
->ungetpos
++] = c
;
479 /* skip to either EOF or the first real EOL */
503 if
(!in_define
&& block
== 0) {
504 while
((c
= lgetc
(0)) != '{' && c
!= EOF
) {
514 if
(c
== '{' || c
== '!') {
525 while
((c
= lgetc
(0)) == ' ' || c
== '\t' || c
== '\n') {
531 yyerror("unterminated block");
535 yylval.lineno
= file
->lineno
;
537 if
(block
!= 0 && c
== block
) {
538 if
((c
= lgetc
(0)) == '}') {
550 if
(in_define
&& block
== 0) {
556 if
(c
== '!' || c
== '{') {
564 } else if
(c
== '{') {
567 } else if
(c
== '\n')
571 if
((size_t)(p
- buf
) >= sizeof
(buf
)) {
572 yyerror("string too long");
575 } while
((c
= lgetc
(0)) != EOF
);
578 yyerror("unterminated block");
583 if
((yylval.v.
string = strdup
(buf
)) == NULL
)
599 } else if
(c
== '!') {
602 } else if
(c
== '\n')
606 if
((size_t)(p
- buf
) >= sizeof
(buf
)) {
607 yyerror("line too long");
610 } while
((c
= lgetc
(0)) != EOF
);
614 yyerror("unterminated block");
620 if
((yylval.v.
string = strdup
(buf
)) == NULL
)
629 if
(!quote
&& isspace
((unsigned char)c
))
635 if
(!quote
&& c
== '|') {
649 } else if
(!quote
&& c
== '}') {
655 if
((size_t)(p
- buf
) >= sizeof
(buf
)) {
656 yyerror("string too long");
659 } while
((c
= lgetc
(0)) != EOF
);
663 yyerror(quote ?
"unterminated quote" : "unterminated block");
668 if
((token
= lookup
(buf
)) == STRING
)
669 if
((yylval.v.
string = strdup
(buf
)) == NULL
)
675 pushfile
(const char *name
, int secret
)
679 if
((nfile
= calloc
(1, sizeof
(*nfile
))) == NULL
)
681 if
((nfile
->name
= strdup
(name
)) == NULL
)
683 if
((nfile
->stream
= fopen
(nfile
->name
, "r")) == NULL
) {
684 warn
("can't open %s", nfile
->name
);
689 nfile
->lineno
= TAILQ_EMPTY
(&files
) ?
1 : 0;
690 nfile
->ungetsize
= 16;
691 nfile
->ungetbuf
= malloc
(nfile
->ungetsize
);
692 if
(nfile
->ungetbuf
== NULL
)
694 TAILQ_INSERT_TAIL
(&files
, nfile
, entry
);
703 if
((prev
= TAILQ_PREV
(file
, files
, entry
)) != NULL
)
704 prev
->errors
+= file
->errors
;
706 TAILQ_REMOVE
(&files
, file
, entry
);
707 fclose
(file
->stream
);
709 free
(file
->ungetbuf
);
712 return
(file ?
0 : EOF
);
716 parse
(FILE *outfile
, const char *filename
)
720 if
((file
= pushfile
(filename
, 0)) == 0)
725 errors
= file
->errors
;
728 return
(errors ?
-1 : 0);
737 if
(yylval.lineno
== lastline
+ 1) {
738 lastline
= yylval.lineno
;
741 lastline
= yylval.lineno
;
743 fprintf
(fp
, "#line %d ", yylval.lineno
);
749 printq
(const char *str
)
752 for
(; *str
; ++str
) {