summaryrefslogtreecommitdiffstats
path: root/js/src/util/CompleteFile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/util/CompleteFile.cpp')
-rw-r--r--js/src/util/CompleteFile.cpp147
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;
+}