diff options
Diffstat (limited to 'js/src/util/CompleteFile.cpp')
-rw-r--r-- | js/src/util/CompleteFile.cpp | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/js/src/util/CompleteFile.cpp b/js/src/util/CompleteFile.cpp new file mode 100644 index 0000000000..dae397f52b --- /dev/null +++ b/js/src/util/CompleteFile.cpp @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "util/CompleteFile.h" + +#include <cstring> // std::strcmp +#include <stdio.h> // FILE, fileno, fopen, getc, getc_unlocked, _getc_nolock +#include <sys/stat.h> // stat, fstat + +#ifdef __wasi__ +# include "js/Vector.h" +#endif // __wasi__ + +#include "js/CharacterEncoding.h" // EncodeUtf8ToWide, EncodeUtf8ToNarrow +#include "js/ErrorReport.h" // JS_ReportErrorNumberUTF8 +#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_CANT_OPEN + +bool js::ReadCompleteFile(JSContext* cx, FILE* fp, FileContents& buffer) { + /* Get the complete length of the file, if possible. */ + struct stat st; + int ok = fstat(fileno(fp), &st); + if (ok != 0) { + // Use the Latin1 variant here (and below), because the encoding of + // strerror() is platform-dependent. + JS_ReportErrorLatin1(cx, "error reading file: %s", strerror(errno)); + errno = 0; + return false; + } + if ((st.st_mode & S_IFDIR) != 0) { + JS_ReportErrorLatin1(cx, "error reading file: %s", strerror(EISDIR)); + return false; + } + + if (st.st_size > 0) { + if (!buffer.reserve(st.st_size)) { + return false; + } + } + + /* Use the fastest available getc. */ + auto fast_getc = +#if defined(HAVE_GETC_UNLOCKED) + getc_unlocked +#elif defined(HAVE__GETC_NOLOCK) + _getc_nolock +#else + getc +#endif + ; + + // Read in the whole file. Note that we can't assume the data's length + // is actually st.st_size, because 1) some files lie about their size + // (/dev/zero and /dev/random), and 2) reading files in text mode on + // Windows collapses "\r\n" pairs to single \n characters. + for (;;) { + int c = fast_getc(fp); + if (c == EOF) { + break; + } + if (!buffer.append(c)) { + return false; + } + } + + if (ferror(fp)) { + // getc failed + JS_ReportErrorLatin1(cx, "error reading file: %s", strerror(errno)); + errno = 0; + return false; + } + + return true; +} + +#ifdef __wasi__ +static bool NormalizeWASIPath(const char* filename, + js::Vector<char>* normalized, JSContext* cx) { + // On WASI, we need to collapse ".." path components for the capabilities + // that we pass to our unit tests to be reasonable; otherwise we need to + // grant "tests/script1.js/../lib.js" and "tests/script2.js/../lib.js" + // separately (because the check appears to be a prefix only). + for (const char* cur = filename; *cur; ++cur) { + if (std::strncmp(cur, "/../", 4) == 0) { + do { + if (normalized->empty()) { + JS_ReportErrorASCII(cx, "Path processing error"); + return false; + } + } while (normalized->popCopy() != '/'); + cur += 2; + continue; + } + if (!normalized->append(*cur)) { + return false; + } + } + if (!normalized->append('\0')) { + return false; + } + return true; +} +#endif + +static FILE* OpenFile(JSContext* cx, const char* filename) { +#ifdef XP_WIN + JS::UniqueWideChars wideFilename = JS::EncodeUtf8ToWide(cx, filename); + if (!wideFilename) { + return nullptr; + } + return _wfopen(wideFilename.get(), L"r"); +#else + JS::UniqueChars narrowFilename = JS::EncodeUtf8ToNarrow(cx, filename); + if (!narrowFilename) { + return nullptr; + } + return fopen(narrowFilename.get(), "r"); +#endif +} + +/* + * Open a source file for reading. Supports "-" and nullptr to mean stdin. The + * return value must be fclosed unless it is stdin. + */ +bool js::AutoFile::open(JSContext* cx, const char* filename) { + if (!filename || std::strcmp(filename, "-") == 0) { + fp_ = stdin; + } else { +#ifdef __wasi__ + js::Vector<char> normalized(cx); + if (!NormalizeWASIPath(filename, &normalized, cx)) { + return false; + } + fp_ = OpenFile(cx, normalized.begin()); +#else + fp_ = OpenFile(cx, filename); +#endif + if (!fp_) { + JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_CANT_OPEN, + filename, "No such file or directory"); + return false; + } + } + return true; +} |