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
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->tp_puts(tp, ");
146 fputs
(")) == -1) goto err;\n", fp
);
152 block
: define body end
{
154 fputs
("return tp_ret;\n", fp
);
158 | define body finally end
{
159 fputs
("return tp_ret;\n", fp
);
165 define
: '{' DEFINE
string '}' {
169 fprintf
(fp
, "int\n%s\n{\n", $3);
170 fputs
("int tp_ret = 0;\n", fp
);
182 special
: '{' RENDER
string '}' {
184 fprintf
(fp
, "if ((tp_ret = %s) == -1) goto err;\n",
189 | if body endif
{ fputs
("}\n", fp
); }
191 |
'{' string '|' UNSAFE
'}' {
194 "if ((tp_ret = tp->tp_puts(tp, %s)) == -1)\n",
196 fputs
("goto err;\n", fp
);
199 |
'{' string '|' URLESCAPE
'}' {
202 "if ((tp_ret = tp_urlescape(tp, %s)) == -1)\n",
204 fputs
("goto err;\n", fp
);
210 "if ((tp_ret = tp->tp_escape(tp, %s)) == -1)\n",
212 fputs
("goto err;\n", fp
);
217 printf
: '{' PRINTF
{
219 fprintf
(fp
, "if (asprintf(&tp->tp_tmp, ");
221 fputs
(") == -1)\n", fp
);
222 fputs
("goto err;\n", fp
);
223 fputs
("if ((tp_ret = tp->tp_escape(tp, tp->tp_tmp)) "
225 fputs
("goto err;\n", fp
);
226 fputs
("free(tp->tp_tmp);\n", fp
);
227 fputs
("tp->tp_tmp = NULL;\n", fp
);
231 printfargs
: /* empty */
232 | printfargs STRING
{
233 fprintf
(fp
, " %s", $2);
238 if
: '{' IF stringy
'}' {
240 fprintf
(fp
, "if (%s) {\n", $3);
250 elsif
: '{' ELSE IF stringy
'}' {
252 fprintf
(fp
, "} else if (%s) {\n", $4);
257 else
: '{' ELSE
'}' {
259 fputs
("} else {\n", fp
);
263 loop
: '{' FOR stringy
'}' {
264 fprintf
(fp
, "for (%s) {\n", $3);
269 |
'{' TQFOREACH STRING STRING STRING
'}' {
270 fprintf
(fp
, "TAILQ_FOREACH(%s, %s, %s) {\n",
278 |
'{' WHILE stringy
'}' {
279 fprintf
(fp
, "while (%s) {\n", $3);
289 finally
: '{' FINALLY
'}' {
295 string : STRING
string {
296 if
(asprintf
(&$$
, "%s %s", $1, $2) == -1)
306 if
(asprintf
(&$$
, "%s %s", $1, $2) == -1)
312 if
(asprintf
(&$$
, "|%s", $2) == -1)
326 yyerror(const char *fmt
, ...
)
333 if
(vasprintf
(&msg
, fmt
, ap
) == -1)
334 err
(1, "yyerror vasprintf");
336 fprintf
(stderr
, "%s:%d: %s\n", file
->name
, yylval.lineno
, msg
);
342 kw_cmp
(const void *k
, const void *e
)
344 return
(strcmp
(k
, ((const struct keywords
*)e
)->k_name
));
350 /* this has to be sorted always */
351 static const struct keywords keywords
[] = {
352 { "define", DEFINE
},
355 { "finally", FINALLY
},
358 { "include", INCLUDE
},
359 { "printf", PRINTF
},
360 { "render", RENDER
},
361 { "tailq-foreach", TQFOREACH
},
362 { "unsafe", UNSAFE
},
363 { "urlescape", URLESCAPE
},
366 const struct keywords
*p
;
368 p
= bsearch
(s
, keywords
, nitems
(keywords
), sizeof
(keywords
[0]),
377 #define START_EXPAND 1
378 #define DONE_EXPAND 2
380 static int expanding
;
388 if
(file
->ungetpos
> 0)
389 c
= file
->ungetbuf
[--file
->ungetpos
];
391 c
= getc
(file
->stream
);
393 if
(c
== START_EXPAND
)
395 else if
(c
== DONE_EXPAND
)
409 if
((c
= igetc
()) == EOF
) {
410 yyerror("reached end of filewhile parsing "
412 if
(file
== topfile || popfile
() == EOF
)
420 if
(c
== '\t' || c
== ' ') {
421 /* Compress blanks to a sigle space. */
423 c
= getc
(file
->stream
);
424 } while
(c
== '\t' || c
== ' ');
425 ungetc
(c
, file
->stream
);
431 * Fake EOL when hit EOF for the first time. This gets line
432 * count right if last line in included file is syntactically
433 * invalid and has no newline.
435 if
(file
->eof_reached
== 0) {
436 file
->eof_reached
= 1;
440 if
(file
== topfile || popfile
() == EOF
)
454 if
(file
->ungetpos
>= file
->ungetsize
) {
455 void *p
= reallocarray
(file
->ungetbuf
, file
->ungetsize
, 2);
457 err
(1, "reallocarray");
459 file
->ungetsize
*= 2;
461 file
->ungetbuf
[file
->ungetpos
++] = c
;
469 /* skip to either EOF or the first real EOL */
493 if
(!in_define
&& block
== 0) {
494 while
((c
= lgetc
(0)) != '{' && c
!= EOF
) {
504 if
(c
== '{' || c
== '!') {
515 while
((c
= lgetc
(0)) == ' ' || c
== '\t' || c
== '\n') {
521 yyerror("unterminated block");
525 yylval.lineno
= file
->lineno
;
527 if
(block
!= 0 && c
== block
) {
528 if
((c
= lgetc
(0)) == '}') {
540 if
(in_define
&& block
== 0) {
546 if
(c
== '!' || c
== '{') {
554 } else if
(c
== '{') {
557 } else if
(c
== '\n')
561 if
((size_t)(p
- buf
) >= sizeof
(buf
)) {
562 yyerror("string too long");
565 } while
((c
= lgetc
(0)) != EOF
);
568 yyerror("unterminated block");
573 if
((yylval.v.
string = strdup
(buf
)) == NULL
)
589 } else if
(c
== '!') {
592 } else if
(c
== '\n')
596 if
((size_t)(p
- buf
) >= sizeof
(buf
)) {
597 yyerror("line too long");
600 } while
((c
= lgetc
(0)) != EOF
);
604 yyerror("unterminated block");
610 if
((yylval.v.
string = strdup
(buf
)) == NULL
)
619 if
(!quote
&& isspace
((unsigned char)c
))
625 if
(!quote
&& c
== '|') {
639 } else if
(!quote
&& c
== '}') {
645 if
((size_t)(p
- buf
) >= sizeof
(buf
)) {
646 yyerror("string too long");
649 } while
((c
= lgetc
(0)) != EOF
);
653 yyerror(quote ?
"unterminated quote" : "unterminated block");
658 if
((token
= lookup
(buf
)) == STRING
)
659 if
((yylval.v.
string = strdup
(buf
)) == NULL
)
665 pushfile
(const char *name
, int secret
)
669 if
((nfile
= calloc
(1, sizeof
(*nfile
))) == NULL
)
671 if
((nfile
->name
= strdup
(name
)) == NULL
)
673 if
((nfile
->stream
= fopen
(nfile
->name
, "r")) == NULL
) {
674 warn
("can't open %s", nfile
->name
);
679 nfile
->lineno
= TAILQ_EMPTY
(&files
) ?
1 : 0;
680 nfile
->ungetsize
= 16;
681 nfile
->ungetbuf
= malloc
(nfile
->ungetsize
);
682 if
(nfile
->ungetbuf
== NULL
)
684 TAILQ_INSERT_TAIL
(&files
, nfile
, entry
);
693 if
((prev
= TAILQ_PREV
(file
, files
, entry
)) != NULL
)
694 prev
->errors
+= file
->errors
;
696 TAILQ_REMOVE
(&files
, file
, entry
);
697 fclose
(file
->stream
);
699 free
(file
->ungetbuf
);
702 return
(file ?
0 : EOF
);
706 parse
(FILE *outfile
, const char *filename
)
710 if
((file
= pushfile
(filename
, 0)) == 0)
715 errors
= file
->errors
;
718 return
(errors ?
-1 : 0);
727 if
(yylval.lineno
== lastline
+ 1) {
728 lastline
= yylval.lineno
;
731 lastline
= yylval.lineno
;
733 fprintf
(fp
, "#line %d ", yylval.lineno
);
739 printq
(const char *str
)
742 for
(; *str
; ++str
) {