2cf4094e93efbd5ef9303c67e23ce75c97fefecc
1 /*******************************************************************************
2 Copyright (c) 2017, 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"
31 #define PSI_CPP_SEARCH
32 #define PSI_CPP_PREDEF
33 #include "php_psi_cpp.h"
35 static void free_cpp_def(zval
*p
)
37 if (Z_TYPE_P(p
) == IS_PTR
) {
38 psi_cpp_macro_decl_free((void *) &Z_PTR_P(p
));
42 struct psi_cpp
*psi_cpp_init(struct psi_parser
*P
)
44 struct psi_cpp
*cpp
= calloc(1, sizeof(*cpp
));
47 zend_hash_init(&cpp
->defs
, 0, NULL
, free_cpp_def
, 1);
48 zend_hash_init(&cpp
->once
, 0, NULL
, NULL
, 1);
53 bool psi_cpp_load_defaults(struct psi_cpp
*cpp
)
55 struct psi_parser_input
*predef
;
57 if ((predef
= psi_parser_open_string(cpp
->parser
, psi_cpp_predef
, sizeof(psi_cpp_predef
) - 1))) {
58 bool parsed
= psi_parser_parse(cpp
->parser
, predef
);
66 static int dump_def(zval
*p
)
68 struct psi_cpp_macro_decl
*decl
= Z_PTR_P(p
);
71 dprintf(2, "#define ");
72 psi_cpp_macro_decl_dump(2, decl
);
75 return ZEND_HASH_APPLY_KEEP
;
78 void psi_cpp_free(struct psi_cpp
**cpp_ptr
)
81 struct psi_cpp
*cpp
= *cpp_ptr
;
84 if (cpp
->parser
->flags
& PSI_DEBUG
) {
85 fprintf(stderr
, "PSI: CPP decls:\n");
86 zend_hash_apply(&cpp
->defs
, dump_def
);
88 zend_hash_destroy(&cpp
->defs
);
89 zend_hash_destroy(&cpp
->once
);
94 static bool psi_cpp_stage1(struct psi_cpp
*cpp
)
96 bool name
= false, define
= false, hash
= false, eol
= true, esc
= false, ws
= false;
98 psi_cpp_tokiter_reset(cpp
);
99 while (psi_cpp_tokiter_valid(cpp
)) {
100 struct psi_token
*token
= psi_cpp_tokiter_current(cpp
);
102 /* strip comments and attributes */
103 if (token
->type
== PSI_T_COMMENT
|| token
->type
== PSI_T_CPP_ATTRIBUTE
) {
104 psi_cpp_tokiter_del_cur(cpp
, true);
108 /* line continuations */
109 if (token
->type
== PSI_T_EOL
) {
111 psi_cpp_tokiter_del_range(cpp
, psi_cpp_tokiter_index(cpp
) - 1, 2, true);
112 psi_cpp_tokiter_prev(cpp
);
116 } else if (token
->type
== PSI_T_BSLASH
) {
122 /* this whole turf is needed to distinct between:
123 * #define foo (1,2,3)
127 if (token
->type
== PSI_T_WHITESPACE
) {
129 psi_cpp_tokiter_del_cur(cpp
, true);
133 switch (token
->type
) {
159 /* mask special token for parser */
160 struct psi_token
*no_ws
= psi_token_copy(token
);
162 no_ws
->type
= PSI_T_NO_WHITESPACE
;
163 no_ws
->text
[0] = '\xA0';
164 psi_cpp_tokiter_ins_cur(cpp
, no_ws
);
170 name
= define
= hash
= eol
= false;
175 psi_cpp_tokiter_next(cpp
);
181 static bool psi_cpp_stage2(struct psi_cpp
*cpp
)
183 struct psi_plist
*parser_tokens
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
186 bool is_eol
= true, do_cpp
= false, do_expansion
= true, skip_paren
= false, skip_all
= false;
188 psi_cpp_tokiter_reset(cpp
);
190 while (psi_cpp_tokiter_valid(cpp
)) {
191 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
193 if (current
->type
== PSI_T_HASH
) {
198 } else if (current
->type
== PSI_T_EOL
) {
200 fprintf(stderr
, "PSI: CPP do_expansion=true, PSI_T_EOL\n");
206 psi_cpp_tokiter_del_cur(cpp
, true);
213 switch (current
->type
) {
216 fprintf(stderr
, "PSI: CPP do_expansion=false, PSI_T_DEFINE, skip_all\n");
218 do_expansion
= false;
228 fprintf(stderr
, "PSI: CPP do_expansion=false, PSI_T_{IF{,N},UN}DEF\n");
230 do_expansion
= false;
240 fprintf(stderr
, "PSI: CPP do_expansion=true, PSI_T_LPAREN, !skip_all, !skip_paren\n");
248 do_expansion
= !skip_all
;
250 fprintf(stderr
, "PSI: CPP do_expansion=%s, <- !skip_all\n", do_expansion
?"true":"false");
257 /* FIXME: del_range */
260 fprintf(stderr
, "PSI: CPP skip ");
261 psi_token_dump(2, current
);
263 psi_cpp_tokiter_del_cur(cpp
, true);
268 if (do_expansion
&& current
->type
== PSI_T_NAME
&& psi_cpp_tokiter_defined(cpp
)) {
269 bool expanded
= false;
271 while (psi_cpp_tokiter_expand(cpp
)) {
280 parser_tokens
= psi_plist_add(parser_tokens
, ¤t
);
283 size_t processed
= 0;
284 bool parsed
= psi_parser_process(cpp
->parser
, parser_tokens
, &processed
);
287 psi_plist_pop(parser_tokens
, NULL
);
288 psi_plist_clean(parser_tokens
);
292 psi_plist_free(parser_tokens
);
296 /* leave EOLs in the input stream, else we might end up
297 * with a hash not preceded with a new line after include */
298 psi_cpp_tokiter_del_cur(cpp
, false);
301 #if PSI_CPP_DEBUG > 1
302 psi_cpp_tokiter_dump(2, cpp
);
308 psi_cpp_tokiter_next(cpp
);
310 } while (cpp
->expanded
);
312 psi_plist_free(parser_tokens
);
317 bool psi_cpp_process(struct psi_cpp
*cpp
, struct psi_plist
**tokens
)
320 struct psi_cpp temp
= *cpp
;
322 cpp
->tokens
= *tokens
;
323 if (psi_cpp_stage1(cpp
) && psi_cpp_stage2(cpp
)) {
326 *tokens
= cpp
->tokens
;
329 cpp
->tokens
= temp
.tokens
;
330 cpp
->index
= temp
.index
;
336 bool psi_cpp_defined(struct psi_cpp
*cpp
, struct psi_token
*tok
)
340 if (tok
->type
== PSI_T_NAME
) {
341 defined
= zend_hash_str_exists(&cpp
->defs
, tok
->text
, tok
->size
);
347 fprintf(stderr
, "PSI: CPP defined -> %s ", defined
? "true" : "false");
348 psi_token_dump(2, tok
);
354 void psi_cpp_define(struct psi_cpp
*cpp
, struct psi_cpp_macro_decl
*decl
)
356 struct psi_cpp_macro_decl
*old
= zend_hash_str_find_ptr(&cpp
->defs
, decl
->token
->text
, decl
->token
->size
);
358 if (old
&& !psi_cpp_macro_decl_equal(old
, decl
)) {
359 cpp
->parser
->error(PSI_DATA(cpp
->parser
), decl
->token
, PSI_WARNING
,
360 "'%s' redefined", decl
->token
->text
);
361 cpp
->parser
->error(PSI_DATA(cpp
->parser
), old
->token
, PSI_WARNING
,
362 "'%s' previously defined", old
->token
->text
);
364 zend_hash_str_update_ptr(&cpp
->defs
, decl
->token
->text
, decl
->token
->size
, decl
);
367 bool psi_cpp_undef(struct psi_cpp
*cpp
, struct psi_token
*tok
)
369 return SUCCESS
== zend_hash_str_del(&cpp
->defs
, tok
->text
, tok
->size
);
372 bool psi_cpp_if(struct psi_cpp
*cpp
, struct psi_cpp_exp
*exp
)
374 if (!psi_num_exp_validate(PSI_DATA(cpp
->parser
), exp
->data
.num
, NULL
, NULL
, NULL
, NULL
, NULL
)) {
377 if (!psi_long_num_exp(exp
->data
.num
, NULL
, &cpp
->defs
)) {
383 static inline bool try_include(struct psi_cpp
*cpp
, const char *path
, bool *parsed
)
385 struct psi_parser_input
*include
;
387 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP include trying %s\n", path
);
389 include
= psi_parser_open_file(cpp
->parser
, path
, false);
391 struct psi_plist
*tokens
;
393 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP include scanning %s\n", path
);
395 tokens
= psi_parser_scan(cpp
->parser
, include
);
397 *parsed
= psi_cpp_process(cpp
, &tokens
);
401 psi_cpp_tokiter_ins_range(cpp
, cpp
->index
,
402 psi_plist_count(tokens
), psi_plist_eles(tokens
));
405 psi_plist_free(tokens
);
410 zend_hash_str_add_empty_element(&cpp
->once
, path
, strlen(path
));
416 bool psi_cpp_include(struct psi_cpp
*cpp
, const char *file
, unsigned flags
)
420 int p_len
, f_len
= strlen(file
) - 2;
422 if (file
[1] == '/') {
423 if (PATH_MAX
> (p_len
= snprintf(path
, PATH_MAX
, "%.*s", f_len
, file
+ 1))) {
424 if ((flags
& PSI_CPP_INCLUDE_ONCE
) && zend_hash_str_exists(&cpp
->once
, path
, p_len
)) {
427 return try_include(cpp
, path
, &parsed
) && parsed
;
432 if ((flags
& PSI_CPP_INCLUDE_NEXT
) && cpp
->search
) {
433 if ((sep
= strchr(cpp
->search
, ':'))) {
434 cpp
->search
= sep
+ 1;
436 /* point to end of string */
437 cpp
->search
+= strlen(cpp
->search
);
441 if (!(flags
& PSI_CPP_INCLUDE_NEXT
) || !cpp
->search
) {
442 cpp
->search
= &psi_cpp_search
[0];
448 sep
= strchr(cpp
->search
, ':');
449 d_len
= sep
? sep
- cpp
->search
: strlen(cpp
->search
);
451 if (PATH_MAX
> (p_len
= snprintf(path
, PATH_MAX
, "%.*s/%.*s", d_len
, cpp
->search
, f_len
, file
+ 1))) {
452 if ((flags
& PSI_CPP_INCLUDE_ONCE
) && zend_hash_str_exists(&cpp
->once
, path
, p_len
)) {
455 if (try_include(cpp
, path
, &parsed
)) {
459 cpp
->search
= sep
+ 1;