fix char_width decl
[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 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #else
29 # include "php_config.h"
30 #endif
31 #include <sys/mman.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <time.h>
36
37 #include <Zend/zend_smart_str.h>
38
39 #include "parser.h"
40
41 struct psi_parser *psi_parser_init(struct psi_parser *P, psi_error_cb error,
42 unsigned flags)
43 {
44 if (!P) {
45 P = pemalloc(sizeof(*P), 1);
46 }
47 memset(P, 0, sizeof(*P));
48
49 psi_data_ctor_with_dtors(PSI_DATA(P), error, flags);
50
51 P->preproc = psi_cpp_init(P);
52
53 return P;
54 }
55
56 struct psi_parser_input *psi_parser_open_file(struct psi_parser *P,
57 const char *filename, bool report_errors)
58 {
59 struct stat sb;
60 FILE *fp;
61 struct psi_parser_input *fb;
62
63 if (stat(filename, &sb)) {
64 if (report_errors) {
65 P->error(PSI_DATA(P), NULL, PSI_WARNING,
66 "Could not stat '%s': %s",
67 filename, strerror(errno));
68 }
69 return NULL;
70 }
71
72 if (!(fb = pemalloc(sizeof(*fb) + sb.st_size + psi_parser_maxfill(), 1))) {
73 if (report_errors) {
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));
77 }
78 return NULL;
79 }
80
81 if (!(fp = fopen(filename, "r"))) {
82 free(fb);
83 if (report_errors) {
84 P->error(PSI_DATA(P), NULL, PSI_WARNING,
85 "Could not open '%s' for reading: %s",
86 filename, strerror(errno));
87 }
88 return NULL;
89 }
90
91 if (sb.st_size != fread(fb->buffer, 1, sb.st_size, fp)) {
92 free(fb);
93 fclose(fp);
94 if (report_errors) {
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));
98 }
99 return NULL;
100 }
101 fclose(fp);
102
103 memset(fb->buffer + sb.st_size, 0, psi_parser_maxfill());
104
105 fb->lmod = sb.st_mtime;
106 fb->length = sb.st_size;
107 fb->file = psi_string_init_interned(filename, strlen(filename), 1);
108
109 return fb;
110 }
111
112 struct psi_parser_input *psi_parser_open_string(struct psi_parser *P,
113 const char *string, size_t length)
114 {
115 struct psi_parser_input *sb;
116
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));
121 return NULL;
122 }
123
124 memcpy(sb->buffer, string, length);
125 memset(sb->buffer + length, 0, psi_parser_maxfill());
126
127 sb->lmod = time(NULL);
128 sb->length = length;
129 sb->file = psi_string_init_interned("<stdin>", strlen("<stdin>"), 1);
130
131 return sb;
132 }
133
134 struct psi_plist *psi_parser_preprocess(struct psi_parser *P,
135 struct psi_plist **tokens)
136 {
137 if (psi_cpp_process(P->preproc, tokens, NULL)) {
138 return *tokens;
139 }
140 return NULL;
141 }
142
143 bool psi_parser_process(struct psi_parser *P, struct psi_plist *tokens,
144 size_t *processed)
145 {
146 if (psi_plist_count(tokens)) {
147 return 0 == psi_parser_proc_parse(P, tokens, processed);
148 }
149 return true;
150 }
151
152 #if PSI_THREADED_PARSER
153 static void psi_smart_str_printf(smart_str *ss, const char *fmt, ...)
154 {
155 va_list argv;
156 char *buf;
157 int len;
158
159 va_start(argv, fmt);
160 len = vasprintf(&buf, fmt, argv);
161 va_end(argv);
162
163 if (len != -1) {
164 smart_str_appendl_ex(ss, buf, len, 1);
165 free(buf);
166 }
167 }
168 #else
169 # define psi_smart_str_printf smart_str_append_printf
170 #endif
171
172 static inline zend_string *macro_to_constant(struct psi_parser *parser,
173 zend_string *name, struct psi_validate_scope *scope)
174 {
175 smart_str str = {0};
176 size_t i = 0;
177 struct psi_token *tok;
178 #if PSI_THREADED_PARSER
179 int persistent = 1;
180 #else
181 int persistent = 0;
182 #endif
183
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};
188
189 psi_num_exp_dump(&dump, scope->macro->exp);
190
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);
194 }
195 smart_str_append_ex(&str, tok->text, persistent);
196 if (tok->type == PSI_T_QUOTED_STRING) {
197 smart_str_appendc_ex(&str, '"', persistent);
198 }
199 smart_str_appendc_ex(&str, ' ', persistent);
200 }
201 smart_str_appendl_ex(&str, ";\n", 2, persistent);
202 return smart_str_extract(&str);
203 }
204
205 void psi_parser_postprocess(struct psi_parser *P)
206 {
207 unsigned flags;
208 zend_string *name;
209 struct psi_validate_scope scope = {0};
210
211 psi_validate_scope_ctor(&scope);
212 scope.cpp = P->preproc;
213
214 flags = P->flags;
215 P->flags |= PSI_SILENT;
216
217 ZEND_HASH_FOREACH_STR_KEY_PTR(&P->preproc->defs, name, scope.macro)
218 {
219 bool parsed;
220 size_t processed = 0;
221 struct psi_plist *scanned, *preproc;
222 struct psi_parser_input *I;
223 zend_string *cnst;
224
225 if (scope.macro->sig) {
226 continue;
227 } else if (!scope.macro->exp) {
228 struct psi_token *tok;
229 if (psi_plist_count(scope.macro->tokens) != 1) {
230 continue;
231 } else if (!psi_plist_get(scope.macro->tokens, 0, &tok)) {
232 continue;
233 } else if (tok->type != PSI_T_QUOTED_STRING) {
234 continue;
235 }
236 } else if (!psi_num_exp_validate(PSI_DATA(P), scope.macro->exp, &scope)) {
237 continue;
238 }
239
240 cnst = macro_to_constant(P, name, &scope);
241 if (!cnst) {
242 continue;
243 }
244
245 I = psi_parser_open_string(P, ZSTR_VAL(cnst), ZSTR_LEN(cnst));
246 zend_string_release(cnst);
247
248 if (!(scanned = psi_parser_scan(P, I))) {
249 psi_parser_input_free(&I);
250 continue;
251 }
252 psi_parser_input_free(&I);
253
254 if (!(preproc = psi_parser_preprocess(P, &scanned))) {
255 psi_plist_free(scanned);
256 continue;
257 }
258
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);
263 }
264 ZEND_HASH_FOREACH_END();
265
266 P->flags = flags;
267
268 psi_validate_scope_dtor(&scope);
269 }
270
271 bool psi_parser_parse(struct psi_parser *P, struct psi_parser_input *I)
272 {
273 struct psi_plist *scanned, *preproc;
274 size_t processed = 0;
275
276 P->input = I;
277
278 if (!(scanned = psi_parser_scan(P, I))) {
279 P->input = NULL;
280 return false;
281 }
282
283 if (!(preproc = psi_parser_preprocess(P, &scanned))) {
284 P->input = NULL;
285 psi_plist_free(scanned);
286 return false;
287 }
288
289 if (!psi_parser_process(P, preproc, &processed)) {
290 P->input = NULL;
291 psi_plist_free(preproc);
292 return false;
293 }
294
295 psi_parser_postprocess(P);
296
297 psi_plist_free(preproc);
298 P->input = NULL;
299 return true;
300 }
301
302 void psi_parser_dtor(struct psi_parser *P)
303 {
304 psi_cpp_free(&P->preproc);
305 psi_data_dtor(PSI_DATA(P));
306
307 memset(P, 0, sizeof(*P));
308 }
309
310 void psi_parser_free(struct psi_parser **P)
311 {
312 if (*P) {
313 psi_parser_dtor(*P);
314 free(*P);
315 *P = NULL;
316 }
317 }
318