+
+bool psi_cpp_include(struct psi_cpp *cpp, const struct psi_token *file, unsigned flags)
+{
+ bool parsed = false;
+ char path[PATH_MAX];
+ struct psi_plist *tokens;
+ struct psi_parser_input *include;
+
+ if (!psi_cpp_has_include(cpp, file, flags, path)) {
+ return false;
+ }
+
+ if (flags & PSI_CPP_INCLUDE_ONCE) {
+ if (zend_hash_str_exists(&cpp->once, path, strlen(path))) {
+ return true;
+ }
+ }
+
+ PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP %s opening %s\n",
+ include_flavor[flags], path);
+
+ include = psi_parser_open_file(cpp->parser, path, false);
+ if (!include) {
+ return false;
+ }
+
+ zend_hash_str_add_empty_element(&cpp->once, path, strlen(path));
+
+ PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP include scanning %s\n", path);
+
+ tokens = psi_parser_scan(cpp->parser, include);
+ psi_parser_input_free(&include);
+
+ if (!tokens) {
+ return false;
+ }
+
+ parsed = psi_cpp_process(cpp, &tokens, NULL);
+ if (!parsed) {
+ psi_plist_free(tokens);
+ return false;
+ }
+
+ psi_cpp_tokiter_add_range(cpp, psi_plist_count(tokens), psi_plist_eles(tokens));
+ free(tokens);
+
+ ++cpp->expanded;
+ return true;
+}
+
+#ifndef HAVE_EACCESS
+# define eaccess access
+#endif
+bool psi_cpp_has_include(struct psi_cpp *cpp, const struct psi_token *file, unsigned flags, char *path)
+{
+ char temp[PATH_MAX];
+
+ if (!path) {
+ path = temp;
+ }
+
+ if (file->type == PSI_T_QUOTED_STRING && (!(flags & PSI_CPP_INCLUDE_NEXT) || file->text->val[0] == '/')) {
+ /* first try as is, full or relative path */
+ if (file->text->val[0] == '/') {
+ path = file->text->val;
+ } else {
+ char *dir;
+ size_t len;
+
+ strncpy(path, file->file->val, PATH_MAX);
+
+ dir = dirname(path);
+ len = strlen(dir);
+
+ assert(len + file->text->len + 1 < PATH_MAX);
+
+ memmove(path, dir, len);
+ path[len] = '/';
+ memcpy(&(path)[len + 1], file->text->val, file->text->len + 1);
+ }
+
+ PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP %s trying %s\n",
+ include_flavor[flags], path);
+ if (0 == eaccess(path, R_OK)) {
+ return true;
+ }
+ }
+
+ /* look through search paths */
+ if (file->text->val[0] != '/') {
+ const char *sep;
+ int p_len;
+
+ if ((flags & PSI_CPP_INCLUDE_NEXT) && cpp->search) {
+ if ((sep = strchr(cpp->search, ':'))) {
+ cpp->search = sep + 1;
+ } else {
+ /* point to end of string */
+ cpp->search += strlen(cpp->search);
+ }
+ }
+
+ if (!(flags & PSI_CPP_INCLUDE_NEXT)) {
+ cpp->search = PSI_G(search_path);
+ }
+
+ do {
+ int d_len;
+
+ sep = strchr(cpp->search, ':');
+ d_len = sep ? sep - cpp->search : strlen(cpp->search);
+
+ if (PATH_MAX > (p_len = snprintf(path, PATH_MAX, "%.*s/%.*s", d_len, cpp->search, (int) file->text->len, file->text->val))) {
+ PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP %s trying %s\n",
+ include_flavor[flags], path);
+ if (0 == eaccess(path, R_OK)) {
+ return true;
+ }
+ }
+
+ if (sep) {
+ cpp->search = sep + 1;
+ }
+ } while (sep);
+ }
+
+ return false;
+}