1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
29 # include "php_config.h"
37 #include <Zend/zend_smart_str.h>
41 struct psi_parser
*psi_parser_init(struct psi_parser
*P
, psi_error_cb error
,
45 P
= pemalloc(sizeof(*P
), 1);
47 memset(P
, 0, sizeof(*P
));
49 psi_data_ctor_with_dtors(PSI_DATA(P
), error
, flags
);
51 P
->preproc
= psi_cpp_init(P
);
56 struct psi_parser_input
*psi_parser_open_file(struct psi_parser
*P
,
57 const char *filename
, bool report_errors
)
61 struct psi_parser_input
*fb
;
63 if (stat(filename
, &sb
)) {
65 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
66 "Could not stat '%s': %s",
67 filename
, strerror(errno
));
72 if (!(fb
= pemalloc(sizeof(*fb
) + sb
.st_size
+ psi_parser_maxfill(), 1))) {
74 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
75 "Could not allocate %zu bytes for reading '%s': %s",
76 sb
.st_size
+ psi_parser_maxfill(), filename
, strerror(errno
));
81 if (!(fp
= fopen(filename
, "r"))) {
84 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
85 "Could not open '%s' for reading: %s",
86 filename
, strerror(errno
));
91 if (sb
.st_size
!= fread(fb
->buffer
, 1, sb
.st_size
, fp
)) {
95 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
96 "Could not read %zu bytes from '%s': %s",
97 sb
.st_size
+ psi_parser_maxfill(), filename
, strerror(errno
));
103 memset(fb
->buffer
+ sb
.st_size
, 0, psi_parser_maxfill());
105 fb
->lmod
= sb
.st_mtime
;
106 fb
->length
= sb
.st_size
;
107 fb
->file
= psi_string_init_interned(filename
, strlen(filename
), 1);
112 struct psi_parser_input
*psi_parser_open_string(struct psi_parser
*P
,
113 const char *string
, size_t length
)
115 struct psi_parser_input
*sb
;
117 if (!(sb
= pemalloc(sizeof(*sb
) + length
+ psi_parser_maxfill(), 1))) {
118 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
119 "Could not allocate %zu bytes: %s",
120 length
+ psi_parser_maxfill(), strerror(errno
));
124 memcpy(sb
->buffer
, string
, length
);
125 memset(sb
->buffer
+ length
, 0, psi_parser_maxfill());
127 sb
->lmod
= time(NULL
);
129 sb
->file
= psi_string_init_interned("<stdin>", strlen("<stdin>"), 1);
134 struct psi_plist
*psi_parser_preprocess(struct psi_parser
*P
,
135 struct psi_plist
**tokens
)
137 if (psi_cpp_process(P
->preproc
, tokens
, NULL
)) {
143 bool psi_parser_process(struct psi_parser
*P
, struct psi_plist
*tokens
,
146 if (psi_plist_count(tokens
)) {
147 return 0 == psi_parser_proc_parse(P
, tokens
, processed
);
152 #if PSI_THREADED_PARSER
153 static void psi_smart_str_printf(smart_str
*ss
, const char *fmt
, ...)
160 len
= vasprintf(&buf
, fmt
, argv
);
164 smart_str_appendl_ex(ss
, buf
, len
, 1);
169 # define psi_smart_str_printf smart_str_append_printf
172 static inline zend_string
*macro_to_constant(struct psi_parser
*parser
,
173 zend_string
*name
, struct psi_validate_scope
*scope
)
177 struct psi_token
*tok
;
178 #if PSI_THREADED_PARSER
184 psi_smart_str_printf(&str
, "const psi\\%s = ", name
->val
);
185 if (scope
->macro
->exp
) {
186 struct psi_dump dump
= {{.hn
= &str
},
187 .fun
= (psi_dump_cb
) psi_smart_str_printf
};
189 psi_num_exp_dump(&dump
, scope
->macro
->exp
);
191 } else while (psi_plist_get(scope
->macro
->tokens
, i
++, &tok
)) {
192 if (tok
->type
== PSI_T_QUOTED_STRING
) {
193 smart_str_appendc_ex(&str
, '"', persistent
);
195 smart_str_append_ex(&str
, tok
->text
, persistent
);
196 if (tok
->type
== PSI_T_QUOTED_STRING
) {
197 smart_str_appendc_ex(&str
, '"', persistent
);
199 smart_str_appendc_ex(&str
, ' ', persistent
);
201 smart_str_appendl_ex(&str
, ";\n", 2, persistent
);
202 return smart_str_extract(&str
);
205 void psi_parser_postprocess(struct psi_parser
*P
)
209 struct psi_validate_scope scope
= {0};
211 psi_validate_scope_ctor(&scope
);
212 scope
.cpp
= P
->preproc
;
215 P
->flags
|= PSI_SILENT
;
217 ZEND_HASH_FOREACH_STR_KEY_PTR(&P
->preproc
->defs
, name
, scope
.macro
)
220 size_t processed
= 0;
221 struct psi_plist
*scanned
, *preproc
;
222 struct psi_parser_input
*I
;
225 if (scope
.macro
->sig
) {
227 } else if (!scope
.macro
->exp
) {
228 struct psi_token
*tok
;
229 if (psi_plist_count(scope
.macro
->tokens
) != 1) {
231 } else if (!psi_plist_get(scope
.macro
->tokens
, 0, &tok
)) {
233 } else if (tok
->type
!= PSI_T_QUOTED_STRING
) {
236 } else if (!psi_num_exp_validate(PSI_DATA(P
), scope
.macro
->exp
, &scope
)) {
240 cnst
= macro_to_constant(P
, name
, &scope
);
245 I
= psi_parser_open_string(P
, ZSTR_VAL(cnst
), ZSTR_LEN(cnst
));
246 zend_string_release(cnst
);
248 if (!(scanned
= psi_parser_scan(P
, I
))) {
249 psi_parser_input_free(&I
);
252 psi_parser_input_free(&I
);
254 if (!(preproc
= psi_parser_preprocess(P
, &scanned
))) {
255 psi_plist_free(scanned
);
259 parsed
= psi_parser_process(P
, preproc
, &processed
);
260 PSI_DEBUG_PRINT(PSI_DATA(P
), "PSI: processed=%zu success=%d\n",
261 processed
, (int) parsed
);
262 psi_plist_free(preproc
);
264 ZEND_HASH_FOREACH_END();
268 psi_validate_scope_dtor(&scope
);
271 bool psi_parser_parse(struct psi_parser
*P
, struct psi_parser_input
*I
)
273 struct psi_plist
*scanned
, *preproc
;
274 size_t processed
= 0;
278 if (!(scanned
= psi_parser_scan(P
, I
))) {
283 if (!(preproc
= psi_parser_preprocess(P
, &scanned
))) {
285 psi_plist_free(scanned
);
289 if (!psi_parser_process(P
, preproc
, &processed
)) {
291 psi_plist_free(preproc
);
295 psi_parser_postprocess(P
);
297 psi_plist_free(preproc
);
302 void psi_parser_dtor(struct psi_parser
*P
)
304 psi_cpp_free(&P
->preproc
);
305 psi_data_dtor(PSI_DATA(P
));
307 memset(P
, 0, sizeof(*P
));
310 void psi_parser_free(struct psi_parser
**P
)