paranoid push
[m6w6/ext-psi] / src / parser.c
1 /*******************************************************************************
2 Copyright (c) 2016, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
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.
13
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 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27 #include <sys/mman.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <stdarg.h>
31
32 #include <Zend/zend_smart_str.h>
33
34 #include "parser.h"
35
36 struct psi_parser *psi_parser_init(struct psi_parser *P, psi_error_cb error,
37 unsigned flags)
38 {
39 if (!P) {
40 P = pemalloc(sizeof(*P), 1);
41 }
42 memset(P, 0, sizeof(*P));
43
44 psi_data_ctor_with_dtors(PSI_DATA(P), error, flags);
45
46 P->preproc = psi_cpp_init(P);
47
48 return P;
49 }
50
51 struct psi_parser_input *psi_parser_open_file(struct psi_parser *P,
52 const char *filename, bool report_errors)
53 {
54 struct stat sb;
55 FILE *fp;
56 struct psi_parser_input *fb;
57
58 if (stat(filename, &sb)) {
59 if (report_errors) {
60 P->error(PSI_DATA(P), NULL, PSI_WARNING,
61 "Could not stat '%s': %s",
62 filename, strerror(errno));
63 }
64 return NULL;
65 }
66
67 if (!(fb = pemalloc(sizeof(*fb) + sb.st_size + psi_parser_maxfill(), 1))) {
68 if (report_errors) {
69 P->error(PSI_DATA(P), NULL, PSI_WARNING,
70 "Could not allocate %zu bytes for reading '%s': %s",
71 sb.st_size + psi_parser_maxfill(), filename, strerror(errno));
72 }
73 return NULL;
74 }
75
76 if (!(fp = fopen(filename, "r"))) {
77 free(fb);
78 if (report_errors) {
79 P->error(PSI_DATA(P), NULL, PSI_WARNING,
80 "Could not open '%s' for reading: %s",
81 filename, strerror(errno));
82 }
83 return NULL;
84 }
85
86 if (sb.st_size != fread(fb->buffer, 1, sb.st_size, fp)) {
87 free(fb);
88 fclose(fp);
89 if (report_errors) {
90 P->error(PSI_DATA(P), NULL, PSI_WARNING,
91 "Could not read %zu bytes from '%s': %s",
92 sb.st_size + psi_parser_maxfill(), filename, strerror(errno));
93 }
94 return NULL;
95 }
96 fclose(fp);
97
98 memset(fb->buffer + sb.st_size, 0, psi_parser_maxfill());
99
100 fb->length = sb.st_size;
101 fb->file = zend_string_init_interned(filename, strlen(filename), 1);
102
103 return fb;
104 }
105
106 struct psi_parser_input *psi_parser_open_string(struct psi_parser *P,
107 const char *string, size_t length)
108 {
109 struct psi_parser_input *sb;
110
111 if (!(sb = pemalloc(sizeof(*sb) + length + psi_parser_maxfill(), 1))) {
112 P->error(PSI_DATA(P), NULL, PSI_WARNING,
113 "Could not allocate %zu bytes: %s",
114 length + psi_parser_maxfill(), strerror(errno));
115 return NULL;
116 }
117
118 memcpy(sb->buffer, string, length);
119 memset(sb->buffer + length, 0, psi_parser_maxfill());
120
121 sb->length = length;
122 sb->file = zend_string_init_interned("<stdin>", strlen("<stdin>"), 1);
123
124 return sb;
125 }
126
127 struct psi_plist *psi_parser_preprocess(struct psi_parser *P,
128 struct psi_plist **tokens)
129 {
130 if (psi_cpp_process(P->preproc, tokens)) {
131 return *tokens;
132 }
133 return NULL;
134 }
135
136 bool psi_parser_process(struct psi_parser *P, struct psi_plist *tokens,
137 size_t *processed)
138 {
139 if (psi_plist_count(tokens)) {
140 return 0 == psi_parser_proc_parse(P, tokens, processed);
141 }
142 return true;
143 }
144
145 static inline zend_string *macro_to_constant(struct psi_parser *parser,
146 zend_string *name, struct psi_validate_scope *scope)
147 {
148 smart_str str = {0};
149
150 size_t i = 0;
151 struct psi_token *tok;
152
153 smart_str_append_printf(&str, "const psi\\%s = ", name->val);
154 if (scope->macro->exp) {
155 struct psi_dump dump = {{.hn = &str},
156 .fun = (psi_dump_cb) smart_str_append_printf};
157
158 psi_num_exp_dump(&dump, scope->macro->exp);
159 } else while (psi_plist_get(scope->macro->tokens, i++, &tok)) {
160 if (tok->type == PSI_T_QUOTED_STRING) {
161 smart_str_appendc(&str, '"');
162 }
163 smart_str_append(&str, tok->text);
164 if (tok->type == PSI_T_QUOTED_STRING) {
165 smart_str_appendc(&str, '"');
166 }
167 smart_str_appendc(&str, ' ');
168 }
169 smart_str_appendl(&str, ";\n", 2);
170 return smart_str_extract(&str);
171 }
172
173 void psi_parser_postprocess(struct psi_parser *P)
174 {
175 unsigned flags;
176 zend_string *name;
177 struct psi_validate_scope scope = {0};
178
179 psi_validate_scope_ctor(&scope);
180 scope.cpp = P->preproc;
181
182 flags = P->flags;
183 P->flags |= PSI_SILENT;
184
185 ZEND_HASH_FOREACH_STR_KEY_PTR(&P->preproc->defs, name, scope.macro)
186 {
187 bool parsed;
188 size_t processed = 0;
189 struct psi_plist *scanned, *preproc;
190 struct psi_parser_input *I;
191 zend_string *cnst;
192
193 if (scope.macro->sig) {
194 continue;
195 } else if (!scope.macro->exp) {
196 struct psi_token *tok;
197 if (psi_plist_count(scope.macro->tokens) != 1) {
198 continue;
199 } else if (!psi_plist_get(scope.macro->tokens, 0, &tok)) {
200 continue;
201 } else if (tok->type != PSI_T_QUOTED_STRING) {
202 continue;
203 }
204 } else if (!psi_num_exp_validate(PSI_DATA(P), scope.macro->exp, &scope)) {
205 continue;
206 }
207
208 cnst = macro_to_constant(P, name, &scope);
209 if (!cnst) {
210 continue;
211 }
212 //fprintf(stderr, "PARSE: %s", ZSTR_VAL(cnst));
213 I = psi_parser_open_string(P, ZSTR_VAL(cnst), ZSTR_LEN(cnst));
214 zend_string_release(cnst);
215
216 if (!(scanned = psi_parser_scan(P, I))) {
217 psi_parser_input_free(&I);
218 continue;
219 }
220 psi_parser_input_free(&I);
221
222 if (!(preproc = psi_parser_preprocess(P, &scanned))) {
223 psi_plist_free(scanned);
224 continue;
225 }
226
227 parsed = psi_parser_process(P, preproc, &processed);
228 PSI_DEBUG_PRINT(PSI_DATA(P), "PSI: processed=%zu success=%d\n",
229 processed, (int) parsed);
230 psi_plist_free(preproc);
231 }
232 ZEND_HASH_FOREACH_END();
233
234 P->flags = flags;
235
236 psi_validate_scope_dtor(&scope);
237 }
238
239 bool psi_parser_parse(struct psi_parser *P, struct psi_parser_input *I)
240 {
241 struct psi_plist *scanned, *preproc;
242 size_t processed = 0;
243
244 if (!(scanned = psi_parser_scan(P, I))) {
245 return false;
246 }
247
248 if (!(preproc = psi_parser_preprocess(P, &scanned))) {
249 psi_plist_free(scanned);
250 return false;
251 }
252
253 if (!psi_parser_process(P, preproc, &processed)) {
254 psi_plist_free(preproc);
255 return false;
256 }
257
258 psi_parser_postprocess(P);
259
260 psi_plist_free(preproc);
261 return true;
262 }
263
264 void psi_parser_dtor(struct psi_parser *P)
265 {
266 psi_cpp_free(&P->preproc);
267 psi_data_dtor(PSI_DATA(P));
268
269 memset(P, 0, sizeof(*P));
270 }
271
272 void psi_parser_free(struct psi_parser **P)
273 {
274 if (*P) {
275 psi_parser_dtor(*P);
276 free(*P);
277 *P = NULL;
278 }
279 }
280