c831b67838171f7157a117e30862b91b10d702cc
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
104 || token
->type
== PSI_T_CPP_ATTRIBUTE
) {
105 psi_cpp_tokiter_del_cur(cpp
, true);
109 /* line continuations */
110 if (token
->type
== PSI_T_EOL
) {
112 psi_cpp_tokiter_del_range(cpp
, psi_cpp_tokiter_index(cpp
) - 1, 2, true);
113 psi_cpp_tokiter_prev(cpp
);
117 } else if (token
->type
== PSI_T_BSLASH
) {
123 /* this whole turf is needed to distinct between:
124 * #define foo (1,2,3)
128 if (token
->type
== PSI_T_WHITESPACE
) {
133 psi_cpp_tokiter_del_cur(cpp
, true);
137 switch (token
->type
) {
163 /* mask special token for parser */
164 struct psi_token
*no_ws
= psi_token_copy(token
);
166 no_ws
->type
= PSI_T_NO_WHITESPACE
;
167 no_ws
->text
[0] = '\xA0';
168 psi_cpp_tokiter_ins_cur(cpp
, no_ws
);
174 name
= define
= hash
= eol
= false;
179 psi_cpp_tokiter_next(cpp
);
185 static bool psi_cpp_stage2(struct psi_cpp
*cpp
)
187 struct psi_plist
*parser_tokens
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
190 bool is_eol
= true, do_cpp
= false, do_expansion
= true, skip_paren
= false, skip_all
= false;
192 psi_cpp_tokiter_reset(cpp
);
194 while (psi_cpp_tokiter_valid(cpp
)) {
195 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
197 if (current
->type
== PSI_T_HASH
) {
202 } else if (current
->type
== PSI_T_EOL
) {
204 fprintf(stderr
, "PSI: CPP do_expansion=true, PSI_T_EOL\n");
210 psi_cpp_tokiter_del_cur(cpp
, true);
217 switch (current
->type
) {
220 fprintf(stderr
, "PSI: CPP do_expansion=false, PSI_T_DEFINE, skip_all\n");
222 do_expansion
= false;
232 fprintf(stderr
, "PSI: CPP do_expansion=false, PSI_T_{IF{,N},UN}DEF\n");
234 do_expansion
= false;
244 fprintf(stderr
, "PSI: CPP do_expansion=true, PSI_T_LPAREN, !skip_all, !skip_paren\n");
252 do_expansion
= !skip_all
;
254 fprintf(stderr
, "PSI: CPP do_expansion=%s, <- !skip_all\n", do_expansion
?"true":"false");
261 /* FIXME: del_range */
264 fprintf(stderr
, "PSI: CPP skip ");
265 psi_token_dump(2, current
);
267 psi_cpp_tokiter_del_cur(cpp
, true);
272 if (do_expansion
&& current
->type
== PSI_T_NAME
&& psi_cpp_tokiter_defined(cpp
)) {
273 bool expanded
= false;
275 while (psi_cpp_tokiter_expand(cpp
)) {
284 parser_tokens
= psi_plist_add(parser_tokens
, ¤t
);
287 size_t processed
= 0;
288 bool parsed
= psi_parser_process(cpp
->parser
, parser_tokens
, &processed
);
291 psi_plist_pop(parser_tokens
, NULL
);
292 psi_plist_clean(parser_tokens
);
296 psi_plist_free(parser_tokens
);
300 /* leave EOLs in the input stream, else we might end up
301 * with a hash not preceded with a new line after include */
302 psi_cpp_tokiter_del_cur(cpp
, false);
305 #if PSI_CPP_DEBUG > 1
306 psi_cpp_tokiter_dump(2, cpp
);
312 psi_cpp_tokiter_next(cpp
);
314 } while (cpp
->expanded
);
316 psi_plist_free(parser_tokens
);
321 bool psi_cpp_process(struct psi_cpp
*cpp
, struct psi_plist
**tokens
)
324 struct psi_cpp temp
= *cpp
;
326 cpp
->tokens
= *tokens
;
327 if (psi_cpp_stage1(cpp
) && psi_cpp_stage2(cpp
)) {
330 *tokens
= cpp
->tokens
;
333 cpp
->tokens
= temp
.tokens
;
334 cpp
->index
= temp
.index
;
340 bool psi_cpp_defined(struct psi_cpp
*cpp
, struct psi_token
*tok
)
344 if (tok
->type
== PSI_T_NAME
) {
345 defined
= zend_hash_str_exists(&cpp
->defs
, tok
->text
, tok
->size
);
351 fprintf(stderr
, "PSI: CPP defined -> %s ", defined
? "true" : "false");
352 psi_token_dump(2, tok
);
358 void psi_cpp_define(struct psi_cpp
*cpp
, struct psi_cpp_macro_decl
*decl
)
360 struct psi_cpp_macro_decl
*old
= zend_hash_str_find_ptr(&cpp
->defs
, decl
->token
->text
, decl
->token
->size
);
362 if (old
&& !psi_cpp_macro_decl_equal(old
, decl
)) {
363 cpp
->parser
->error(PSI_DATA(cpp
->parser
), decl
->token
, PSI_WARNING
,
364 "'%s' redefined", decl
->token
->text
);
365 cpp
->parser
->error(PSI_DATA(cpp
->parser
), old
->token
, PSI_WARNING
,
366 "'%s' previously defined", old
->token
->text
);
368 zend_hash_str_update_ptr(&cpp
->defs
, decl
->token
->text
, decl
->token
->size
, decl
);
371 bool psi_cpp_undef(struct psi_cpp
*cpp
, struct psi_token
*tok
)
373 return SUCCESS
== zend_hash_str_del(&cpp
->defs
, tok
->text
, tok
->size
);
376 bool psi_cpp_if(struct psi_cpp
*cpp
, struct psi_cpp_exp
*exp
)
378 if (!psi_num_exp_validate(PSI_DATA(cpp
->parser
), exp
->data
.num
, NULL
, NULL
, NULL
, NULL
, NULL
)) {
381 if (!psi_long_num_exp(exp
->data
.num
, NULL
, &cpp
->defs
)) {
387 static inline bool try_include(struct psi_cpp
*cpp
, const char *path
, bool *parsed
)
389 struct psi_parser_input
*include
;
391 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP include trying %s\n", path
);
393 include
= psi_parser_open_file(cpp
->parser
, path
, false);
395 struct psi_plist
*tokens
;
397 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP include scanning %s\n", path
);
399 tokens
= psi_parser_scan(cpp
->parser
, include
);
401 *parsed
= psi_cpp_process(cpp
, &tokens
);
405 psi_cpp_tokiter_ins_range(cpp
, cpp
->index
,
406 psi_plist_count(tokens
), psi_plist_eles(tokens
));
409 psi_plist_free(tokens
);
414 zend_hash_str_add_empty_element(&cpp
->once
, path
, strlen(path
));
420 bool psi_cpp_include(struct psi_cpp
*cpp
, const char *file
, unsigned flags
)
423 int f_len
= strlen(file
);
425 if (!(flags
& PSI_CPP_INCLUDE_NEXT
) || *file
== '/') {
426 /* first try as is, full or relative path */
427 if ((flags
& PSI_CPP_INCLUDE_ONCE
) && zend_hash_str_exists(&cpp
->once
, file
, f_len
)) {
430 if (try_include(cpp
, file
, &parsed
)) {
436 /* look through search paths */
442 if ((flags
& PSI_CPP_INCLUDE_NEXT
) && cpp
->search
) {
443 if ((sep
= strchr(cpp
->search
, ':'))) {
444 cpp
->search
= sep
+ 1;
446 /* point to end of string */
447 cpp
->search
+= strlen(cpp
->search
);
451 if (!(flags
& PSI_CPP_INCLUDE_NEXT
) || !cpp
->search
) {
452 cpp
->search
= &psi_cpp_search
[0];
458 sep
= strchr(cpp
->search
, ':');
459 d_len
= sep
? sep
- cpp
->search
: strlen(cpp
->search
);
461 if (PATH_MAX
> (p_len
= snprintf(path
, PATH_MAX
, "%.*s/%.*s", d_len
, cpp
->search
, f_len
, file
))) {
462 if ((flags
& PSI_CPP_INCLUDE_ONCE
) && zend_hash_str_exists(&cpp
->once
, path
, p_len
)) {
465 if (try_include(cpp
, path
, &parsed
)) {
469 cpp
->search
= sep
+ 1;