| /* | |
| * Copyright 2015 WebAssembly Community Group participants | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| #include "support/file.h" | |
| #include "support/debug.h" | |
| #include "support/path.h" | |
| #include "support/utilities.h" | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <iostream> | |
| #include <limits> | |
| #define DEBUG_TYPE "file" | |
| std::vector<char> wasm::read_stdin() { | |
| BYN_TRACE("Loading stdin...\n"); | |
| std::vector<char> input; | |
| char c; | |
| while (std::cin.get(c) && !std::cin.eof()) { | |
| input.push_back(c); | |
| } | |
| return input; | |
| } | |
| template<typename T> struct do_read_stdin { | |
| T operator()(); | |
| }; | |
| template<> std::vector<char> do_read_stdin<std::vector<char>>::operator()() { | |
| return wasm::read_stdin(); | |
| } | |
| template<> std::string do_read_stdin<std::string>::operator()() { | |
| auto vec = wasm::read_stdin(); | |
| return std::string(vec.begin(), vec.end()); | |
| } | |
| template<typename T> | |
| T wasm::read_file(const std::string& filename, Flags::BinaryOption binary) { | |
| if (filename == "-") { | |
| return do_read_stdin<T>{}(); | |
| } | |
| BYN_TRACE("Loading '" << filename << "'...\n"); | |
| std::ifstream infile; | |
| std::ios_base::openmode flags = std::ifstream::in; | |
| if (binary == Flags::Binary) { | |
| flags |= std::ifstream::binary; | |
| } | |
| infile.open(wasm::Path::to_path(filename), flags); | |
| if (!infile.is_open()) { | |
| Fatal() << "Failed opening '" << filename << "'"; | |
| } | |
| infile.seekg(0, std::ios::end); | |
| std::streampos insize = infile.tellg(); | |
| if (uint64_t(insize) >= std::numeric_limits<size_t>::max()) { | |
| // Building a 32-bit executable where size_t == 32 bits, we are not able to | |
| // create strings larger than 2^32 bytes in length, so must abort here. | |
| Fatal() << "Failed opening '" << filename | |
| << "': Input file too large: " << insize | |
| << " bytes. Try rebuilding in 64-bit mode."; | |
| } | |
| // Zero-initialize the string or vector with the expected size. | |
| T input(size_t(insize), '\0'); | |
| if (size_t(insize) == 0) { | |
| return input; | |
| } | |
| infile.seekg(0); | |
| infile.read(&input[0], insize); | |
| if (binary == Flags::Text) { | |
| size_t chars = size_t(infile.gcount()); | |
| // Truncate size to the number of ASCII characters actually read in text | |
| // mode (which is generally less than the number of bytes on Windows, if | |
| // \r\n line endings are present) | |
| input.resize(chars); | |
| } | |
| return input; | |
| } | |
| std::string wasm::read_possible_response_file(const std::string& input) { | |
| if (input.size() == 0 || input[0] != '@') { | |
| return input; | |
| } | |
| return wasm::read_file<std::string>(input.substr(1), Flags::Text); | |
| } | |
| // Explicit instantiations for the explicit specializations. | |
| template std::string wasm::read_file<>(const std::string&, Flags::BinaryOption); | |
| template std::vector<char> wasm::read_file<>(const std::string&, | |
| Flags::BinaryOption); | |
| wasm::Output::Output(const std::string& filename, Flags::BinaryOption binary) | |
| : outfile(), out([this, filename, binary]() { | |
| // Ensure a single return at the very end, to avoid clang-tidy warnings | |
| // about the types of different returns here. | |
| std::streambuf* buffer; | |
| if (filename == "-" || filename.empty()) { | |
| buffer = std::cout.rdbuf(); | |
| } else { | |
| BYN_TRACE("Opening '" << filename << "'\n"); | |
| std::ios_base::openmode flags = | |
| std::ofstream::out | std::ofstream::trunc; | |
| if (binary == Flags::Binary) { | |
| flags |= std::ofstream::binary; | |
| } | |
| outfile.open(wasm::Path::to_path(filename), flags); | |
| if (!outfile.is_open()) { | |
| Fatal() << "Failed opening output file '" << filename | |
| << "': " << strerror(errno); | |
| } | |
| buffer = outfile.rdbuf(); | |
| } | |
| return buffer; | |
| }()) {} | |
| void wasm::copy_file(std::string input, std::string output) { | |
| std::ifstream src(wasm::Path::to_path(input), std::ios::binary); | |
| std::ofstream dst(wasm::Path::to_path(output), std::ios::binary); | |
| dst << src.rdbuf(); | |
| } | |
| size_t wasm::file_size(std::string filename) { | |
| std::ifstream infile(wasm::Path::to_path(filename), | |
| std::ifstream::ate | std::ifstream::binary); | |
| return infile.tellg(); | |
| } | |
| void wasm::flush_and_quick_exit(int code) { | |
| // We expect C++ files to be flushed by their destructors already. Flush the | |
| // standard streams manually. | |
| std::cout << std::flush; | |
| std::cerr << std::flush; | |
| // To be safe, also flush at the C level. | |
| fflush(NULL); | |
| #if __has_feature(address_sanitizer) || __has_feature(thread_sanitizer) || \ | |
| __has_feature(memory_sanitizer) || __has_feature(leak_sanitizer) || \ | |
| __has_feature(undefined_behavior_sanitizer) | |
| // Avoid quick_exit when using sanitizers, so that leak checks and other | |
| // things can run during shutdown normally. | |
| std::exit(code); | |
| #else | |
| // A "better" function to use here would be std::quick_exit, however on older | |
| // version of macOS (at least as new as 13.2 which currently runs on our CI) | |
| // the C symbol _quick_exit (which is called by std::quick_exit) is missing. | |
| // So instead use _Exit(); the only difference is that _Exit() does not call | |
| // handlers registered by at_quick_exit(). Currently Binaryen does not have | |
| // any of those. | |
| _Exit(code); | |
| #endif | |
| } |