diff options
Diffstat (limited to 'libcpp/files.cc')
-rw-r--r-- | libcpp/files.cc | 378 |
1 files changed, 362 insertions, 16 deletions
diff --git a/libcpp/files.cc b/libcpp/files.cc index fc66b9c..52b1850 100644 --- a/libcpp/files.cc +++ b/libcpp/files.cc @@ -87,6 +87,9 @@ struct _cpp_file /* As filled in by stat(2) for the file. */ struct stat st; + /* Size for #embed, perhaps smaller than st.st_size. */ + size_t limit; + /* File descriptor. Invalid if -1, otherwise open. */ int fd; @@ -113,6 +116,9 @@ struct _cpp_file and error should be emitted if it is included normally. */ bool deferred_error : 1; + /* File loaded from #embed. */ + bool embed : 1; + /* > 0: Known C++ Module header unit, <0: known not. ==0, unknown */ int header_unit : 2; }; @@ -187,7 +193,8 @@ static const char *dir_name_of_file (_cpp_file *file); static void open_file_failed (cpp_reader *pfile, _cpp_file *file, int, location_t); static struct cpp_file_hash_entry *search_cache (struct cpp_file_hash_entry *head, - const cpp_dir *start_dir); + const cpp_dir *start_dir, + bool is_embed); static _cpp_file *make_cpp_file (cpp_dir *, const char *fname); static void destroy_cpp_file (_cpp_file *); static cpp_dir *make_cpp_dir (cpp_reader *, const char *dir_name, int sysp); @@ -425,7 +432,7 @@ find_file_in_dir (cpp_reader *pfile, _cpp_file *file, bool *invalid_pch, } file->path = path; - if (pch_open_file (pfile, file, invalid_pch)) + if (!file->embed && pch_open_file (pfile, file, invalid_pch)) return true; if (open_file (file)) @@ -514,7 +521,9 @@ _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, bool invalid_pch = false; bool saw_bracket_include = false; bool saw_quote_include = false; + bool saw_embed_include = false; struct cpp_dir *found_in_cache = NULL; + bool is_embed = kind == _cpp_FFK_EMBED || kind == _cpp_FFK_HAS_EMBED; /* Ensure we get no confusion between cached files and directories. */ if (start_dir == NULL) @@ -526,10 +535,12 @@ _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, /* First check the cache before we resort to memory allocation. */ cpp_file_hash_entry *entry - = search_cache ((struct cpp_file_hash_entry *) *hash_slot, start_dir); + = search_cache ((struct cpp_file_hash_entry *) *hash_slot, start_dir, + is_embed); if (entry) { - if (entry->u.file->deferred_error && kind == _cpp_FFK_NORMAL) + if (entry->u.file->deferred_error + && (kind == _cpp_FFK_NORMAL || kind == _cpp_FFK_EMBED)) { open_file_failed (pfile, entry->u.file, angle_brackets, loc); entry->u.file->deferred_error = false; @@ -541,6 +552,7 @@ _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, file->implicit_preinclude = (kind == _cpp_FFK_PRE_INCLUDE || (pfile->buffer && pfile->buffer->file->implicit_preinclude)); + file->embed = is_embed; if (kind == _cpp_FFK_FAKE) file->dont_read = true; @@ -551,10 +563,17 @@ _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, if (find_file_in_dir (pfile, file, &invalid_pch, loc)) break; - file->dir = file->dir->next; + if (is_embed + && file->dir == start_dir + && start_dir != pfile->embed_include + && start_dir != &pfile->no_search_path) + file->dir = pfile->embed_include; + else + file->dir = file->dir->next; if (file->dir == NULL) { - if (search_path_exhausted (pfile, fname, file)) + if (!is_embed + && search_path_exhausted (pfile, fname, file)) { /* Although this file must not go in the cache, because the file found might depend on things (like @@ -601,7 +620,7 @@ _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, return NULL; } - if (kind != _cpp_FFK_HAS_INCLUDE) + if (kind != _cpp_FFK_HAS_INCLUDE && kind != _cpp_FFK_HAS_EMBED) open_file_failed (pfile, file, angle_brackets, loc); else file->deferred_error = true; @@ -615,11 +634,14 @@ _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, saw_bracket_include = true; else if (file->dir == pfile->quote_include) saw_quote_include = true; + else if (file->dir == pfile->embed_include) + saw_embed_include = true; else continue; entry - = search_cache ((struct cpp_file_hash_entry *) *hash_slot, file->dir); + = search_cache ((struct cpp_file_hash_entry *) *hash_slot, + file->dir, is_embed); if (entry) { found_in_cache = file->dir; @@ -673,6 +695,17 @@ _cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, entry->u.file = file; *hash_slot = (void *) entry; } + if (saw_embed_include + && pfile->embed_include != start_dir + && found_in_cache != pfile->embed_include) + { + entry = new_file_hash_entry (pfile); + entry->next = (struct cpp_file_hash_entry *) *hash_slot; + entry->start_dir = pfile->embed_include; + entry->location = loc; + entry->u.file = file; + *hash_slot = (void *) entry; + } return file; } @@ -760,7 +793,7 @@ read_file_guts (cpp_reader *pfile, _cpp_file *file, location_t loc, if (pfile && regular && total != size && STAT_SIZE_RELIABLE (file->st)) cpp_error_at (pfile, CPP_DL_WARNING, loc, - "%s is shorter than expected", file->path); + "%s is shorter than expected", file->path); file->buffer = _cpp_convert_input (pfile, input_charset, @@ -871,6 +904,9 @@ has_unique_contents (cpp_reader *pfile, _cpp_file *file, bool import, if (f == file) continue; /* It'sa me! */ + if (f->embed) + continue; + if ((import || f->once_only) && f->err_no == 0 && f->st.st_mtime == file->st.st_mtime @@ -1062,12 +1098,12 @@ search_path_head (cpp_reader *pfile, const char *fname, int angle_brackets, && file->dir != &pfile->no_search_path) dir = file->dir->next; else if (angle_brackets) - dir = pfile->bracket_include; + dir = type == IT_EMBED ? pfile->embed_include : pfile->bracket_include; else if (type == IT_CMDLINE) /* -include and -imacros use the #include "" chain with the preprocessor's cwd prepended. */ return make_cpp_dir (pfile, "./", false); - else if (pfile->quote_ignores_source_dir) + else if (pfile->quote_ignores_source_dir && type != IT_EMBED) dir = pfile->quote_include; else return make_cpp_dir (pfile, dir_name_of_file (file), @@ -1181,6 +1217,306 @@ cpp_probe_header_unit (cpp_reader *pfile, const char *name, bool angle, return nullptr; } +/* Try to load FNAME with #embed/__has_embed parameters PARAMS. + If !PARAMS->has_embed, return new token in pfile->directive_result + (first token) and rest in a pushed non-macro context. + Returns 0 for not found/errors, 1 for non-empty resource and 2 + for empty resource. */ + +int +_cpp_stack_embed (cpp_reader *pfile, const char *fname, bool angle, + struct cpp_embed_params *params) +{ + cpp_dir *dir = search_path_head (pfile, fname, angle, IT_EMBED, + params->has_embed); + if (!dir) + return 0; + _cpp_file *file = _cpp_find_file (pfile, fname, dir, angle, + params->has_embed + ? _cpp_FFK_HAS_EMBED : _cpp_FFK_EMBED, + params->loc); + if (!file) + return 0; + if (file->dont_read || file->err_no) + return 0; + _cpp_file *orig_file = file; + if (file->buffer_valid + && (!S_ISREG (file->st.st_mode) + || (file->limit < file->st.st_size + (size_t) 0 + && file->limit < params->limit))) + { + bool found = false; + if (S_ISREG (file->st.st_mode)) + { + while (file->next_file + && file->next_file->embed + && file->next_file->buffer_valid + && file->next_file->dir == file->dir + && strcmp (file->name, file->next_file->name) == 0 + && strcmp (file->path, file->next_file->path) == 0) + { + file = file->next_file; + if (file->limit >= file->st.st_size + (size_t) 0 + || file->limit >= params->limit) + { + found = true; + break; + } + } + } + if (!found) + { + _cpp_file *file2 = make_cpp_file (file->dir, file->name); + file2->path = xstrdup (file->path); + file2->next_file = file->next_file; + file2->embed = true; + file->next_file = file2; + file = file2; + } + } + if (!file->buffer_valid) + { + if (file->fd == -1 && !open_file (file)) + { + if (params->has_embed) + file->deferred_error = true; + else + open_file_failed (pfile, file, 0, params->loc); + return 0; + } + if (S_ISBLK (file->st.st_mode)) + { + if (params->has_embed) + { + close (file->fd); + file->fd = -1; + return 0; + } + cpp_error_at (pfile, CPP_DL_ERROR, params->loc, + "%s is a block device", file->path); + fail: + close (file->fd); + file->fd = -1; + file->dont_read = true; + return 0; + } + + if (CPP_OPTION (pfile, deps.style) + && !params->has_embed + && file == orig_file + && file->path[0]) + deps_add_dep (pfile->deps, file->path); + + bool regular = S_ISREG (file->st.st_mode) != 0; + ssize_t size, total, count; + uchar *buf; + if (regular) + { + cpp_num_part limit; + if (file->st.st_size + (cpp_num_part) 0 < params->limit) + limit = file->st.st_size; + else + limit = params->limit; + if (params->has_embed) + return limit != 0 ? 1 : 2; + if (limit > INTTYPE_MAXIMUM (ssize_t)) + { + cpp_error_at (pfile, CPP_DL_ERROR, params->loc, + "%s is too large", file->path); + goto fail; + } + file->limit = limit; + size = limit; + } + else if (params->has_embed) + return 2; + else if (params->limit > 8 * 1024) + size = 8 * 1024; + else + size = params->limit; + buf = XNEWVEC (uchar, size ? size : 1); + total = 0; + + while ((count = read (file->fd, buf + total, size - total)) > 0) + { + total += count; + if (total == size) + { + if (regular || size + (cpp_num_part) 0 == params->limit) + break; + size = (size_t) size * 2; + if (size < 0) + { + if (params->limit <= INTTYPE_MAXIMUM (ssize_t)) + size = params->limit; + else + { + cpp_error_at (pfile, CPP_DL_ERROR, params->loc, + "%s is too large", file->path); + free (buf); + goto fail; + } + } + else if (size + (cpp_num_part) 0 > params->limit) + size = params->limit; + buf = XRESIZEVEC (uchar, buf, size); + } + } + + if (count < 0) + { + cpp_errno_filename (pfile, CPP_DL_ERROR, file->path, params->loc); + free (buf); + goto fail; + } + + if (regular && total != size && STAT_SIZE_RELIABLE (file->st)) + { + cpp_error_at (pfile, CPP_DL_WARNING, params->loc, + "%s is shorter than expected", file->path); + file->limit = total; + } + else if (!regular) + file->limit = total; + + file->buffer_start = buf; + file->buffer = buf; + file->buffer_valid = 1; + close (file->fd); + file->fd = -1; + } + else if (params->has_embed) + return file->limit && params->limit ? 1 : 2; + + size_t limit = file->limit; + if (params->limit < limit) + limit = params->limit; + + /* For sizes larger than say 64 bytes, this is just a temporary + solution, we should emit a single new token which the FEs will + handle as an optimization. */ + size_t max = INTTYPE_MAXIMUM (size_t) / sizeof (cpp_token); + if (limit > max / 2 + || (limit + ? (params->prefix.count > max + || params->suffix.count > max + || (limit * 2 + params->prefix.count + + params->suffix.count > max)) + : params->if_empty.count > max)) + { + cpp_error_at (pfile, CPP_DL_ERROR, params->loc, + "%s is too large", file->path); + return 0; + } + + size_t len = 0; + for (size_t i = 0; i < limit; ++i) + { + if (file->buffer[i] < 10) + len += 2; + else if (file->buffer[i] < 100) + len += 3; +#if UCHAR_MAX == 255 + else + len += 4; +#else + else if (file->buffer[i] < 1000) + len += 4; + else + { + char buf[64]; + len += sprintf (buf, "%d", file->buffer[i]) + 1; + } +#endif + if (len > INTTYPE_MAXIMUM (ssize_t)) + { + cpp_error_at (pfile, CPP_DL_ERROR, params->loc, + "%s is too large", file->path); + return 0; + } + } + uchar *s = len ? _cpp_unaligned_alloc (pfile, len) : NULL; + _cpp_buff *tok_buff = NULL; + cpp_token *toks = NULL, *tok = &pfile->directive_result; + size_t count = 0; + if (limit) + count = (params->prefix.count + limit * 2 - 1 + + params->suffix.count) - 1; + else if (params->if_empty.count) + count = params->if_empty.count - 1; + if (count) + { + tok_buff = _cpp_get_buff (pfile, count * sizeof (cpp_token)); + toks = (cpp_token *) tok_buff->base; + } + cpp_embed_params_tokens *prefix + = limit ? ¶ms->prefix : ¶ms->if_empty; + if (prefix->count) + { + *tok = *prefix->base_run.base; + tok = toks; + tokenrun *cur_run = &prefix->base_run; + while (cur_run) + { + size_t cnt = (cur_run->next ? cur_run->limit + : prefix->cur_token) - cur_run->base; + cpp_token *t = cur_run->base; + if (cur_run == &prefix->base_run) + { + t++; + cnt--; + } + memcpy (tok, t, cnt * sizeof (cpp_token)); + tok += cnt; + cur_run = cur_run->next; + } + } + for (size_t i = 0; i < limit; ++i) + { + tok->src_loc = params->loc; + tok->type = CPP_NUMBER; + tok->flags = NO_EXPAND; + if (i == 0) + tok->flags |= PREV_WHITE; + tok->val.str.text = s; + tok->val.str.len = sprintf ((char *) s, "%d", file->buffer[i]); + s += tok->val.str.len + 1; + if (tok == &pfile->directive_result) + tok = toks; + else + tok++; + if (i < limit - 1) + { + tok->src_loc = params->loc; + tok->type = CPP_COMMA; + tok->flags = NO_EXPAND; + tok++; + } + } + if (limit && params->suffix.count) + { + tokenrun *cur_run = ¶ms->suffix.base_run; + cpp_token *orig_tok = tok; + while (cur_run) + { + size_t cnt = (cur_run->next ? cur_run->limit + : params->suffix.cur_token) - cur_run->base; + cpp_token *t = cur_run->base; + memcpy (tok, t, cnt * sizeof (cpp_token)); + tok += cnt; + cur_run = cur_run->next; + } + orig_tok->flags |= PREV_WHITE; + } + pfile->directive_result.flags |= PREV_WHITE; + if (count) + { + _cpp_push_token_context (pfile, NULL, toks, count); + pfile->context->buff = tok_buff; + } + return limit ? 1 : 2; +} + /* Retrofit the just-entered main file asif it was an include. This will permit correct include_next use, and mark it as a system header if that's where it resides. We use filesystem-appropriate @@ -1256,9 +1592,11 @@ open_file_failed (cpp_reader *pfile, _cpp_file *file, int angle_brackets, /* Search in the chain beginning at HEAD for a file whose search path started at START_DIR != NULL. */ static struct cpp_file_hash_entry * -search_cache (struct cpp_file_hash_entry *head, const cpp_dir *start_dir) +search_cache (struct cpp_file_hash_entry *head, const cpp_dir *start_dir, + bool is_embed) { - while (head && head->start_dir != start_dir) + while (head && (head->start_dir != start_dir + || head->u.file->embed != is_embed)) head = head->next; return head; @@ -1692,21 +2030,24 @@ _cpp_get_file_name (_cpp_file *file) struct stat * _cpp_get_file_stat (_cpp_file *file) { - return &file->st; + return &file->st; } /* Set the include chain for "" to QUOTE, for <> to BRACKET. If QUOTE_IGNORES_SOURCE_DIR, then "" includes do not look in the directory of the including file. - If BRACKET does not lie in the QUOTE chain, it is set to QUOTE. */ + If BRACKET does not lie in the QUOTE chain, it is set to QUOTE. + + EMBED is include chain for #embed <>. */ void cpp_set_include_chains (cpp_reader *pfile, cpp_dir *quote, cpp_dir *bracket, - int quote_ignores_source_dir) + cpp_dir *embed, int quote_ignores_source_dir) { pfile->quote_include = quote; pfile->bracket_include = quote; pfile->quote_ignores_source_dir = quote_ignores_source_dir; + pfile->embed_include = embed; for (; quote; quote = quote->next) { @@ -1715,6 +2056,11 @@ cpp_set_include_chains (cpp_reader *pfile, cpp_dir *quote, cpp_dir *bracket, if (quote == bracket) pfile->bracket_include = bracket; } + for (; embed; embed = embed->next) + { + embed->name_map = NULL; + embed->len = strlen (embed->name); + } } /* Append the file name to the directory to create the path, but don't |