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 *******************************************************************************/
26 #include "php_psi_stdinc.h"
32 #include <Zend/zend_smart_str.h>
36 struct psi_parser
*psi_parser_init(struct psi_parser
*P
, psi_error_cb error
, unsigned flags
)
39 P
= pemalloc(sizeof(*P
), 1);
41 memset(P
, 0, sizeof(*P
));
43 psi_data_ctor_with_dtors(PSI_DATA(P
), error
, flags
);
45 P
->preproc
= psi_cpp_init(P
);
50 struct psi_parser_input
*psi_parser_open_file(struct psi_parser
*P
, const char *filename
, bool report_errors
)
54 struct psi_parser_input
*fb
;
56 if (stat(filename
, &sb
)) {
58 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
59 "Could not stat '%s': %s",
60 filename
, strerror(errno
));
65 if (!(fb
= pemalloc(sizeof(*fb
) + sb
.st_size
+ psi_parser_maxfill(), 1))) {
67 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
68 "Could not allocate %zu bytes for reading '%s': %s",
69 sb
.st_size
+ psi_parser_maxfill(), filename
, strerror(errno
));
74 if (!(fp
= fopen(filename
, "r"))) {
77 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
78 "Could not open '%s' for reading: %s",
79 filename
, strerror(errno
));
84 if (sb
.st_size
!= fread(fb
->buffer
, 1, sb
.st_size
, fp
)) {
88 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
89 "Could not read %zu bytes from '%s': %s",
90 sb
.st_size
+ psi_parser_maxfill(), filename
, strerror(errno
));
96 memset(fb
->buffer
+ sb
.st_size
, 0, psi_parser_maxfill());
98 fb
->length
= sb
.st_size
;
99 fb
->file
= zend_string_init_interned(filename
, strlen(filename
), 1);
104 struct psi_parser_input
*psi_parser_open_string(struct psi_parser
*P
, const char *string
, size_t length
)
106 struct psi_parser_input
*sb
;
108 if (!(sb
= pemalloc(sizeof(*sb
) + length
+ psi_parser_maxfill(), 1))) {
109 P
->error(PSI_DATA(P
), NULL
, PSI_WARNING
,
110 "Could not allocate %zu bytes: %s",
111 length
+ psi_parser_maxfill(), strerror(errno
));
115 memcpy(sb
->buffer
, string
, length
);
116 memset(sb
->buffer
+ length
, 0, psi_parser_maxfill());
119 sb
->file
= zend_string_init_interned("<stdin>", strlen("<stdin>"), 1);
124 struct psi_plist
*psi_parser_preprocess(struct psi_parser
*P
, struct psi_plist
**tokens
)
126 if (psi_cpp_process(P
->preproc
, tokens
)) {
132 bool psi_parser_process(struct psi_parser
*P
, struct psi_plist
*tokens
, size_t *processed
)
134 if (psi_plist_count(tokens
)) {
135 return 0 == psi_parser_proc_parse(P
, tokens
, processed
);
140 static inline zend_string
*macro_to_constant(struct psi_parser
*parser
,
141 zend_string
*name
, struct psi_validate_scope
*scope
)
146 struct psi_token
*tok
;
148 smart_str_append_printf(&str
, "const psi\\%s = ", name
->val
);
149 if (scope
->macro
->exp
) {
150 struct psi_dump dump
= {{.hn
= &str
}, .fun
= (psi_dump_cb
) smart_str_append_printf
};
152 psi_num_exp_dump(&dump
, scope
->macro
->exp
);
153 } else while (psi_plist_get(scope
->macro
->tokens
, i
++, &tok
)) {
154 if (tok
->type
== PSI_T_QUOTED_STRING
) {
155 smart_str_appendc(&str
, '"');
157 smart_str_append(&str
, tok
->text
);
158 if (tok
->type
== PSI_T_QUOTED_STRING
) {
159 smart_str_appendc(&str
, '"');
161 smart_str_appendc(&str
, ' ');
163 smart_str_appendl(&str
, ";\n", 2);
164 return smart_str_extract(&str
);
167 void psi_parser_postprocess(struct psi_parser
*P
)
171 struct psi_validate_scope scope
= {0};
173 psi_validate_scope_ctor(&scope
);
174 scope
.cpp
= P
->preproc
;
177 P
->flags
|= PSI_SILENT
;
179 ZEND_HASH_FOREACH_STR_KEY_PTR(&P
->preproc
->defs
, name
, scope
.macro
)
182 size_t processed
= 0;
183 struct psi_plist
*scanned
, *preproc
;
184 struct psi_parser_input
*I
;
187 if (scope
.macro
->sig
) {
189 } else if (!scope
.macro
->exp
) {
190 struct psi_token
*tok
;
191 if (psi_plist_count(scope
.macro
->tokens
) != 1) {
193 } else if (!psi_plist_get(scope
.macro
->tokens
, 0, &tok
)) {
195 } else if (tok
->type
!= PSI_T_QUOTED_STRING
) {
198 } else if (!psi_num_exp_validate(PSI_DATA(P
), scope
.macro
->exp
, &scope
)) {
202 cnst
= macro_to_constant(P
, name
, &scope
);
207 I
= psi_parser_open_string(P
, ZSTR_VAL(cnst
), ZSTR_LEN(cnst
));
208 zend_string_release(cnst
);
210 if (!(scanned
= psi_parser_scan(P
, I
))) {
211 psi_parser_input_free(&I
);
214 psi_parser_input_free(&I
);
216 if (!(preproc
= psi_parser_preprocess(P
, &scanned
))) {
217 psi_plist_free(scanned
);
221 parsed
= psi_parser_process(P
, preproc
, &processed
);
222 PSI_DEBUG_PRINT(PSI_DATA(P
), "PSI: processed=%zu success=%d\n",
223 processed
, (int) parsed
);
224 psi_plist_free(preproc
);
226 ZEND_HASH_FOREACH_END();
230 psi_validate_scope_dtor(&scope
);
233 bool psi_parser_parse(struct psi_parser
*P
, struct psi_parser_input
*I
)
235 struct psi_plist
*scanned
, *preproc
;
236 size_t processed
= 0;
238 if (!(scanned
= psi_parser_scan(P
, I
))) {
242 if (!(preproc
= psi_parser_preprocess(P
, &scanned
))) {
243 psi_plist_free(scanned
);
247 if (!psi_parser_process(P
, preproc
, &processed
)) {
248 psi_plist_free(preproc
);
252 psi_parser_postprocess(P
);
254 psi_plist_free(preproc
);
258 void psi_parser_dtor(struct psi_parser
*P
)
260 psi_cpp_free(&P
->preproc
);
261 psi_data_dtor(PSI_DATA(P
));
263 memset(P
, 0, sizeof(*P
));
266 void psi_parser_free(struct psi_parser
**P
)