wip [ci skip]
diff --git a/emcc.py b/emcc.py
index 7b656f9..690b14b 100755
--- a/emcc.py
+++ b/emcc.py
@@ -865,7 +865,7 @@
     # configure tests want a more shell-like style, where we emit return codes on exit()
     cmd += ['-s', 'EXIT_RUNTIME=1']
     # use node.js raw filesystem access, to behave just like a native executable
-    cmd += ['-s', 'NODERAWFS=1']
+    cmd += ['-s', 'RAW_OS=1']
 
     logger.debug('just configuring: ' + ' '.join(cmd))
     if debug_configure:
@@ -1570,8 +1570,8 @@
       shared.Settings.PROXY_TO_WORKER = 1
 
     if options.use_preload_plugins or len(options.preload_files) or len(options.embed_files):
-      if shared.Settings.NODERAWFS:
-        exit_with_error('--preload-file and --embed-file cannot be used with NODERAWFS which disables virtual filesystem')
+      if shared.Settings.RAW_OS:
+        exit_with_error('--preload-file and --embed-file cannot be used with RAW_OS which disables virtual filesystem')
       # if we include any files, or intend to use preload plugins, then we definitely need filesystem support
       shared.Settings.FORCE_FILESYSTEM = 1
 
diff --git a/src/modules.js b/src/modules.js
index 9f1c796..dc83488 100644
--- a/src/modules.js
+++ b/src/modules.js
@@ -106,8 +106,8 @@
         'library_sockfs.js', // ok to include it by default since it's only used if the syscall is used
       ]);
 
-      if (NODERAWFS) {
-        // NODERAWFS requires NODEFS
+      if (RAW_OS) {
+        // Enable NODERAWFS, which also requires NODEFS
         if (SYSTEM_JS_LIBRARIES.indexOf('library_nodefs.js') < 0) {
           libraries.push('library_nodefs.js');
         }
diff --git a/src/settings.js b/src/settings.js
index dd6a629..5a0bb7d 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -824,8 +824,11 @@
 // some JS that does, you might need this.
 var FORCE_FILESYSTEM = 0;
 
-// Enables support for the NODERAWFS filesystem backend. This is a special
-// backend as it replaces all normal filesystem access with direct Node.js
+// Enables direct OS access. This matters in a *non*-Web environment (as on
+// the Web, OS access is always indirect), where it allows directly accessing
+// local files and so forth.
+// Specifically, on Node.js this enables the NODERAWFS filesystem, a special
+// backend that replaces all normal filesystem access with direct Node.js
 // operations, without the need to do `FS.mount()`, and this backend only
 // works with Node.js. The initial working directory will be same as
 // process.cwd() instead of VFS root directory.  Because this mode directly uses
@@ -834,7 +837,11 @@
 // program would be, which means that differences in how the underlying OS
 // handles permissions and errors and so forth may be noticeable.  This has
 // mostly been tested on Linux so far.
-var NODERAWFS = 0;
+// With wasm2c this also enables direct file access, allowing an executable
+// built from the C code to behave like a normal C program would. As with
+// NODERAWFS, this will be as portable as a C program would be in general -
+// underlying OS differences may be noticeable.
+var RAW_OS = 0;
 
 // This saves the compiled wasm module in a file with name
 //   $WASM_BINARY_NAME.$V8_VERSION.cached
@@ -1814,4 +1821,5 @@
   ['BINARYEN_MEM_MAX', 'MAXIMUM_MEMORY'],
   ['BINARYEN_PASSES', [''], 'Use BINARYEN_EXTRA_PASSES to add additional passes'],
   ['SWAPPABLE_ASM_MODULE', [0], 'Fully swappable asm modules are no longer supported'],
+  ['NODERAWFS', 'RAW_OS'],
 ];
diff --git a/tests/test_core.py b/tests/test_core.py
index edf19cc..7e34b6b 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -133,7 +133,7 @@
     orig_args = self.emcc_args[:]
     func(self)
     print('noderawfs')
-    self.emcc_args = orig_args + ['-s', 'NODERAWFS=1', '-DNODERAWFS']
+    self.emcc_args = orig_args + ['-s', 'RAW_OS', '-DNODERAWFS']
     with js_engines_modify([NODE_JS]):
       func(self)
   return decorated
@@ -5391,7 +5391,7 @@
     # Node.js fs.chmod is nearly no-op on Windows
     if not WINDOWS:
       self.emcc_args = orig_compiler_opts
-      self.emcc_args += ['-s', 'NODERAWFS=1']
+      self.emcc_args += ['-s', 'RAW_OS']
       self.do_run_in_out_file_test('tests', 'unistd', 'access', js_engines=[NODE_JS])
 
   def test_unistd_curdir(self):
@@ -5447,7 +5447,7 @@
     # FIXME
     self.skipTest('fails on some node versions and OSes, e.g. 10.13.0 on linux')
 
-    self.emcc_args += ['-s', 'NODERAWFS=1']
+    self.emcc_args += ['-s', 'RAW_OS']
     self.do_run_in_out_file_test('tests', 'unistd', 'truncate', js_engines=[NODE_JS])
 
   def test_unistd_swab(self):
@@ -5495,7 +5495,7 @@
       # 0 if root user
       if os.geteuid() == 0:
         self.emcc_args += ['-DSKIP_ACCESS_TESTS']
-      self.emcc_args += ['-s', 'NODERAWFS=1']
+      self.emcc_args += ['-s', 'RAW_OS']
       self.do_run(src, 'success', force_c=True, js_engines=[NODE_JS])
 
   def test_unistd_links(self):
diff --git a/tests/test_other.py b/tests/test_other.py
index 2f321ed..8abac23 100644
--- a/tests/test_other.py
+++ b/tests/test_other.py
@@ -8484,18 +8484,18 @@
     # replaced subprocess functions should not cause errors
     run_process([EMCC, path_from_root('tests', 'hello_world.c')], env=environ)
 
-  def test_noderawfs(self):
+  def test_RAW_OS(self):
     fopen_write = open(path_from_root('tests', 'asmfs', 'fopen_write.cpp')).read()
     create_test_file('main.cpp', fopen_write)
-    run_process([EMCC, 'main.cpp', '-s', 'NODERAWFS=1'])
+    run_process([EMCC, 'main.cpp', '-s', 'RAW_OS=1'])
     self.assertContained("read 11 bytes. Result: Hello data!", run_js('a.out.js'))
 
-    # NODERAWFS should directly write on OS file system
+    # RAW_OS should directly write on OS file system
     self.assertEqual("Hello data!", open('hello_file.txt').read())
 
-  def test_noderawfs_disables_embedding(self):
-    expected = '--preload-file and --embed-file cannot be used with NODERAWFS which disables virtual filesystem'
-    base = [EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'NODERAWFS=1']
+  def test_RAW_OS_disables_embedding(self):
+    expected = '--preload-file and --embed-file cannot be used with RAW_OS which disables virtual filesystem'
+    base = [EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'RAW_OS']
     err = self.expect_fail(base + ['--preload-file', 'somefile'])
     self.assertContained(expected, err)
     err = self.expect_fail(base + ['--embed-file', 'somefile'])
@@ -10270,6 +10270,29 @@
     with open(path_from_root('tests', 'other', 'wasm2c', 'output.txt')) as f:
       self.assertEqual(output, f.read())
 
+  @no_windows('TODO: fix setjmp.h on clang on windows on ci')
+  def test_wasm2c_RAW_OS(self):
+    create_test_file('data.txt', 'hello')
+    create_test_file('main.cpp', r'''
+      #include <stdio.h>
+      int main() {
+        FILE *f = fopen("data.dat", "r");
+        if (!f) {
+          puts("failed to open file");
+          return 1;
+        }
+        char buf[6];
+        fread(buf, 1, 5, f);
+        buf[5] = 0;
+        fclose(f);
+        printf("read |%s|\n", buf);
+      }
+    ''')
+    run_process([EMCC, 'main.cpp', '-s', 'WASM2C', '-o', 'main.wasm'])
+    run_process([CLANG_CC, 'main.wasm.c', '-o', 'program.exe'])
+    output = run_process([os.path.abspath('program.exe')], stdout=PIPE).stdout
+    self.assertContained('|hello|', output)
+
   @parameterized({
     'wasm2js': (['-s', 'WASM=0'], ''),
     'modularize': (['-s', 'MODULARIZE'], 'Module()'),
diff --git a/tools/wasm2c.py b/tools/wasm2c.py
index 5b0a3c6..761730a 100644
--- a/tools/wasm2c.py
+++ b/tools/wasm2c.py
@@ -49,13 +49,13 @@
   support_files = ['base']
   if Settings.AUTODEBUG:
     support_files.append('autodebug')
-  if Settings.EXPECT_MAIN:
-    # TODO: add an option for direct OS access. For now, do that when building
-    #       an executable with main, as opposed to a library
+  if Settings.RAW_OS:
     support_files.append('os')
-    support_files.append('main')
   else:
     support_files.append('os_sandboxed')
+  if Settings.EXPECT_MAIN:
+    support_files.append('main')
+  else:
     support_files.append('reactor')
     # for a reactor, also append wasmbox_* API definitions
     with open(h_file, 'a') as f:
diff --git a/tools/wasm2c/os_sandboxed.c b/tools/wasm2c/os_sandboxed.c
index 728d713..0edb708 100644
--- a/tools/wasm2c/os_sandboxed.c
+++ b/tools/wasm2c/os_sandboxed.c
@@ -20,3 +20,6 @@
 STUB_IMPORT_IMPL(u32, Z_envZ___sys_accessZ_iii, (u32 pathname, u32 mode), EM_EACCES);
 STUB_IMPORT_IMPL(u32, Z_wasi_snapshot_preview1Z_clock_time_getZ_iiji, (u32 clock_id, u64 max_lag, u32 out), WASI_EINVAL);
 STUB_IMPORT_IMPL(u32, Z_wasi_snapshot_preview1Z_clock_res_getZ_iii, (u32 clock_id, u32 out), WASI_EINVAL);
+
+// called by main() if main() exists
+static void init_fds() {}