diff --git a/src/library_path.js b/src/library_path.js index 5daa4e646198f..21c2e12982525 100644 --- a/src/library_path.js +++ b/src/library_path.js @@ -6,7 +6,14 @@ mergeInto(LibraryManager.library, { $PATH: { - isAbs: (path) => path.charAt(0) === '/', + isAbs: (path) => { +#if ENVIRONMENT_MAY_BE_NODE + if (typeof ENVIRONMENT_IS_NODE != 'undefined' && ENVIRONMENT_IS_NODE && typeof nodePath !== 'undefined' && nodePath.isAbsolute !== undefined) { + return nodePath.isAbsolute(path); + } +#endif + return path.charAt(0) === '/'; + }, // split a filename into [root, dir, basename, ext], unix version // 'root' is just a slash, or nothing. splitPath: (filename) => { @@ -37,6 +44,14 @@ mergeInto(LibraryManager.library, { return parts; }, normalize: (path) => { +#if ENVIRONMENT_MAY_BE_NODE + if (typeof ENVIRONMENT_IS_NODE != 'undefined' && ENVIRONMENT_IS_NODE && typeof nodePath !== 'undefined' && nodePath.normalize !== undefined) { + const normalized = nodePath.normalize(path); + if (normalized) { + return normalized.replace(/\\/g, '/'); + } + } +#endif var isAbsolute = PATH.isAbs(path), trailingSlash = path.substr(-1) === '/'; // Normalize the path diff --git a/test/common.py b/test/common.py index b4155c37ed334..9d516963ff20e 100644 --- a/test/common.py +++ b/test/common.py @@ -32,7 +32,7 @@ from tools.shared import TEMP_DIR, EMCC, EMXX, DEBUG, EMCONFIGURE, EMCMAKE from tools.shared import EMSCRIPTEN_TEMP_DIR from tools.shared import get_canonical_temp_dir, path_from_root -from tools.utils import MACOS, WINDOWS, read_file, read_binary, write_file, write_binary, exit_with_error +from tools.utils import MACOS, WINDOWS, LINUX, read_file, read_binary, write_file, write_binary, exit_with_error from tools import shared, line_endings, building, config, utils logger = logging.getLogger('common') @@ -154,6 +154,13 @@ def no_mac(note=''): return lambda f: f +def no_linux(note=''): + assert not callable(note) + if LINUX: + return unittest.skip(note) + return lambda f: f + + def no_windows(note=''): assert not callable(note) if WINDOWS: diff --git a/test/dirent/test_readdir_windows.c b/test/dirent/test_readdir_windows.c new file mode 100644 index 0000000000000..180981ab7a9d0 --- /dev/null +++ b/test/dirent/test_readdir_windows.c @@ -0,0 +1,218 @@ +/* + * Copyright 2013 The Emscripten Authors. All rights reserved. + * Emscripten is available under two separate licenses, the MIT license and the + * University of Illinois/NCSA Open Source License. Both these licenses can be + * found in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void cleanup(); + +#define ASSERT_WITH_CLEANUP(cond) if (!(cond)) { cleanup(); assert(cond); } + +#define CHECK(cond) if (!(cond)) { printf("errno: %s\n", strerror(errno)); ASSERT_WITH_CLEANUP(cond); } + +static void create_file(const char *path, const char *buffer, int mode) { + int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode); + CHECK(fd >= 0); + + int err = write(fd, buffer, sizeof(char) * strlen(buffer)); + ASSERT_WITH_CLEANUP(err == (sizeof(char) * strlen(buffer))); + + close(fd); +} + +char windir[PATH_MAX] = {0}; +char tempdir[PATH_MAX] = {0}; +char testtmp[PATH_MAX] = {0}; + +void setup() { + sprintf(testtmp, "%s\\testtmp", tempdir); + int err; + err = mkdir(testtmp, 0777); // can't call it tmp, that already exists + CHECK(!err); + chdir(testtmp); + err = mkdir("nocanread", 0111); + CHECK(!err); + err = mkdir("foobar", 0777); + CHECK(!err); + create_file("foobar\\file.txt", "ride into the danger zone", 0666); +} + +void cleanup() { + rmdir("nocanread"); + unlink("foobar\\file.txt"); + rmdir("foobar"); + chdir(".."); + rmdir("testtmp"); +} + +void test() { + int err; + long loc, loc2; + DIR *dir; + struct dirent *ent; + struct dirent ent_r; + struct dirent *result; + int i; + + // check bad opendir input + dir = opendir("noexist"); + ASSERT_WITH_CLEANUP(!dir); + ASSERT_WITH_CLEANUP(errno == ENOENT); + char filepath[PATH_MAX] = {0}; + sprintf(filepath, "%s\\foobar\\file.txt", testtmp); + dir = opendir(filepath); + ASSERT_WITH_CLEANUP(!dir); + + // + // do a normal read with readdir + // + char dirpath[PATH_MAX] = {0}; + sprintf(dirpath, "%s\\foobar", testtmp); + dir = opendir(dirpath); + ASSERT_WITH_CLEANUP(dir); + int seen[3] = { 0, 0, 0 }; + for (i = 0; i < 3; i++) { + errno = 0; + ent = readdir(dir); + if (ent) { + fprintf(stderr, "%d file: %s (%d : %lu)\n", i, ent->d_name, ent->d_reclen, sizeof(*ent)); + } else { + fprintf(stderr, "ent: %p, errno: %d\n", ent, errno); + ASSERT_WITH_CLEANUP(ent); + } + ASSERT_WITH_CLEANUP(ent->d_reclen == sizeof(*ent)); + if (!seen[0] && !strcmp(ent->d_name, ".")) { + ASSERT_WITH_CLEANUP(ent->d_type & DT_DIR); + seen[0] = 1; + continue; + } + if (!seen[1] && !strcmp(ent->d_name, "..")) { + ASSERT_WITH_CLEANUP(ent->d_type & DT_DIR); + seen[1] = 1; + continue; + } + if (!seen[2] && !strcmp(ent->d_name, "file.txt")) { + ASSERT_WITH_CLEANUP(ent->d_type & DT_REG); + seen[2] = 1; + continue; + } + ASSERT_WITH_CLEANUP(0 && "odd filename"); + } + ent = readdir(dir); + if (ent) printf("surprising ent: %p : %s\n", ent, ent->d_name); + ASSERT_WITH_CLEANUP(!ent); + + // test rewinddir + rewinddir(dir); + ent = readdir(dir); + ASSERT_WITH_CLEANUP(ent && ent->d_ino); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "file.txt")); + + // test seek / tell + rewinddir(dir); + ent = readdir(dir); + ASSERT_WITH_CLEANUP(ent && ent->d_ino); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "file.txt")); + loc = telldir(dir); + ASSERT_WITH_CLEANUP(loc >= 0); + //printf("loc=%d\n", loc); + loc2 = ent->d_off; + ent = readdir(dir); + ASSERT_WITH_CLEANUP(ent && ent->d_ino); + char name_at_loc[1024]; + strcpy(name_at_loc, ent->d_name); + //printf("name_at_loc: %s\n", name_at_loc); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "file.txt")); + ent = readdir(dir); + ASSERT_WITH_CLEANUP(ent && ent->d_ino); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "file.txt")); + seekdir(dir, loc); + ent = readdir(dir); + ASSERT_WITH_CLEANUP(ent && ent->d_ino); + //printf("check: %s / %s\n", ent->d_name, name_at_loc); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, name_at_loc)); + + seekdir(dir, loc2); + ent = readdir(dir); + ASSERT_WITH_CLEANUP(ent && ent->d_ino); + //printf("check: %s / %s\n", ent->d_name, name_at_loc); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, name_at_loc)); + + // + // do a normal read with readdir_r + // + rewinddir(dir); + err = readdir_r(dir, &ent_r, &result); + ASSERT_WITH_CLEANUP(!err); + ASSERT_WITH_CLEANUP(&ent_r == result); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "file.txt")); + err = readdir_r(dir, &ent_r, &result); + ASSERT_WITH_CLEANUP(!err); + ASSERT_WITH_CLEANUP(&ent_r == result); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "file.txt")); + err = readdir_r(dir, &ent_r, &result); + ASSERT_WITH_CLEANUP(!err); + ASSERT_WITH_CLEANUP(&ent_r == result); + ASSERT_WITH_CLEANUP(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "file.txt")); + err = readdir_r(dir, &ent_r, &result); + ASSERT_WITH_CLEANUP(!err); + ASSERT_WITH_CLEANUP(!result); + + err = closedir(dir); + ASSERT_WITH_CLEANUP(!err); + + // Read the windir and verify + dir = opendir(windir); + ASSERT_WITH_CLEANUP(dir); + err = closedir(dir); + ASSERT_WITH_CLEANUP(!err); + + puts("success"); +} + +void test_scandir() { + struct dirent **namelist; + int n; + + n = scandir(".", &namelist, NULL, alphasort); + printf("n: %d\n", n); + if (n < 0) + return; + else { + while (n--) { + printf("name: %s\n", namelist[n]->d_name); + free(namelist[n]); + } + free(namelist); + } +} + +int main(int argc, char * argv[]) { + if (argc < 3) { + printf("test_readdir_windows args: %%WINDIR%% %%TEMP%%\n"); + return -1; + } + strcpy(windir, argv[1]); + strcpy(tempdir, argv[2]); + // atexit(cleanup); Doesn't work on windows + signal(SIGABRT, cleanup); + setup(); + test(); + test_scandir(); + cleanup(); + + return EXIT_SUCCESS; +} diff --git a/test/dirent/test_readdir_windows.out b/test/dirent/test_readdir_windows.out new file mode 100644 index 0000000000000..a7dd1b3e5933d --- /dev/null +++ b/test/dirent/test_readdir_windows.out @@ -0,0 +1,9 @@ +0 file: . (280 : 280) +1 file: .. (280 : 280) +2 file: file.txt (280 : 280) +success +n: 4 +name: nocanread +name: foobar +name: .. +name: . diff --git a/test/test_core.py b/test/test_core.py index 8e34ba996d674..0f0ace25dbfde 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -25,7 +25,7 @@ from tools import shared, building, config, webassembly import common from common import RunnerCore, path_from_root, requires_native_clang, test_file, create_file -from common import skip_if, needs_dylink, no_windows, no_mac, is_slow_test, parameterized +from common import skip_if, needs_dylink, no_windows, no_mac, no_linux, is_slow_test, parameterized from common import env_modify, with_env_modify, disabled, node_pthreads, also_with_wasm_bigint from common import read_file, read_binary, requires_v8, requires_node from common import NON_ZERO, WEBIDL_BINDER, EMBUILDER, PYTHON @@ -5655,6 +5655,14 @@ def test_fileno(self): def test_readdir(self): self.do_run_in_out_file_test('dirent/test_readdir.c') + @no_mac("uses windows only paths for testing") + @no_linux("uses windows only paths for testing") + @also_with_noderawfs + def test_readdir_windows(self): + self.require_node() + self.emcc_args += ['-sNODERAWFS'] + self.do_run_in_out_file_test('dirent/test_readdir_windows.c', args=[os.environ["WINDIR"], os.environ["TEMP"]]) + @also_with_wasm_bigint def test_readdir_empty(self): self.do_run_in_out_file_test('dirent/test_readdir_empty.c')