diff --git a/cmake/Modules/Platform/Emscripten.cmake b/cmake/Modules/Platform/Emscripten.cmake index d2c413176f2e3..01b75b23ce392 100644 --- a/cmake/Modules/Platform/Emscripten.cmake +++ b/cmake/Modules/Platform/Emscripten.cmake @@ -17,6 +17,10 @@ set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_CROSSCOMPILING TRUE) +# Advertise Emscripten as a 32-bit platform (as opposed to CMAKE_SYSTEM_PROCESSOR=x86_64 for 64-bit platform), +# since some projects (e.g. OpenCV) use this to detect bitness. +set(CMAKE_SYSTEM_PROCESSOR x86) + # Tell CMake how it should instruct the compiler to generate multiple versions of an outputted .so library: e.g. "libfoo.so, libfoo.so.1, libfoo.so.1.4" etc. # This feature is activated if a shared library project has the property SOVERSION defined. set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-Wl,-soname,") diff --git a/embuilder.py b/embuilder.py index 0a4557e61245b..8e7280bbc9e4d 100755 --- a/embuilder.py +++ b/embuilder.py @@ -35,10 +35,14 @@ gl struct_info native_optimizer - zlib + bullet + freetype libpng + ogg sdl2 sdl2-image + vorbis + zlib It is also possible to build native_optimizer manually by using CMake. To do that, run @@ -63,6 +67,12 @@ def build(src, result_libs, args=[]): for lib in result_libs: assert os.path.exists(shared.Cache.get_path(lib)), 'not seeing that requested library %s has been built' % lib +def build_port(port_name, lib_name, params): + build(''' + int main() {} + ''', [os.path.join('ports-builds', port_name, lib_name)], params) + + operation = sys.argv[1] if operation == 'build': @@ -126,21 +136,21 @@ def build(src, result_libs, args=[]): int main() {} ''', ['optimizer.exe'], ['-O2']) elif what == 'zlib': - build(''' - int main() {} - ''', [os.path.join('ports-builds', 'zlib', 'libz.a')], ['-s', 'USE_ZLIB=1']) + build_port('zlib', 'libz.a', ['-s', 'USE_ZLIB=1']) + elif what == 'bullet': + build_port('bullet', 'libbullet.bc', ['-s', 'USE_BULLET=1']) + elif what == 'vorbis': + build_port('vorbis', 'libvorbis.bc', ['-s', 'USE_VORBIS=1']) + elif what == 'ogg': + build_port('ogg', 'libogg.bc', ['-s', 'USE_OGG=1']) elif what == 'libpng': - build(''' - int main() {} - ''', [os.path.join('ports-builds', 'libpng', 'libpng.bc')], ['-s', 'USE_ZLIB=1', '-s', 'USE_LIBPNG=1']) + build_port('libpng', 'libpng.bc', ['-s', 'USE_ZLIB=1', '-s', 'USE_LIBPNG=1']) elif what == 'sdl2': - build(''' - int main() {} - ''', [os.path.join('ports-builds', 'sdl2', 'libsdl2.bc')], ['-s', 'USE_SDL=2']) + build_port('sdl2', 'libsdl2.bc', ['-s', 'USE_SDL=2']) elif what == 'sdl2-image': - build(''' - int main() {} - ''', [os.path.join('ports-builds', 'sdl2-image', 'libsdl2_image.bc')], ['-s', 'USE_SDL=2', '-s', 'USE_SDL_IMAGE=2']) + build_port('sdl2-image', 'libsdl2_image.bc', ['-s', 'USE_SDL=2', '-s', 'USE_SDL_IMAGE=2']) + elif what == 'freetype': + build_port('freetype', 'libfreetype.a', ['-s', 'USE_FREETYPE=1']) else: shared.logging.error('unfamiliar build target: ' + what) sys.exit(1) diff --git a/emcc b/emcc index 3dd564f6d0c71..1abf0057e0481 100755 --- a/emcc +++ b/emcc @@ -7,40 +7,16 @@ emcc - compiler helper script emcc is a drop-in replacement for a compiler like gcc or clang. -Tell your build system to use this instead of the compiler, and similarly -use emar, emranlib etc. instead of the same command without 'em'. - -Example uses: - - * For configure, instead of ./configure, cmake, etc., run emconfigure.py - with that command as an argument, for example - - emconfigure.py ./configure [options] - - emconfigure.py is a tiny script that just sets some environment vars - as a convenience. The command just shown is equivalent to - - EMMAKEN_JUST_CONFIGURE=1 RANLIB=PATH/emranlib AR=PATH/emar CXX=PATH/em++ CC=PATH/emcc ./configure [options] - - where PATH is the path to this file. - - EMMAKEN_JUST_CONFIGURE tells emcc that it is being run in ./configure, - so it should relay everything to gcc/g++. You should not define that when - running make, of course. - -After setting that up, run your build system normally. - -Note the appearance of em++ instead of emcc -for the C++ compiler. This is needed for cases where we get -a C++ file with a C extension, in which case CMake can be told -to run g++ on it despite the .c extension, see - - https://github.com/kripken/emscripten/issues/6 - -(If a similar situation occurs with ./configure, you can do the same there too.) +See emcc --help for details. emcc can be influenced by a few environment variables: + EMCC_DEBUG - "1" will log out useful information during compilation, as well as + save each compiler step as an emcc-* file in the temp dir + (by default /tmp/emscripten_temp). "2" will save additional emcc-* + steps, that would normally not be separately produced (so this + slows down compilation). + EMMAKEN_NO_SDK - Will tell emcc *not* to use the emscripten headers. Instead your system headers will be used. @@ -419,7 +395,7 @@ try: js_opts = None llvm_opts = None llvm_lto = None - closure = None + use_closure_compiler = None js_transform = None pre_js = '' post_js = '' @@ -517,7 +493,7 @@ try: newargs[i+1] = '' elif newargs[i].startswith('--closure'): check_bad_eq(newargs[i]) - closure = int(newargs[i+1]) + use_closure_compiler = int(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--js-transform'): @@ -840,7 +816,7 @@ try: break if found: break if found: break - if not found and lib not in ['GL', 'GLU', 'glut', 'm', 'SDL']: # whitelist our default libraries + if not found and lib not in ['GL', 'GLU', 'glut', 'm', 'SDL', 'stdc++']: # whitelist our default libraries logging.warning('emcc: cannot find library "%s"', lib) # If not compiling to JS, then we are compiling to an intermediate bitcode objects or library, so @@ -925,16 +901,16 @@ try: js_opts = True logging.debug('enabling js opts for SAFE_HEAP') - if debug_level > 1 and closure: + if debug_level > 1 and use_closure_compiler: logging.warning('disabling closure because debug info was requested') - closure = False + use_closure_compiler = False - assert not (shared.Settings.NO_DYNAMIC_EXECUTION and closure), 'cannot have both NO_DYNAMIC_EXECUTION and closure compiler enabled at the same time' + assert not (shared.Settings.NO_DYNAMIC_EXECUTION and use_closure_compiler), 'cannot have both NO_DYNAMIC_EXECUTION and closure compiler enabled at the same time' - if closure: - shared.Settings.CLOSURE_COMPILER = closure + if use_closure_compiler: + shared.Settings.USE_CLOSURE_COMPILER = use_closure_compiler assert os.path.exists(shared.CLOSURE_COMPILER), logging.error('fatal: Closure compiler (%s) does not exist', shared.CLOSURE_COMPILER) - if closure == 2 and shared.Settings.ASM_JS == 1: + if use_closure_compiler == 2 and shared.Settings.ASM_JS == 1: logging.warning('not all asm.js optimizations are possible with --closure 2, disabling those - your code will be run more slowly') shared.Settings.ASM_JS = 2 @@ -950,7 +926,7 @@ try: shared.Settings.LINKABLE = 1 # TODO: add FORCE_DCE option for the brave people that do want to dce here and in side modules shared.Settings.RELOCATABLE = 1 shared.Settings.PRECISE_I64_MATH = 1 # other might use precise math, we need to be able to print it - assert not closure, 'cannot use closure compiler on shared modules' + assert not use_closure_compiler, 'cannot use closure compiler on shared modules' if shared.Settings.EMULATE_FUNCTION_POINTER_CASTS: shared.Settings.ALIASING_FUNCTION_POINTERS = 0 @@ -990,7 +966,7 @@ try: if not js_opts: js_opts = True logging.debug('enabling js opts for EMTERPRETIFY') - assert closure is not 2, 'EMTERPRETIFY requires valid asm.js, and is incompatible with closure 2 which disables that' + assert use_closure_compiler is not 2, 'EMTERPRETIFY requires valid asm.js, and is incompatible with closure 2 which disables that' if shared.Settings.DEAD_FUNCTIONS and not js_opts: js_opts = True @@ -1198,6 +1174,14 @@ try: log_time('calculate system libraries') + # final will be an array if linking is deferred, otherwise a normal string. + DEFAULT_FINAL = in_temp(target_basename + '.bc') + def get_final(): + global final + if type(final) != str: + final = DEFAULT_FINAL + return final + # First, combine the bitcode files if there are several. We must also link if we have a singleton .a if len(input_files) + len(extra_files_to_link) > 1 or \ (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0][1]) in BITCODE_ENDINGS or suffix(temp_files[0][1]) in DYNAMICLIB_ENDINGS) and shared.Building.is_ar(temp_files[0][1])): @@ -1205,8 +1189,8 @@ try: logging.debug('linking: ' + str(linker_inputs)) # force archive contents to all be included, if just archives, or if linking shared modules force_archive_contents = len([temp for i, temp in temp_files if not temp.endswith(STATICLIB_ENDINGS)]) == 0 or not shared.Building.can_build_standalone() - shared.Building.link(linker_inputs, in_temp(target_basename + '.bc'), force_archive_contents=force_archive_contents) - final = in_temp(target_basename + '.bc') + just_calculate = DEBUG != '2' # if EMCC_DEBUG=2 , link now, otherwise defer to be more efficient + final = shared.Building.link(linker_inputs, DEFAULT_FINAL, force_archive_contents=force_archive_contents, temp_files=misc_temp_files, just_calculate=just_calculate) else: if not LEAVE_INPUTS_RAW: _, temp_file = temp_files[0] @@ -1230,7 +1214,11 @@ try: intermediate_counter = 0 def save_intermediate(name=None, suffix='js'): global intermediate_counter - shutil.copyfile(final, os.path.join(emscripten_temp_dir, 'emcc-%d%s.%s' % (intermediate_counter, '' if name is None else '-' + name, suffix))) + name = os.path.join(emscripten_temp_dir, 'emcc-%d%s.%s' % (intermediate_counter, '' if name is None else '-' + name, suffix)) + if type(final) != str: + logging.debug('(not saving intermediate %s because deferring linking)' % name) + return + shutil.copyfile(final, name) intermediate_counter += 1 if not LEAVE_INPUTS_RAW: save_intermediate('basebc', 'bc') @@ -1238,10 +1226,12 @@ try: # Optimize, if asked to if not LEAVE_INPUTS_RAW: link_opts = [] if debug_level >= 4 else ['-strip-debug'] # remove LLVM debug if we are not asked for it + if not shared.Settings.ASSERTIONS: + link_opts += ['-disable-verify'] if llvm_lto >= 2: logging.debug('running LLVM opts as pre-LTO') - shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts) + final = shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts, DEFAULT_FINAL) if DEBUG: save_intermediate('opt', 'bc') # If we can LTO, do it before dce, since it opens up dce opportunities @@ -1250,7 +1240,7 @@ try: # add a manual internalize with the proper things we need to be kept alive during lto link_opts += shared.Building.get_safe_internalize() + ['-std-link-opts'] # execute it now, so it is done entirely before we get to the stage of legalization etc. - shared.Building.llvm_opt(final, pre_fastcomp_opts + link_opts) + final = shared.Building.llvm_opt(final, pre_fastcomp_opts + link_opts, DEFAULT_FINAL) if DEBUG: save_intermediate('lto', 'bc') pre_fastcomp_opts = [] link_opts = [] @@ -1261,19 +1251,17 @@ try: if AUTODEBUG: # let llvm opt directly emit ll, to skip writing and reading all the bitcode link_opts += ['-S'] - shared.Building.llvm_opt(final, link_opts, final + '.link.ll') - final = final + '.link.ll' + final = shared.Building.llvm_opt(final, link_opts, get_final() + '.link.ll') if DEBUG: save_intermediate('linktime', 'll') else: if not save_bc: # Simplify LLVM bitcode for fastcomp link_opts = pre_fastcomp_opts + link_opts + fastcomp_opts - shared.Building.llvm_opt(final, link_opts) + final = shared.Building.llvm_opt(final, link_opts, DEFAULT_FINAL) if DEBUG: save_intermediate('linktime', 'bc') if save_bc: shutil.copyfile(final, save_bc) - shared.Building.llvm_opt(final, fastcomp_opts, final + '.adsimp.bc') - final += '.adsimp.bc' + final = shared.Building.llvm_opt(final, fastcomp_opts, get_final() + '.adsimp.bc') if DEBUG: save_intermediate('adsimp', 'bc') # Prepare .ll for Emscripten @@ -1283,16 +1271,18 @@ try: if AUTODEBUG: logging.debug('autodebug') - execute([shared.PYTHON, shared.AUTODEBUGGER, final, final + '.ad.ll']) - final += '.ad.ll' + next = get_final() + '.ad.ll' + execute([shared.PYTHON, shared.AUTODEBUGGER, final, next]) + final = next if DEBUG: save_intermediate('autodebug', 'll') # Simplify bitcode after autodebug if AUTODEBUG or LEAVE_INPUTS_RAW: - shared.Building.llvm_opt(final, fastcomp_opts, final + '.adsimp.bc') - final += '.adsimp.bc' + final = shared.Building.llvm_opt(final, fastcomp_opts, get_final() + '.adsimp.bc') if DEBUG: save_intermediate('adsimp', 'bc') + assert type(final) == str, 'we must have linked the final files, if linking was deferred, by this point' + log_time('post-link') # Emscripten @@ -1326,7 +1316,7 @@ try: file_args.append('--use-preload-cache') if no_heap_copy: file_args.append('--no-heap-copy') - if not closure: + if not use_closure_compiler: file_args.append('--no-closure') file_code = execute([shared.PYTHON, shared.FILE_PACKAGER, unsuffixed(target) + '.data'] + file_args, stdout=PIPE)[0] pre_js = file_code + pre_js @@ -1518,15 +1508,15 @@ try: global js_optimizer_queue if opt_level >= 2: - if debug_level < 2 and not closure == 2: + if debug_level < 2 and not use_closure_compiler == 2: js_optimizer_queue += ['minifyNames'] if debug_level == 0: global minify_whitespace minify_whitespace = True - if closure == 1: + if use_closure_compiler == 1: js_optimizer_queue += ['closure'] - elif debug_level <= 2 and shared.Settings.FINALIZE_ASM_JS and not closure: + elif debug_level <= 2 and shared.Settings.FINALIZE_ASM_JS and not use_closure_compiler: js_optimizer_queue += ['cleanup'] if js_opts: @@ -1552,7 +1542,7 @@ try: flush_js_optimizer_queue() - if closure == 2: + if use_closure_compiler == 2: flush_js_optimizer_queue() logging.debug('running closure') @@ -1822,5 +1812,4 @@ finally: pass else: logging.info('emcc saved files are in:' + temp_dir) - misc_temp_files.clean() diff --git a/emscripten-version.txt b/emscripten-version.txt index 29d93075197f2..6c1f386048777 100644 --- a/emscripten-version.txt +++ b/emscripten-version.txt @@ -1,2 +1,2 @@ -1.34.4 +1.34.5 diff --git a/emscripten.py b/emscripten.py index 3ad1581df657b..bf8c47fc08fc7 100755 --- a/emscripten.py +++ b/emscripten.py @@ -416,9 +416,11 @@ def make_emulated_param(i): specific_bad, specific_bad_func = make_bad(j) Counter.pre.append(specific_bad_func) return specific_bad if not newline else (specific_bad + '\n') - if item not in implemented_functions and not settings['EMULATED_FUNCTION_POINTERS']: # when emulating function pointers, we don't need wrappers + clean_item = item.replace("asm['", '').replace("']", '') + if clean_item not in implemented_functions and not (settings['EMULATED_FUNCTION_POINTERS'] and not settings['RELOCATABLE']): # when emulating function pointers, we don't need wrappers + # but if relocating, then we also have the copies in-module, and do # this is imported into asm, we must wrap it - call_ident = item + call_ident = clean_item if call_ident in metadata['redirects']: call_ident = metadata['redirects'][call_ident] if not call_ident.startswith('_') and not call_ident.startswith('Math_'): call_ident = '_' + call_ident code = call_ident + '(' + coerced_params + ')' @@ -427,8 +429,8 @@ def make_emulated_param(i): if sig[0] == 'f': code = '+' + code code = 'return ' + shared.JS.make_coercion(code, sig[0], settings) code += ';' - Counter.pre.append(make_func(item + '__wrapper', code, params, coercions)) - return item + '__wrapper' + Counter.pre.append(make_func(clean_item + '__wrapper', code, params, coercions)) + return clean_item + '__wrapper' return item if not newline else (item + '\n') if settings['ASSERTIONS'] >= 2: debug_tables[sig] = body @@ -439,8 +441,7 @@ def make_emulated_param(i): Counter.pre = [] function_tables_defs = '\n'.join([info[0] for info in infos]) + '\n' - if not settings['EMULATED_FUNCTION_POINTERS']: - function_tables_defs += '\n// EMSCRIPTEN_END_FUNCS\n' + function_tables_defs += '\n// EMSCRIPTEN_END_FUNCS\n' function_tables_defs += '\n'.join([info[1] for info in infos]) asm_setup = '' @@ -610,14 +611,22 @@ def keyfunc(other): ''' % (sig, ', '.join(full_args), prelude, table_access, ', '.join(args)) basic_funcs.append('ftCall_%s' % sig) + if settings.get('RELOCATABLE'): + params = ','.join(['ptr'] + ['p%d' % p for p in range(len(sig)-1)]) + coerced_params = ','.join([shared.JS.make_coercion('ptr', 'i', settings)] + [shared.JS.make_coercion('p%d', unfloat(sig[p+1]), settings) % p for p in range(len(sig)-1)]) + coercions = ';'.join(['ptr = ptr | 0'] + ['p%d = %s' % (p, shared.JS.make_coercion('p%d' % p, sig[p+1], settings)) for p in range(len(sig)-1)]) + ';' + mini_coerced_params = make_coerced_params(sig) + maybe_return = '' if sig[0] == 'v' else 'return' + funcs_js.append(make_func('mftCall_' + sig, 'if (((ptr|0) >= (fb|0)) & ((ptr|0) < (fb + {{{ FTM_' + sig + ' }}} | 0))) { ' + maybe_return + ' ' + shared.JS.make_coercion('FUNCTION_TABLE_' + sig + '[(ptr-fb)&{{{ FTM_' + sig + ' }}}](' + mini_coerced_params + ')', sig[0], settings) + '; ' + ('return;' if sig[0] == 'v' else '') + ' }' + maybe_return + ' ' + shared.JS.make_coercion('ftCall_' + sig + '(' + coerced_params + ')', sig[0], settings) + ';', params, coercions) + '\n') + def quote(prop): - if settings['CLOSURE_COMPILER'] == 2: + if settings['USE_CLOSURE_COMPILER'] == 2: return "'" + prop + "'" else: return prop def access_quote(prop): - if settings['CLOSURE_COMPILER'] == 2: + if settings['USE_CLOSURE_COMPILER'] == 2: return "['" + prop + "']" else: return '.' + prop @@ -698,16 +707,16 @@ def math_fix(g): if DEBUG: logging.debug('asm text sizes' + str([map(len, funcs_js), len(asm_setup), len(asm_global_vars), len(asm_global_funcs), len(pre_tables), len('\n'.join(function_tables_impls)), len(function_tables_defs.replace('\n', '\n ')), len(exports), len(the_global), len(sending), len(receiving)])) - if not settings.get('EMULATED_FUNCTION_POINTERS'): - final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs - else: + final_function_tables = '\n'.join(function_tables_impls) + '\n' + function_tables_defs + if settings.get('EMULATED_FUNCTION_POINTERS'): asm_setup += '\n' + '\n'.join(function_tables_impls) + '\n' - receiving += '\n' + function_tables_defs + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in last_forwarded_json['Functions']['tables']]) + receiving += '\n' + function_tables_defs.replace('// EMSCRIPTEN_END_FUNCS\n', '') + '\n' + ''.join(['Module["dynCall_%s"] = dynCall_%s\n' % (sig, sig) for sig in last_forwarded_json['Functions']['tables']]) for sig in last_forwarded_json['Functions']['tables'].keys(): name = 'FUNCTION_TABLE_' + sig fullname = name if not settings['SIDE_MODULE'] else ('SIDE_' + name) receiving += 'Module["' + name + '"] = ' + fullname + ';\n' - final_function_tables = '\n// EMSCRIPTEN_END_FUNCS\n' + + final_function_tables = final_function_tables.replace("asm['", '').replace("']", '').replace('var SIDE_FUNCTION_TABLE_', 'var FUNCTION_TABLE_').replace('var dynCall_', '//') if settings['RELOCATABLE']: receiving += ''' diff --git a/site/source/docs/building_from_source/building_emscripten_from_source_on_mac_os_x.rst b/site/source/docs/building_from_source/building_emscripten_from_source_on_mac_os_x.rst index 157a7e8442cca..31be3afa9005c 100644 --- a/site/source/docs/building_from_source/building_emscripten_from_source_on_mac_os_x.rst +++ b/site/source/docs/building_from_source/building_emscripten_from_source_on_mac_os_x.rst @@ -32,12 +32,9 @@ These instructions explain how to install **all** the :ref:`required tools `_). - Download and install git directly from http://git-scm.com/. -#. Install *cmake* if you do not have it yet: - - - Download and install `cmake-2.8.10.2-Darwin64-universal.dmg `_. - - .. tip:: This specific version (2.8.10) is recommended — it has been tested and shown to work. Other versions may not correctly set up the PATH variables, with the result that running *cmake* gives you "not found" errors. +#. Install *CMake* if you do not have it yet: + - Download and install `CMake `_, and make sure it is available in PATH after installation. #. Install *node.js* from http://nodejs.org/ diff --git a/site/source/docs/getting_started/FAQ.rst b/site/source/docs/getting_started/FAQ.rst index 6a09b6271f84d..883040f0fa3ff 100644 --- a/site/source/docs/getting_started/FAQ.rst +++ b/site/source/docs/getting_started/FAQ.rst @@ -62,6 +62,8 @@ The main tips for improving build time are: - Make sure that the native optimizer is being used, which greatly speeds up optimized builds as of 1.28.2. ``EMCC_DEBUG=1`` output should not report errors about the native optimizer failing to build or not being used because of a previous failed build (if it previously failed, do ``emcc --clear-cache`` then compile your file again, and the optimizer will be automatically rebuilt). +- When you have multiple bitcode files as inputs, put the largest file first (LLVM linking links the second and later ones into the first, so less copying is done on the first input to the linker). + Why does my code run slowly? ============================ diff --git a/site/source/docs/introducing_emscripten/Talks-and-Publications.rst b/site/source/docs/introducing_emscripten/Talks-and-Publications.rst index 6b3e7a010eced..0e4431ac09bf1 100644 --- a/site/source/docs/introducing_emscripten/Talks-and-Publications.rst +++ b/site/source/docs/introducing_emscripten/Talks-and-Publications.rst @@ -39,5 +39,6 @@ Papers Books ===== -- `HTML5 Game Development Insights `_ — Book with a chapter on Emscripten +- `WebGL Insights `_: Desaulniers, N. (2015). **Chapter 5: Emscripten and WebGL**. In P. Cozzi, WebGL Insights (pp. 71-88). Boca Raton, Florida: A K Peters/CRC Press. Amazon CRC Press. +- `HTML5 Game Development Insights `_ — Book with a chapter on Emscripten (2014). diff --git a/site/source/docs/optimizing/Optimizing-Code.rst b/site/source/docs/optimizing/Optimizing-Code.rst index bb088e2ba1a2a..86256c3d3f6fa 100644 --- a/site/source/docs/optimizing/Optimizing-Code.rst +++ b/site/source/docs/optimizing/Optimizing-Code.rst @@ -31,7 +31,6 @@ In addition to the ``-Ox`` options, there are separate compiler options that can .. note:: - The meanings of the *emcc* optimization flags (``-O1, -O2`` etc.) are different to the similarly-named options in *gcc*, *clang*, and other compilers, because optimizing JavaScript is very different to optimizing native code. The mapping of the *emcc* levels to the LLVM bitcode optimization levels is documented in the reference. - - If you compile several files into a single JavaScript output, be sure to specify the **same** optimization flags when compiling sources into objects, and objects into JavaScript or HTML. See :ref:`Building-Projects` for more details. Advanced compiler settings diff --git a/src/deps_info.json b/src/deps_info.json index dca929ce25170..95d2452dfb13a 100644 --- a/src/deps_info.json +++ b/src/deps_info.json @@ -21,7 +21,7 @@ "inet_ntoa": ["malloc"], "gethostbyname": ["malloc"], "gethostbyname_r": ["free"], - "getaddrinfo": ["malloc"], + "getaddrinfo": ["malloc", "htonl", "htons", "ntohs"], "freeaddrinfo": ["free"], "gai_strerror": ["malloc"], "setprotoent": ["malloc"], @@ -35,6 +35,10 @@ "emscripten_GetProcAddress": ["strstr"], "emscripten_get_preloaded_image_data_from_FILE": ["fileno"], "__cxa_begin_catch": ["__cxa_can_catch", "__cxa_is_pointer_type"], + "setjmp": ["realloc"], + "glfwSleep": ["sleep"], + "bind": ["htonl", "htons", "ntohs"], + "connect": ["htonl", "htons", "ntohs"], "sleep": ["usleep"] } diff --git a/src/library.js b/src/library.js index 6817ba1f84d5e..db8a8a0405412 100644 --- a/src/library.js +++ b/src/library.js @@ -52,163 +52,6 @@ LibraryManager.library = { pgid: 42 }, - // ========================================================================== - // dirent.h - // ========================================================================== - - opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'open'], - opendir: function(dirname) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_OPENDIR') }}}, dirname); -#endif - // DIR *opendir(const char *dirname); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/opendir.html - // NOTE: Calculating absolute path redundantly since we need to associate it - // with the opened stream. - var path = Pointer_stringify(dirname); - if (!path) { - ___setErrNo(ERRNO_CODES.ENOENT); - return 0; - } - var node; - try { - var lookup = FS.lookupPath(path, { follow: true }); - node = lookup.node; - } catch (e) { - FS.handleFSError(e); - return 0; - } - if (!FS.isDir(node.mode)) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - return 0; - } - var fd = _open(dirname, {{{ cDefine('O_RDONLY') }}}, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); - return fd === -1 ? 0 : FS.getPtrForStream(FS.getStream(fd)); - }, - closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'close', 'fileno'], - closedir: function(dirp) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CLOSEDIR') }}}, dirp); -#endif - // int closedir(DIR *dirp); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html - var fd = _fileno(dirp); - var stream = FS.getStream(fd); - if (stream.currReading) stream.currReading = null; - return _close(fd); - }, - telldir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - telldir: function(dirp) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_TELLDIR') }}}, dirp); -#endif - // long int telldir(DIR *dirp); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/telldir.html - var stream = FS.getStreamFromPtr(dirp); - if (!stream || !FS.isDir(stream.node.mode)) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - return stream.position; - }, - seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'lseek', 'fileno'], - seekdir: function(dirp, loc) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_SEEKDIR') }}}, dirp, loc); -#endif - // void seekdir(DIR *dirp, long int loc); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/seekdir.html - var fd = _fileno(dirp); - _lseek(fd, loc, {{{ cDefine('SEEK_SET') }}}); - }, - rewinddir__deps: ['seekdir'], - rewinddir: function(dirp) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_REWINDDIR') }}}, dirp); -#endif - // void rewinddir(DIR *dirp); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/rewinddir.html - _seekdir(dirp, 0); - var stream = FS.getStreamFromPtr(dirp); - if (stream.currReading) stream.currReading = null; - }, - readdir_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - readdir_r: function(dirp, entry, result) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_READDIR_R') }}}, dirp, entry, result); -#endif - // int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html - var stream = FS.getStreamFromPtr(dirp); - if (!stream) { - return ___setErrNo(ERRNO_CODES.EBADF); - } - if (!stream.currReading) { - try { - // load the list of entries now, then readdir will traverse that list, to ignore changes to files - stream.currReading = FS.readdir(stream.path); - } catch (e) { - return FS.handleFSError(e); - } - } - if (stream.position < 0 || stream.position >= stream.currReading.length) { - {{{ makeSetValue('result', '0', '0', 'i8*') }}}; - return 0; - } - var id; - var type; - var name = stream.currReading[stream.position++]; - if (!name.indexOf('.')) { - id = 1; - type = 4; - } else { - try { - // child may have been removed since we started to read this directory - var child = FS.lookupNode(stream.node, name); - } catch (e) { - // skip to the next entry (not infinite since position is incremented until currReading.length) - return _readdir_r(dirp, entry, result); - } - id = child.id; - type = FS.isChrdev(child.mode) ? 2 : // DT_CHR, character device. - FS.isDir(child.mode) ? 4 : // DT_DIR, directory. - FS.isLink(child.mode) ? 10 : // DT_LNK, symbolic link. - 8; // DT_REG, regular file. - } - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_ino, 'id', 'i32') }}}; - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_off, 'stream.position', 'i32') }}}; - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_reclen, C_STRUCTS.dirent.__size__, 'i32') }}}; - for (var i = 0; i < name.length; i++) { - {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', 'name.charCodeAt(i)', 'i8') }}}; - } - {{{ makeSetValue('entry + ' + C_STRUCTS.dirent.d_name, 'i', '0', 'i8') }}}; - {{{ makeSetValue('entry', C_STRUCTS.dirent.d_type, 'type', 'i8') }}}; - {{{ makeSetValue('result', '0', 'entry', 'i8*') }}}; - return 0; - }, - readdir__deps: ['readdir_r', '__setErrNo', '$ERRNO_CODES'], - readdir: function(dirp) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_READDIR') }}}, dirp); -#endif - // struct dirent *readdir(DIR *dirp); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html - var stream = FS.getStreamFromPtr(dirp); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return 0; - } - // TODO Is it supposed to be safe to execute multiple readdirs? - if (!_readdir.entry) _readdir.entry = _malloc({{{ C_STRUCTS.dirent.__size__ }}}); - if (!_readdir.result) _readdir.result = _malloc(4); - var err = _readdir_r(dirp, _readdir.entry, _readdir.result); - if (err) { - ___setErrNo(err); - return 0; - } - return {{{ makeGetValue(0, '_readdir.result', 'i8*') }}}; - }, - // ========================================================================== // utime.h // ========================================================================== @@ -264,7120 +107,3073 @@ LibraryManager.library = { }, // ========================================================================== - // libgen.h + // sys/file.h // ========================================================================== - __libgenSplitName: function(path) { - if (path === 0 || {{{ makeGetValue('path', 0, 'i8') }}} === 0) { - // Null or empty results in '.'. - var me = ___libgenSplitName; - if (!me.ret) { - me.ret = allocate([{{{ charCode('.') }}}, 0], 'i8', ALLOC_NORMAL); - } - return [me.ret, -1]; - } else { - var slash = {{{ charCode('/') }}}; - var allSlashes = true; - var slashPositions = []; - for (var i = 0; {{{ makeGetValue('path', 'i', 'i8') }}} !== 0; i++) { - if ({{{ makeGetValue('path', 'i', 'i8') }}} === slash) { - slashPositions.push(i); - } else { - allSlashes = false; - } - } - var length = i; - if (allSlashes) { - // All slashes result in a single slash. - {{{ makeSetValue('path', '1', '0', 'i8') }}}; - return [path, -1]; - } else { - // Strip trailing slashes. - while (slashPositions.length && - slashPositions[slashPositions.length - 1] == length - 1) { - {{{ makeSetValue('path', 'slashPositions.pop(i)', '0', 'i8') }}}; - length--; - } - return [path, slashPositions.pop()]; - } - } - }, - basename__deps: ['__libgenSplitName'], - basename: function(path) { - // char *basename(char *path); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/basename.html - var result = ___libgenSplitName(path); - return result[0] + result[1] + 1; - }, - dirname__deps: ['__libgenSplitName'], - dirname: function(path) { - // char *dirname(char *path); - // http://pubs.opengroup.org/onlinepubs/007908799/xsh/dirname.html - var result = ___libgenSplitName(path); - if (result[1] == 0) { - {{{ makeSetValue('result[0]', 1, '0', 'i8') }}}; - } else if (result[1] !== -1) { - {{{ makeSetValue('result[0]', 'result[1]', '0', 'i8') }}}; - } - return result[0]; + flock: function(fd, operation) { + // int flock(int fd, int operation); + // Pretend to succeed + return 0; }, - // ========================================================================== - // sys/stat.h - // ========================================================================== - - stat__deps: ['$FS'], - stat: function(path, buf, dontResolveLastLink) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_STAT') }}}, path, buf, dontResolveLastLink); -#endif - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html - // int stat(const char *path, struct stat *buf); - // NOTE: dontResolveLastLink is a shortcut for lstat(). It should never be - // used in client code. - path = typeof path !== 'string' ? Pointer_stringify(path) : path; - try { - var stat = dontResolveLastLink ? FS.lstat(path) : FS.stat(path); - {{{ makeSetValue('buf', C_STRUCTS.stat.st_dev, 'stat.dev', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.__st_dev_padding, '0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.__st_ino_truncated, 'stat.ino', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mode, 'stat.mode', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_nlink, 'stat.nlink', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_uid, 'stat.uid', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_gid, 'stat.gid', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_rdev, 'stat.rdev', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.__st_rdev_padding, '0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_size, 'stat.size', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_blksize, '4096', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_blocks, 'stat.blocks', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_sec, '(stat.atime.getTime() / 1000)|0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_atim.tv_nsec, '0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_sec, '(stat.mtime.getTime() / 1000)|0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_mtim.tv_nsec, '0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_sec, '(stat.ctime.getTime() / 1000)|0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ctim.tv_nsec, '0', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.stat.st_ino, 'stat.ino', 'i32') }}}; - return 0; - } catch (e) { - if (e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) { - // an error occurred while trying to look up the path; we should just report ENOTDIR - e.setErrno(ERRNO_CODES.ENOTDIR); - } - FS.handleFSError(e); - return -1; - } - }, - lstat__deps: ['stat'], - lstat: function(path, buf) { + chroot__deps: ['__setErrNo', '$ERRNO_CODES'], + chroot: function(path) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_LSTAT') }}}, path, buf); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CHROOT') }}}, path); #endif - // int lstat(const char *path, struct stat *buf); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/lstat.html - return _stat(path, buf, true); + // int chroot(const char *path); + // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html + ___setErrNo(ERRNO_CODES.EACCES); + return -1; }, - fstat__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'stat'], - fstat: function(fildes, buf) { + + fpathconf__deps: ['__setErrNo', '$ERRNO_CODES'], + fpathconf: function(fildes, name) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FSTAT') }}}, fildes, buf); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FPATHCONF') }}}, fildes, name); #endif - // int fstat(int fildes, struct stat *buf); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fstat.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + // long fpathconf(int fildes, int name); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html + // NOTE: The first parameter is ignored, so pathconf == fpathconf. + // The constants here aren't real values. Just mimicking glibc. + switch (name) { + case {{{ cDefine('_PC_LINK_MAX') }}}: + return 32000; + case {{{ cDefine('_PC_MAX_CANON') }}}: + case {{{ cDefine('_PC_MAX_INPUT') }}}: + case {{{ cDefine('_PC_NAME_MAX') }}}: + return 255; + case {{{ cDefine('_PC_PATH_MAX') }}}: + case {{{ cDefine('_PC_PIPE_BUF') }}}: + case {{{ cDefine('_PC_REC_MIN_XFER_SIZE') }}}: + case {{{ cDefine('_PC_REC_XFER_ALIGN') }}}: + case {{{ cDefine('_PC_ALLOC_SIZE_MIN') }}}: + return 4096; + case {{{ cDefine('_PC_CHOWN_RESTRICTED') }}}: + case {{{ cDefine('_PC_NO_TRUNC') }}}: + case {{{ cDefine('_PC_2_SYMLINKS') }}}: + return 1; + case {{{ cDefine('_PC_VDISABLE') }}}: + return 0; + case {{{ cDefine('_PC_SYNC_IO') }}}: + case {{{ cDefine('_PC_ASYNC_IO') }}}: + case {{{ cDefine('_PC_PRIO_IO') }}}: + case {{{ cDefine('_PC_SOCK_MAXBUF') }}}: + case {{{ cDefine('_PC_REC_INCR_XFER_SIZE') }}}: + case {{{ cDefine('_PC_REC_MAX_XFER_SIZE') }}}: + case {{{ cDefine('_PC_SYMLINK_MAX') }}}: + return -1; + case {{{ cDefine('_PC_FILESIZEBITS') }}}: + return 64; } - return _stat(stream.path, buf); + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; }, - mknod__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - mknod: function(path, mode, dev) { + pathconf: 'fpathconf', + + confstr__deps: ['__setErrNo', '$ERRNO_CODES', '$ENV'], + confstr: function(name, buf, len) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_MKNOD') }}}, path, mode, dev); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_CONFSTR') }}}, name, buf, len); #endif - // int mknod(const char *path, mode_t mode, dev_t dev); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mknod.html - path = Pointer_stringify(path); - // we don't want this in the JS API as the JS API - // uses mknod to create all nodes. - switch (mode & {{{ cDefine('S_IFMT') }}}) { - case {{{ cDefine('S_IFREG') }}}: - case {{{ cDefine('S_IFCHR') }}}: - case {{{ cDefine('S_IFBLK') }}}: - case {{{ cDefine('S_IFIFO') }}}: - case {{{ cDefine('S_IFSOCK') }}}: + // size_t confstr(int name, char *buf, size_t len); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/confstr.html + var value; + switch (name) { + case {{{ cDefine('_CS_PATH') }}}: + value = ENV['PATH'] || '/'; + break; + case {{{ cDefine('_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS') }}}: + // Mimicking glibc. + value = 'POSIX_V6_ILP32_OFF32\nPOSIX_V6_ILP32_OFFBIG'; + break; + case {{{ cDefine('_CS_GNU_LIBC_VERSION') }}}: + // This JS implementation was tested against this glibc version. + value = 'glibc 2.14'; + break; + case {{{ cDefine('_CS_GNU_LIBPTHREAD_VERSION') }}}: + // We don't support pthreads. + value = ''; + break; + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LIBS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LIBS') }}}: + value = ''; + break; + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_CFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LDFLAGS') }}}: + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS') }}}: + value = '-m32'; + break; + case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS') }}}: + value = '-m32 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'; break; default: ___setErrNo(ERRNO_CODES.EINVAL); - return -1; + return 0; } - try { - FS.mknod(path, mode, dev); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; + if (len == 0 || buf == 0) { + return value.length + 1; + } else { + var length = Math.min(len, value.length); + for (var i = 0; i < length; i++) { + {{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}}; + } + if (len > length) {{{ makeSetValue('buf', 'i++', '0', 'i8') }}}; + return i; } }, - mkdir__deps: ['mknod'], - mkdir: function(path, mode) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_MKDIR') }}}, path, mode); -#endif - // int mkdir(const char *path, mode_t mode); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkdir.html - path = Pointer_stringify(path); - // remove a trailing slash, if one - /a/b/ has basename of '', but - // we want to create b in the context of this function - path = PATH.normalize(path); - if (path[path.length-1] === '/') path = path.substr(0, path.length-1); - try { - FS.mkdir(path, mode, 0); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } + + execl__deps: ['__setErrNo', '$ERRNO_CODES'], + execl: function(/* ... */) { + // int execl(const char *path, const char *arg0, ... /*, (char *)0 */); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html + // We don't support executing external code. + ___setErrNo(ERRNO_CODES.ENOEXEC); + return -1; }, - mkfifo__deps: ['__setErrNo', '$ERRNO_CODES'], - mkfifo: function(path, mode) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_MKFIFO') }}}, path, mode); -#endif - // int mkfifo(const char *path, mode_t mode); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkfifo.html - // NOTE: We support running only a single process, and named pipes require - // blocking, which we can't provide. The error code is not very - // accurate, but it's the closest among those allowed in the standard - // and unlikely to result in retries. - ___setErrNo(ERRNO_CODES.EROFS); + execle: 'execl', + execlp: 'execl', + execv: 'execl', + execve: 'execl', + execvp: 'execl', + + _exit: function(status) { + // void _exit(int status); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html + Module['exit'](status); + }, + + fork__deps: ['__setErrNo', '$ERRNO_CODES'], + fork: function() { + // pid_t fork(void); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/fork.html + // We don't support multiple processes. + ___setErrNo(ERRNO_CODES.EAGAIN); return -1; }, - chmod__deps: ['$FS', '__setErrNo'], - chmod: function(path, mode, dontResolveLastLink) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_CHMOD') }}}, path, mode, dontResolveLastLink); -#endif - // int chmod(const char *path, mode_t mode); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chmod.html - // NOTE: dontResolveLastLink is a shortcut for lchmod(). It should never be - // used in client code. - path = typeof path !== 'string' ? Pointer_stringify(path) : path; - try { - FS.chmod(path, mode); - return 0; - } catch (e) { - FS.handleFSError(e); + vfork: 'fork', + + setgroups__deps: ['__setErrNo', '$ERRNO_CODES', 'sysconf'], + setgroups: function(ngroups, gidset) { + // int setgroups(int ngroups, const gid_t *gidset); + // https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/setgroups.2.html + if (ngroups < 1 || ngroups > _sysconf({{{ cDefine('_SC_NGROUPS_MAX') }}})) { + ___setErrNo(ERRNO_CODES.EINVAL); return -1; - } - }, - fchmod__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chmod'], - fchmod: function(fildes, mode) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FCHMOD') }}}, fildes, mode); -#endif - // int fchmod(int fildes, mode_t mode); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fchmod.html - try { - FS.fchmod(fildes, mode); - return 0; - } catch (e) { - FS.handleFSError(e); + } else { + // We have just one process/user/group, so it makes no sense to set groups. + ___setErrNo(ERRNO_CODES.EPERM); return -1; } }, - lchmod__deps: ['chmod'], - lchmod: function(path, mode) { + getpagesize: function() { + // int getpagesize(void); + return PAGE_SIZE; + }, + + sysconf__deps: ['__setErrNo', '$ERRNO_CODES'], + sysconf: function(name) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_LCHMOD') }}}, path, mode); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_SYSCONF') }}}, name); #endif - path = Pointer_stringify(path); - try { - FS.lchmod(path, mode); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; + // long sysconf(int name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/sysconf.html + switch(name) { + case {{{ cDefine('_SC_PAGE_SIZE') }}}: return PAGE_SIZE; + case {{{ cDefine('_SC_PHYS_PAGES') }}}: return totalMemory / PAGE_SIZE; + case {{{ cDefine('_SC_ADVISORY_INFO') }}}: + case {{{ cDefine('_SC_BARRIERS') }}}: + case {{{ cDefine('_SC_ASYNCHRONOUS_IO') }}}: + case {{{ cDefine('_SC_CLOCK_SELECTION') }}}: + case {{{ cDefine('_SC_CPUTIME') }}}: + case {{{ cDefine('_SC_FSYNC') }}}: + case {{{ cDefine('_SC_IPV6') }}}: + case {{{ cDefine('_SC_MAPPED_FILES') }}}: + case {{{ cDefine('_SC_MEMLOCK') }}}: + case {{{ cDefine('_SC_MEMLOCK_RANGE') }}}: + case {{{ cDefine('_SC_MEMORY_PROTECTION') }}}: + case {{{ cDefine('_SC_MESSAGE_PASSING') }}}: + case {{{ cDefine('_SC_MONOTONIC_CLOCK') }}}: + case {{{ cDefine('_SC_PRIORITIZED_IO') }}}: + case {{{ cDefine('_SC_PRIORITY_SCHEDULING') }}}: + case {{{ cDefine('_SC_RAW_SOCKETS') }}}: + case {{{ cDefine('_SC_READER_WRITER_LOCKS') }}}: + case {{{ cDefine('_SC_REALTIME_SIGNALS') }}}: + case {{{ cDefine('_SC_SEMAPHORES') }}}: + case {{{ cDefine('_SC_SHARED_MEMORY_OBJECTS') }}}: + case {{{ cDefine('_SC_SPAWN') }}}: + case {{{ cDefine('_SC_SPIN_LOCKS') }}}: + case {{{ cDefine('_SC_SYNCHRONIZED_IO') }}}: + case {{{ cDefine('_SC_THREAD_ATTR_STACKADDR') }}}: + case {{{ cDefine('_SC_THREAD_ATTR_STACKSIZE') }}}: + case {{{ cDefine('_SC_THREAD_CPUTIME') }}}: + case {{{ cDefine('_SC_THREAD_PRIO_INHERIT') }}}: + case {{{ cDefine('_SC_THREAD_PRIO_PROTECT') }}}: + case {{{ cDefine('_SC_THREAD_PROCESS_SHARED') }}}: + case {{{ cDefine('_SC_THREAD_SAFE_FUNCTIONS') }}}: + case {{{ cDefine('_SC_THREADS') }}}: + case {{{ cDefine('_SC_TIMEOUTS') }}}: + case {{{ cDefine('_SC_TIMERS') }}}: + case {{{ cDefine('_SC_VERSION') }}}: + case {{{ cDefine('_SC_2_C_BIND') }}}: + case {{{ cDefine('_SC_2_C_DEV') }}}: + case {{{ cDefine('_SC_2_CHAR_TERM') }}}: + case {{{ cDefine('_SC_2_LOCALEDEF') }}}: + case {{{ cDefine('_SC_2_SW_DEV') }}}: + case {{{ cDefine('_SC_2_VERSION') }}}: + return 200809; + case {{{ cDefine('_SC_THREAD_PRIORITY_SCHEDULING') }}}: + return 0; + case {{{ cDefine('_SC_MQ_OPEN_MAX') }}}: + case {{{ cDefine('_SC_XOPEN_STREAMS') }}}: + case {{{ cDefine('_SC_XBS5_LP64_OFF64') }}}: + case {{{ cDefine('_SC_XBS5_LPBIG_OFFBIG') }}}: + case {{{ cDefine('_SC_AIO_LISTIO_MAX') }}}: + case {{{ cDefine('_SC_AIO_MAX') }}}: + case {{{ cDefine('_SC_SPORADIC_SERVER') }}}: + case {{{ cDefine('_SC_THREAD_SPORADIC_SERVER') }}}: + case {{{ cDefine('_SC_TRACE') }}}: + case {{{ cDefine('_SC_TRACE_EVENT_FILTER') }}}: + case {{{ cDefine('_SC_TRACE_EVENT_NAME_MAX') }}}: + case {{{ cDefine('_SC_TRACE_INHERIT') }}}: + case {{{ cDefine('_SC_TRACE_LOG') }}}: + case {{{ cDefine('_SC_TRACE_NAME_MAX') }}}: + case {{{ cDefine('_SC_TRACE_SYS_MAX') }}}: + case {{{ cDefine('_SC_TRACE_USER_EVENT_MAX') }}}: + case {{{ cDefine('_SC_TYPED_MEMORY_OBJECTS') }}}: + case {{{ cDefine('_SC_V6_LP64_OFF64') }}}: + case {{{ cDefine('_SC_V6_LPBIG_OFFBIG') }}}: + case {{{ cDefine('_SC_2_FORT_DEV') }}}: + case {{{ cDefine('_SC_2_FORT_RUN') }}}: + case {{{ cDefine('_SC_2_PBS') }}}: + case {{{ cDefine('_SC_2_PBS_ACCOUNTING') }}}: + case {{{ cDefine('_SC_2_PBS_CHECKPOINT') }}}: + case {{{ cDefine('_SC_2_PBS_LOCATE') }}}: + case {{{ cDefine('_SC_2_PBS_MESSAGE') }}}: + case {{{ cDefine('_SC_2_PBS_TRACK') }}}: + case {{{ cDefine('_SC_2_UPE') }}}: + case {{{ cDefine('_SC_THREAD_THREADS_MAX') }}}: + case {{{ cDefine('_SC_SEM_NSEMS_MAX') }}}: + case {{{ cDefine('_SC_SYMLOOP_MAX') }}}: + case {{{ cDefine('_SC_TIMER_MAX') }}}: + return -1; + case {{{ cDefine('_SC_V6_ILP32_OFF32') }}}: + case {{{ cDefine('_SC_V6_ILP32_OFFBIG') }}}: + case {{{ cDefine('_SC_JOB_CONTROL') }}}: + case {{{ cDefine('_SC_REGEXP') }}}: + case {{{ cDefine('_SC_SAVED_IDS') }}}: + case {{{ cDefine('_SC_SHELL') }}}: + case {{{ cDefine('_SC_XBS5_ILP32_OFF32') }}}: + case {{{ cDefine('_SC_XBS5_ILP32_OFFBIG') }}}: + case {{{ cDefine('_SC_XOPEN_CRYPT') }}}: + case {{{ cDefine('_SC_XOPEN_ENH_I18N') }}}: + case {{{ cDefine('_SC_XOPEN_LEGACY') }}}: + case {{{ cDefine('_SC_XOPEN_REALTIME') }}}: + case {{{ cDefine('_SC_XOPEN_REALTIME_THREADS') }}}: + case {{{ cDefine('_SC_XOPEN_SHM') }}}: + case {{{ cDefine('_SC_XOPEN_UNIX') }}}: + return 1; + case {{{ cDefine('_SC_THREAD_KEYS_MAX') }}}: + case {{{ cDefine('_SC_IOV_MAX') }}}: + case {{{ cDefine('_SC_GETGR_R_SIZE_MAX') }}}: + case {{{ cDefine('_SC_GETPW_R_SIZE_MAX') }}}: + case {{{ cDefine('_SC_OPEN_MAX') }}}: + return 1024; + case {{{ cDefine('_SC_RTSIG_MAX') }}}: + case {{{ cDefine('_SC_EXPR_NEST_MAX') }}}: + case {{{ cDefine('_SC_TTY_NAME_MAX') }}}: + return 32; + case {{{ cDefine('_SC_ATEXIT_MAX') }}}: + case {{{ cDefine('_SC_DELAYTIMER_MAX') }}}: + case {{{ cDefine('_SC_SEM_VALUE_MAX') }}}: + return 2147483647; + case {{{ cDefine('_SC_SIGQUEUE_MAX') }}}: + case {{{ cDefine('_SC_CHILD_MAX') }}}: + return 47839; + case {{{ cDefine('_SC_BC_SCALE_MAX') }}}: + case {{{ cDefine('_SC_BC_BASE_MAX') }}}: + return 99; + case {{{ cDefine('_SC_LINE_MAX') }}}: + case {{{ cDefine('_SC_BC_DIM_MAX') }}}: + return 2048; + case {{{ cDefine('_SC_ARG_MAX') }}}: return 2097152; + case {{{ cDefine('_SC_NGROUPS_MAX') }}}: return 65536; + case {{{ cDefine('_SC_MQ_PRIO_MAX') }}}: return 32768; + case {{{ cDefine('_SC_RE_DUP_MAX') }}}: return 32767; + case {{{ cDefine('_SC_THREAD_STACK_MIN') }}}: return 16384; + case {{{ cDefine('_SC_BC_STRING_MAX') }}}: return 1000; + case {{{ cDefine('_SC_XOPEN_VERSION') }}}: return 700; + case {{{ cDefine('_SC_LOGIN_NAME_MAX') }}}: return 256; + case {{{ cDefine('_SC_COLL_WEIGHTS_MAX') }}}: return 255; + case {{{ cDefine('_SC_CLK_TCK') }}}: return 100; + case {{{ cDefine('_SC_HOST_NAME_MAX') }}}: return 64; + case {{{ cDefine('_SC_AIO_PRIO_DELTA_MAX') }}}: return 20; + case {{{ cDefine('_SC_STREAM_MAX') }}}: return 16; + case {{{ cDefine('_SC_TZNAME_MAX') }}}: return 6; + case {{{ cDefine('_SC_THREAD_DESTRUCTOR_ITERATIONS') }}}: return 4; + case {{{ cDefine('_SC_NPROCESSORS_ONLN') }}}: { + if (typeof navigator === 'object') return navigator['hardwareConcurrency'] || 1; + return 1; + } } + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; }, - - umask__deps: ['$FS'], - umask: function(newMask) { + sbrk: function(bytes) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_UMASK') }}}, newMask); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_SBRK') }}}, bytes); #endif - // mode_t umask(mode_t cmask); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/umask.html - // NOTE: This value isn't actually used for anything. - if (_umask.cmask === undefined) _umask.cmask = 0x1FF; // S_IRWXU | S_IRWXG | S_IRWXO. - var oldMask = _umask.cmask; - _umask.cmask = newMask; - return oldMask; + // Implement a Linux-like 'memory area' for our 'process'. + // Changes the size of the memory area by |bytes|; returns the + // address of the previous top ('break') of the memory area + // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP + var self = _sbrk; + if (!self.called) { + DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned + self.called = true; + assert(Runtime.dynamicAlloc); + self.alloc = Runtime.dynamicAlloc; + Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') }; + } + var ret = DYNAMICTOP; + if (bytes != 0) { + var success = self.alloc(bytes); + if (!success) return -1 >>> 0; // sbrk failure code + } + return ret; // Previous break location. + }, + + system__deps: ['__setErrNo', '$ERRNO_CODES'], + system: function(command) { + // int system(const char *command); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html + // Can't call external programs. + ___setErrNo(ERRNO_CODES.EAGAIN); + return -1; }, // ========================================================================== - // sys/statvfs.h + // stdlib.h // ========================================================================== - statvfs__deps: ['$FS'], - statvfs: function(path, buf) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_STATVFS') }}}, path, buf); + // tiny, fake malloc/free implementation. If the program actually uses malloc, + // a compiled version will be used; this will only be used if the runtime + // needs to allocate something, for which this is good enough if otherwise + // no malloc is needed. + malloc: function(bytes) { + /* Over-allocate to make sure it is byte-aligned by 8. + * This will leak memory, but this is only the dummy + * implementation (replaced by dlmalloc normally) so + * not an issue. + */ +#if ASSERTIONS == 2 + Runtime.warnOnce('using stub malloc (reference it from C to have the real one included)'); #endif - // http://pubs.opengroup.org/onlinepubs/009695399/functions/statvfs.html - // int statvfs(const char *restrict path, struct statvfs *restrict buf); - // NOTE: None of the constants here are true. We're just returning safe and - // sane values. - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bsize, '4096', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_frsize, '4096', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_blocks, '1000000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bfree, '500000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_bavail, '500000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_files, 'FS.nextInode', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_ffree, '1000000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_favail, '1000000', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_fsid, '42', 'i32') }}}; - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_flag, '2', 'i32') }}}; // ST_NOSUID - {{{ makeSetValue('buf', C_STRUCTS.statvfs.f_namemax, '255', 'i32') }}}; - return 0; + var ptr = Runtime.dynamicAlloc(bytes + 8); + return (ptr+8) & 0xFFFFFFF8; }, - fstatvfs__deps: ['statvfs'], - fstatvfs: function(fildes, buf) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FSTATVFS') }}}, fildes, buf); + free: function() { +#if ASSERTIONS == 2 + Runtime.warnOnce('using stub free (reference it from C to have the real one included)'); #endif - // int fstatvfs(int fildes, struct statvfs *buf); - // http://pubs.opengroup.org/onlinepubs/009604499/functions/statvfs.html - return _statvfs(0, buf); - }, +}, - // ========================================================================== - // fcntl.h - // ========================================================================== + abs: 'Math_abs', + labs: 'Math_abs', - open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - open: function(path, oflag, varargs) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_OPEN') }}}, path, oflag, varargs); -#endif - // int open(const char *path, int oflag, ...); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html - var mode = {{{ makeGetValue('varargs', 0, 'i32') }}}; - path = Pointer_stringify(path); - try { - var stream = FS.open(path, oflag, mode); - return stream.fd; - } catch (e) { - FS.handleFSError(e); - return -1; - } + exit__deps: ['_exit'], + exit: function(status) { + __exit(status); }, - creat__deps: ['open'], - creat: function(path, mode) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_CREAT') }}}, path, mode); -#endif - // int creat(const char *path, mode_t mode); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/creat.html - return _open(path, {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_TRUNC') }}}, allocate([mode, 0, 0, 0], 'i32', ALLOC_STACK)); + + _ZSt9terminatev__deps: ['exit'], + _ZSt9terminatev: function() { + _exit(-1234); }, - mktemp: function(template) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_MKTEMP') }}}, template); -#endif - if (!_mktemp.counter) _mktemp.counter = 0; - var c = (_mktemp.counter++).toString(); - var rep = 'XXXXXX'; - while (c.length < rep.length) c = '0' + c; - writeArrayToMemory(intArrayFromString(c), template + Pointer_stringify(template).indexOf(rep)); - return template; - }, - mkstemp__deps: ['creat', 'mktemp'], - mkstemp: function(template) { + + atexit: function(func, arg) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_MKSTEMP') }}}, template); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_ATEXIT') }}}, func, arg); #endif - return _creat(_mktemp(template), 0600); + __ATEXIT__.unshift({ func: func, arg: arg }); }, - mkdtemp__deps: ['mktemp', 'mkdir'], - mkdtemp: function(template) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_MKDTEMP') }}}, template); -#endif - template = _mktemp(template); - return (_mkdir(template, 0700) === 0) ? template : 0; + __cxa_atexit: 'atexit', + + abort: function() { + Module['abort'](); }, - fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - fcntl: function(fildes, cmd, varargs, dup2) { + + environ__deps: ['$ENV'], #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_FCNTL') }}}, fildes, cmd, varargs, dup2); + environ: '; if (ENVIRONMENT_IS_PTHREAD) _environ = PthreadWorkerInit._environ; else PthreadWorkerInit._environ = _environ = allocate(1, "i32*", ALLOC_STATIC)', +#else + environ: 'allocate(1, "i32*", ALLOC_STATIC)', #endif - // int fcntl(int fildes, int cmd, ...); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/fcntl.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - switch (cmd) { - case {{{ cDefine('F_DUPFD') }}}: - var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; - if (arg < 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - var newStream; - try { - newStream = FS.open(stream.path, stream.flags, 0, arg); - } catch (e) { - FS.handleFSError(e); - return -1; - } - return newStream.fd; - case {{{ cDefine('F_GETFD') }}}: - case {{{ cDefine('F_SETFD') }}}: - return 0; // FD_CLOEXEC makes no sense for a single process. - case {{{ cDefine('F_GETFL') }}}: - return stream.flags; - case {{{ cDefine('F_SETFL') }}}: - var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; - stream.flags |= arg; - return 0; - case {{{ cDefine('F_GETLK') }}}: - case {{{ cDefine('F_GETLK64') }}}: - var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; - var offset = {{{ C_STRUCTS.flock.l_type }}}; - // We're always unlocked. - {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}}; - return 0; - case {{{ cDefine('F_SETLK') }}}: - case {{{ cDefine('F_SETLKW') }}}: - case {{{ cDefine('F_SETLK64') }}}: - case {{{ cDefine('F_SETLKW64') }}}: - // Pretend that the locking is successful. - return 0; - case {{{ cDefine('F_SETOWN') }}}: - case {{{ cDefine('F_GETOWN') }}}: - // These are for sockets. We don't have them fully implemented yet. - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - default: - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - // Should never be reached. Only to silence strict warnings. - return -1; - }, - posix_fadvise: function(fd, offset, len, advice) { - // int posix_fadvise(int fd, off_t offset, off_t len, int advice); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fadvise.html - // Advise as much as you wish. We don't care. - return 0; - }, - posix_madvise: function(){ return 0 }, // ditto as fadvise - posix_fallocate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - posix_fallocate: function(fd, offset, len) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_POSIX_FALLOCATE') }}}, fd, offset, len); -#endif - // int posix_fallocate(int fd, off_t offset, off_t len); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fallocate.html - var stream = FS.getStream(fd); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - try { - FS.allocate(stream, offset, len); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } - }, - - // ========================================================================== - // sys/file.h - // ========================================================================== - - flock: function(fd, operation) { - // int flock(int fd, int operation); - // Pretend to succeed - return 0; - }, - - // ========================================================================== - // nl_types.h - // ========================================================================== - - catopen: function(name, oflag) { - // nl_catd catopen (const char *name, int oflag) - return -1; - }, - - catgets: function(catd, set_id, msg_id, s) { - // char *catgets (nl_catd catd, int set_id, int msg_id, const char *s) - return s; - }, - - catclose: function(catd) { - // int catclose (nl_catd catd) - return 0; - }, - - // ========================================================================== - // poll.h - // ========================================================================== + __environ__deps: ['environ'], + __environ: 'environ', + __buildEnvironment__deps: ['__environ'], + __buildEnvironment: function(env) { + // WARNING: Arbitrary limit! + var MAX_ENV_VALUES = 64; + var TOTAL_ENV_SIZE = 1024; - __DEFAULT_POLLMASK: {{{ cDefine('POLLIN') }}} | {{{ cDefine('POLLOUT') }}}, - poll__deps: ['$FS', '__DEFAULT_POLLMASK'], - poll: function(fds, nfds, timeout) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_POLL') }}}, fds, nfds, timeout); -#endif - // int poll(struct pollfd fds[], nfds_t nfds, int timeout); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html - var nonzero = 0; - for (var i = 0; i < nfds; i++) { - var pollfd = fds + {{{ C_STRUCTS.pollfd.__size__ }}} * i; - var fd = {{{ makeGetValue('pollfd', C_STRUCTS.pollfd.fd, 'i32') }}}; - var events = {{{ makeGetValue('pollfd', C_STRUCTS.pollfd.events, 'i16') }}}; - var mask = {{{ cDefine('POLLNVAL') }}}; - var stream = FS.getStream(fd); - if (stream) { - mask = ___DEFAULT_POLLMASK; - if (stream.stream_ops.poll) { - mask = stream.stream_ops.poll(stream); - } - } - mask &= events | {{{ cDefine('POLLERR') }}} | {{{ cDefine('POLLHUP') }}}; - if (mask) nonzero++; - {{{ makeSetValue('pollfd', C_STRUCTS.pollfd.revents, 'mask', 'i16') }}}; + // Statically allocate memory for the environment. + var poolPtr; + var envPtr; + if (!___buildEnvironment.called) { + ___buildEnvironment.called = true; + // Set default values. Use string keys for Closure Compiler compatibility. + ENV['USER'] = ENV['LOGNAME'] = 'web_user'; + ENV['PATH'] = '/'; + ENV['PWD'] = '/'; + ENV['HOME'] = '/home/web_user'; + ENV['LANG'] = 'C'; + ENV['_'] = Module['thisProgram']; + // Allocate memory. + poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC); + envPtr = allocate(MAX_ENV_VALUES * {{{ Runtime.QUANTUM_SIZE }}}, + 'i8*', ALLOC_STATIC); + {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}}; + {{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}}; + } else { + envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}}; + poolPtr = {{{ makeGetValue('envPtr', '0', 'i8*') }}}; } - return nonzero; - }, - - // ========================================================================== - // unistd.h - // ========================================================================== - access__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - access: function(path, amode) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_ACCESS') }}}, path, amode); -#endif - // int access(const char *path, int amode); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/access.html - path = Pointer_stringify(path); - if (amode & ~{{{ cDefine('S_IRWXO') }}}) { - // need a valid mode - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; + // Collect key=value lines. + var strings = []; + var totalSize = 0; + for (var key in env) { + if (typeof env[key] === 'string') { + var line = key + '=' + env[key]; + strings.push(line); + totalSize += line.length; + } } - var node; - try { - var lookup = FS.lookupPath(path, { follow: true }); - node = lookup.node; - } catch (e) { - FS.handleFSError(e); - return -1; + if (totalSize > TOTAL_ENV_SIZE) { + throw new Error('Environment size exceeded TOTAL_ENV_SIZE!'); } - var perms = ''; - if (amode & {{{ cDefine('R_OK') }}}) perms += 'r'; - if (amode & {{{ cDefine('W_OK') }}}) perms += 'w'; - if (amode & {{{ cDefine('X_OK') }}}) perms += 'x'; - if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; + + // Make new. + var ptrSize = {{{ Runtime.getNativeTypeSize('i8*') }}}; + for (var i = 0; i < strings.length; i++) { + var line = strings[i]; + writeAsciiToMemory(line, poolPtr); + {{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}}; + poolPtr += line.length + 1; } - return 0; + {{{ makeSetValue('envPtr', 'strings.length * ptrSize', '0', 'i8*') }}}; }, - chdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - chdir: function(path) { + $ENV__deps: ['__buildEnvironment'], #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CHDIR') }}}, path); + $ENV__postset: 'if (!ENVIRONMENT_IS_PTHREAD) ___buildEnvironment(ENV);', +#else + $ENV__postset: '___buildEnvironment(ENV);', #endif - // int chdir(const char *path); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/chdir.html - // NOTE: The path argument may be a string, to simplify fchdir(). - if (typeof path !== 'string') path = Pointer_stringify(path); - try { - FS.chdir(path); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } - }, - chown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - chown: function(path, owner, group, dontResolveLastLink) { + $ENV: {}, + getenv__deps: ['$ENV'], + getenv: function(name) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_CHOWN') }}}, path, owner, group, dontResolveLastLink); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_GETENV') }}}, name); #endif - // int chown(const char *path, uid_t owner, gid_t group); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/chown.html - // We don't support multiple users, so changing ownership makes no sense. - // NOTE: The path argument may be a string, to simplify fchown(). - // NOTE: dontResolveLastLink is a shortcut for lchown(). It should never be - // used in client code. - if (typeof path !== 'string') path = Pointer_stringify(path); - try { - FS.chown(path, owner, group); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } + // char *getenv(const char *name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/getenv.html + if (name === 0) return 0; + name = Pointer_stringify(name); + if (!ENV.hasOwnProperty(name)) return 0; + + if (_getenv.ret) _free(_getenv.ret); + _getenv.ret = allocate(intArrayFromString(ENV[name]), 'i8', ALLOC_NORMAL); + return _getenv.ret; }, - chroot__deps: ['__setErrNo', '$ERRNO_CODES'], - chroot: function(path) { + clearenv__deps: ['$ENV', '__buildEnvironment'], + clearenv: function(name) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CHROOT') }}}, path); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CLEARENV') }}}, name); #endif - // int chroot(const char *path); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html - ___setErrNo(ERRNO_CODES.EACCES); - return -1; + // int clearenv (void); + // http://www.gnu.org/s/hello/manual/libc/Environment-Access.html#index-clearenv-3107 + ENV = {}; + ___buildEnvironment(ENV); + return 0; }, - close__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - close: function(fildes) { + setenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], + setenv: function(envname, envval, overwrite) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CLOSE') }}}, fildes); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_SETENV') }}}, envname, envval, overwrite); #endif - // int close(int fildes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/close.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); + // int setenv(const char *envname, const char *envval, int overwrite); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/setenv.html + if (envname === 0) { + ___setErrNo(ERRNO_CODES.EINVAL); return -1; } - try { - FS.close(stream); - return 0; - } catch (e) { - FS.handleFSError(e); + var name = Pointer_stringify(envname); + var val = Pointer_stringify(envval); + if (name === '' || name.indexOf('=') !== -1) { + ___setErrNo(ERRNO_CODES.EINVAL); return -1; } + if (ENV.hasOwnProperty(name) && !overwrite) return 0; + ENV[name] = val; + ___buildEnvironment(ENV); + return 0; }, - dup__deps: ['fcntl'], - dup: function(fildes) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_DUP') }}}, fildes); -#endif - // int dup(int fildes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/dup.html - return _fcntl(fildes, 0, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); // F_DUPFD. - }, - dup2__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'fcntl', 'close'], - dup2: function(fildes, fildes2) { + unsetenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], + unsetenv: function(name) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_DUP2') }}}, fildes, fildes2); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_UNSETENV') }}}, name); #endif - // int dup2(int fildes, int fildes2); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/dup.html - var stream = FS.getStream(fildes); - if (fildes2 < 0) { - ___setErrNo(ERRNO_CODES.EBADF); + // int unsetenv(const char *name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/unsetenv.html + if (name === 0) { + ___setErrNo(ERRNO_CODES.EINVAL); return -1; - } else if (fildes === fildes2 && stream) { - return fildes; - } else { - _close(fildes2); - try { - var stream2 = FS.open(stream.path, stream.flags, 0, fildes2, fildes2); - return stream2.fd; - } catch (e) { - FS.handleFSError(e); - return -1; - } } - }, - fchown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chown'], - fchown: function(fildes, owner, group) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_FCHOWN') }}}, fildes, owner, group); -#endif - // int fchown(int fildes, uid_t owner, gid_t group); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fchown.html - try { - FS.fchown(fildes, owner, group); - return 0; - } catch (e) { - FS.handleFSError(e); + name = Pointer_stringify(name); + if (name === '' || name.indexOf('=') !== -1) { + ___setErrNo(ERRNO_CODES.EINVAL); return -1; } + if (ENV.hasOwnProperty(name)) { + delete ENV[name]; + ___buildEnvironment(ENV); + } + return 0; }, - fchdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chdir'], - fchdir: function(fildes) { + putenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], + putenv: function(string) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FCHDIR') }}}, fildes); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_PUTENV') }}}, string); #endif - // int fchdir(int fildes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fchdir.html - var stream = FS.getStream(fildes); - if (stream) { - return _chdir(stream.path); - } else { - ___setErrNo(ERRNO_CODES.EBADF); + // int putenv(char *string); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/putenv.html + // WARNING: According to the standard (and the glibc implementation), the + // string is taken by reference so future changes are reflected. + // We copy it instead, possibly breaking some uses. + if (string === 0) { + ___setErrNo(ERRNO_CODES.EINVAL); return -1; } - }, - ctermid__deps: ['strcpy'], - ctermid: function(s) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CTERMID') }}}, s); -#endif - // char *ctermid(char *s); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/ctermid.html - if (!_ctermid.ret) { - var arr = intArrayFromString('/dev/tty'); - _ctermid.ret = allocate(arr, 'i8', ALLOC_NORMAL); + string = Pointer_stringify(string); + var splitPoint = string.indexOf('=') + if (string === '' || string.indexOf('=') === -1) { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + var name = string.slice(0, splitPoint); + var value = string.slice(splitPoint + 1); + if (!(name in ENV) || ENV[name] !== value) { + ENV[name] = value; + ___buildEnvironment(ENV); } - return s ? _strcpy(s, _ctermid.ret) : _ctermid.ret; - }, - crypt: function(key, salt) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_CRYPT') }}}, key, salt); -#endif - // char *(const char *, const char *); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/crypt.html - // TODO: Implement (probably compile from C). - ___setErrNo(ERRNO_CODES.ENOSYS); -#if ASSERTIONS - Runtime.warnOnce('crypt() returning an error as we do not support it'); -#endif return 0; }, - encrypt: function(block, edflag) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_ENCRYPT') }}}, block, edflag); -#endif - // void encrypt(char block[64], int edflag); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html - // TODO: Implement (probably compile from C). -#if ASSERTIONS - Runtime.warnOnce('encrypt() returning an error as we do not support it'); -#endif - ___setErrNo(ERRNO_CODES.ENOSYS); + + getloadavg: function(loadavg, nelem) { + // int getloadavg(double loadavg[], int nelem); + // http://linux.die.net/man/3/getloadavg + var limit = Math.min(nelem, 3); + var doubleSize = {{{ Runtime.getNativeTypeSize('double') }}}; + for (var i = 0; i < limit; i++) { + {{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}}; + } + return limit; }, - fpathconf__deps: ['__setErrNo', '$ERRNO_CODES'], - fpathconf: function(fildes, name) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FPATHCONF') }}}, fildes, name); -#endif - // long fpathconf(int fildes, int name); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html - // NOTE: The first parameter is ignored, so pathconf == fpathconf. - // The constants here aren't real values. Just mimicking glibc. - switch (name) { - case {{{ cDefine('_PC_LINK_MAX') }}}: - return 32000; - case {{{ cDefine('_PC_MAX_CANON') }}}: - case {{{ cDefine('_PC_MAX_INPUT') }}}: - case {{{ cDefine('_PC_NAME_MAX') }}}: - return 255; - case {{{ cDefine('_PC_PATH_MAX') }}}: - case {{{ cDefine('_PC_PIPE_BUF') }}}: - case {{{ cDefine('_PC_REC_MIN_XFER_SIZE') }}}: - case {{{ cDefine('_PC_REC_XFER_ALIGN') }}}: - case {{{ cDefine('_PC_ALLOC_SIZE_MIN') }}}: - return 4096; - case {{{ cDefine('_PC_CHOWN_RESTRICTED') }}}: - case {{{ cDefine('_PC_NO_TRUNC') }}}: - case {{{ cDefine('_PC_2_SYMLINKS') }}}: - return 1; - case {{{ cDefine('_PC_VDISABLE') }}}: - return 0; - case {{{ cDefine('_PC_SYNC_IO') }}}: - case {{{ cDefine('_PC_ASYNC_IO') }}}: - case {{{ cDefine('_PC_PRIO_IO') }}}: - case {{{ cDefine('_PC_SOCK_MAXBUF') }}}: - case {{{ cDefine('_PC_REC_INCR_XFER_SIZE') }}}: - case {{{ cDefine('_PC_REC_MAX_XFER_SIZE') }}}: - case {{{ cDefine('_PC_SYMLINK_MAX') }}}: - return -1; - case {{{ cDefine('_PC_FILESIZEBITS') }}}: - return 64; - } - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - }, - pathconf: 'fpathconf', -#if EMTERPRETIFY_ASYNC - fsync__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '$EmterpreterAsync'], - fsync: function(fildes) { - return EmterpreterAsync.handle(function(resume) { - // int fsync(int fildes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsync.html - var stream = FS.getStream(fildes); - if (stream) { - var mount = stream.node.mount; - if (!mount.type.syncfs) { - // We write directly to the file system, so there's nothing to do here. - resume(function() { return 0 }); - return; - } - mount.type.syncfs(mount, false, function(err) { - if (err) { - ___setErrNo(ERRNO_CODES.EIO); - resume(function() { return -1 }); - return; - } - resume(function() { return 0 }); - }); - } else { - ___setErrNo(ERRNO_CODES.EBADF); - resume(function() { return -1 }); - } - }); + + // For compatibility, call to rand() when code requests arc4random(), although this is *not* at all + // as strong as rc4 is. See https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/arc4random.3.html + arc4random: 'rand', + + // ========================================================================== + // string.h + // ========================================================================== + + memcpy__inline: function(dest, src, num, align) { + var ret = ''; + ret += makeCopyValues(dest, src, num, 'null', null, align); + return ret; }, -#else - fsync__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - fsync: function(fildes) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FSYNC') }}}, fildes); -#endif - // int fsync(int fildes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsync.html - var stream = FS.getStream(fildes); - if (stream) { - // We write directly to the file system, so there's nothing to do here. - return 0; - } else { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } + + emscripten_memcpy_big: function(dest, src, num) { + HEAPU8.set(HEAPU8.subarray(src, src+num), dest); + return dest; }, -#endif // EMTERPRETIFY_ASYNC - fdatasync: 'fsync', - truncate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - truncate: function(path, length) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_TRUNCATE') }}}, path, length); -#endif - // int truncate(const char *path, off_t length); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/truncate.html - // NOTE: The path argument may be a string, to simplify ftruncate(). - if (typeof path !== 'string') path = Pointer_stringify(path); - try { - FS.truncate(path, length); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; + + memcpy__asm: true, + memcpy__sig: 'iiii', + memcpy__deps: ['emscripten_memcpy_big'], + memcpy: function(dest, src, num) { + dest = dest|0; src = src|0; num = num|0; + var ret = 0; + if ((num|0) >= 4096) return _emscripten_memcpy_big(dest|0, src|0, num|0)|0; + ret = dest|0; + if ((dest&3) == (src&3)) { + while (dest & 3) { + if ((num|0) == 0) return ret|0; + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; + dest = (dest+1)|0; + src = (src+1)|0; + num = (num-1)|0; + } + while ((num|0) >= 4) { + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i32'), 'i32') }}}; + dest = (dest+4)|0; + src = (src+4)|0; + num = (num-4)|0; + } } - }, - ftruncate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'truncate'], - ftruncate: function(fildes, length) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FTRUNCATE') }}}, fildes, length); -#endif - // int ftruncate(int fildes, off_t length); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftruncate.html - try { - FS.ftruncate(fildes, length); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; + while ((num|0) > 0) { + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; + dest = (dest+1)|0; + src = (src+1)|0; + num = (num-1)|0; } + return ret|0; }, - getcwd__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - getcwd: function(buf, size) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_GETCWD') }}}, buf, size); -#endif - // char *getcwd(char *buf, size_t size); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/getcwd.html - if (size == 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return 0; - } - var cwd = FS.cwd(); - if (size < cwd.length + 1) { - ___setErrNo(ERRNO_CODES.ERANGE); - return 0; + + llvm_memcpy_i32: 'memcpy', + llvm_memcpy_i64: 'memcpy', + llvm_memcpy_p0i8_p0i8_i32: 'memcpy', + llvm_memcpy_p0i8_p0i8_i64: 'memcpy', + + memmove__sig: 'iiii', + memmove__asm: true, + memmove__deps: ['memcpy'], + memmove: function(dest, src, num) { + dest = dest|0; src = src|0; num = num|0; + var ret = 0; + if (((src|0) < (dest|0)) & ((dest|0) < ((src + num)|0))) { + // Unlikely case: Copy backwards in a safe manner + ret = dest; + src = (src + num)|0; + dest = (dest + num)|0; + while ((num|0) > 0) { + dest = (dest - 1)|0; + src = (src - 1)|0; + num = (num - 1)|0; + {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; + } + dest = ret; } else { - writeAsciiToMemory(cwd, buf); - return buf; - } - }, - isatty__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - isatty: function(fildes) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_ISATTY') }}}, fildes); -#endif - // int isatty(int fildes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/isatty.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return 0; - } - // HACK - implement tcgetattr - if (!stream.tty) { - ___setErrNo(ERRNO_CODES.ENOTTY); - return 0; + _memcpy(dest, src, num) | 0; } - return 1; - }, - lchown__deps: ['chown'], - lchown: function(path, owner, group) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_LCHOWN') }}}, path, owner, group); -#endif - // int lchown(const char *path, uid_t owner, gid_t group); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/lchown.html - return _chown(path, owner, group, true); - }, - link__deps: ['__setErrNo', '$ERRNO_CODES'], - link: function(path1, path2) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_LINK') }}}, path1, path2); -#endif - // int link(const char *path1, const char *path2); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/link.html - // We don't support hard links. - ___setErrNo(ERRNO_CODES.EMLINK); - return -1; + return dest | 0; }, - lockf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - lockf: function(fildes, func, size) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_LOCKF') }}}, fildes, func, size); -#endif - // int lockf(int fildes, int function, off_t size); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/lockf.html - var stream = FS.getStream(fildes); - if (stream) { - // Pretend whatever locking or unlocking operation succeeded. Locking does - // not make much sense, since we have a single process/thread. - return 0; - } else { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } + llvm_memmove_i32: 'memmove', + llvm_memmove_i64: 'memmove', + llvm_memmove_p0i8_p0i8_i32: 'memmove', + llvm_memmove_p0i8_p0i8_i64: 'memmove', + + memset__inline: function(ptr, value, num, align) { + return makeSetValues(ptr, 0, value, 'null', num, align); }, - lseek__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - lseek: function(fildes, offset, whence) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_LSEEK') }}}, fildes, offset, whence); -#endif - // off_t lseek(int fildes, off_t offset, int whence); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/lseek.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + memset__sig: 'iiii', + memset__asm: true, + memset: function(ptr, value, num) { + ptr = ptr|0; value = value|0; num = num|0; + var stop = 0, value4 = 0, stop4 = 0, unaligned = 0; + stop = (ptr + num)|0; + if ((num|0) >= {{{ Math.round(2.5*UNROLL_LOOP_MAX) }}}) { + // This is unaligned, but quite large, so work hard to get to aligned settings + value = value & 0xff; + unaligned = ptr & 3; + value4 = value | (value << 8) | (value << 16) | (value << 24); + stop4 = stop & ~3; + if (unaligned) { + unaligned = (ptr + 4 - unaligned)|0; + while ((ptr|0) < (unaligned|0)) { // no need to check for stop, since we have large num + {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; + ptr = (ptr+1)|0; + } + } + while ((ptr|0) < (stop4|0)) { + {{{ makeSetValueAsm('ptr', 0, 'value4', 'i32') }}}; + ptr = (ptr+4)|0; + } } - try { - return FS.llseek(stream, offset, whence); - } catch (e) { - FS.handleFSError(e); - return -1; + while ((ptr|0) < (stop|0)) { + {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; + ptr = (ptr+1)|0; } + return (ptr-num)|0; }, - pipe__deps: ['__setErrNo', '$ERRNO_CODES'], - pipe: function(fildes) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_PIPE') }}}, fildes); -#endif - // int pipe(int fildes[2]); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/pipe.html - // It is possible to implement this using two device streams, but pipes make - // little sense in a single-threaded environment, so we do not support them. - ___setErrNo(ERRNO_CODES.ENOSYS); -#if ASSERTIONS - Runtime.warnOnce('pipe() returning an error as we do not support them'); -#endif - return -1; - }, - pread__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - pread: function(fildes, buf, nbyte, offset) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_PREAD') }}}, fildes, buf, nbyte, offset); -#endif - // ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - try { - var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; - return FS.read(stream, slab, buf, nbyte, offset); - } catch (e) { - FS.handleFSError(e); - return -1; - } + llvm_memset_i32: 'memset', + llvm_memset_p0i8_i32: 'memset', + llvm_memset_p0i8_i64: 'memset', + + // ========================================================================== + // GCC/LLVM specifics + // ========================================================================== + __builtin_prefetch: function(){}, + + // ========================================================================== + // LLVM specifics + // ========================================================================== + + llvm_va_start__inline: function(ptr) { + // varargs - we received a pointer to the varargs as a final 'extra' parameter called 'varrp' + // 2-word structure: struct { void* start; void* currentOffset; } + return makeSetValue(ptr, 0, 'varrp', 'void*') + ';' + makeSetValue(ptr, Runtime.QUANTUM_SIZE, 0, 'void*'); }, - read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'recv', 'pread'], - read: function(fildes, buf, nbyte) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_READ') }}}, fildes, buf, nbyte); -#endif - // ssize_t read(int fildes, void *buf, size_t nbyte); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } -#if SOCKET_WEBRTC - if (stream && ('socket' in stream)) { - return _recv(fildes, buf, nbyte, 0); - } -#endif + llvm_va_end: function() {}, - try { - var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; - return FS.read(stream, slab, buf, nbyte); - } catch (e) { - FS.handleFSError(e); - return -1; - } + llvm_va_copy: function(ppdest, ppsrc) { + // copy the list start + {{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; + + // copy the list's current offset (will be advanced with each call to va_arg) + {{{ makeCopyValues('(ppdest+'+Runtime.QUANTUM_SIZE+')', '(ppsrc+'+Runtime.QUANTUM_SIZE+')', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; }, - sync: function() { - // void sync(void); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/sync.html - // All our writing is already synchronized. This is a no-op. + + llvm_bswap_i16__asm: true, + llvm_bswap_i16__sig: 'ii', + llvm_bswap_i16: function(x) { + x = x|0; + return (((x&0xff)<<8) | ((x>>8)&0xff))|0; }, - rmdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - rmdir: function(path) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_RMDIR') }}}, path); -#endif - // int rmdir(const char *path); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/rmdir.html - path = Pointer_stringify(path); - try { - FS.rmdir(path); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } - }, - unlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - unlink: function(path) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_UNLINK') }}}, path); -#endif - // int unlink(const char *path); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/unlink.html - path = Pointer_stringify(path); - try { - FS.unlink(path); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } - }, - ttyname__deps: ['ttyname_r'], - ttyname: function(fildes) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_TTYNAME') }}}, fildes); -#endif - // char *ttyname(int fildes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/ttyname.html - if (!_ttyname.ret) _ttyname.ret = _malloc(256); - return _ttyname_r(fildes, _ttyname.ret, 256) ? 0 : _ttyname.ret; - }, - ttyname_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'isatty'], - ttyname_r: function(fildes, name, namesize) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_TTYNAME_R') }}}, fildes, name, namesize); -#endif - // int ttyname_r(int fildes, char *name, size_t namesize); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/ttyname.html - var stream = FS.getStream(fildes); - var ttyname = '/dev/tty'; - if (!stream) { - return ___setErrNo(ERRNO_CODES.EBADF); - } else if (!_isatty(fildes)) { - return ___setErrNo(ERRNO_CODES.ENOTTY); - } else if (namesize < ttyname.length + 1) { - return ___setErrNo(ERRNO_CODES.ERANGE); - } - writeStringToMemory(ttyname, name); - return 0; - }, - symlink__deps: ['$FS', '$PATH', '__setErrNo', '$ERRNO_CODES'], - symlink: function(path1, path2) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_SYMLINK') }}}, path1, path2); -#endif - // int symlink(const char *path1, const char *path2); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/symlink.html - path1 = Pointer_stringify(path1); - path2 = Pointer_stringify(path2); - try { - FS.symlink(path1, path2); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } + + llvm_bswap_i32__asm: true, + llvm_bswap_i32__sig: 'ii', + llvm_bswap_i32: function(x) { + x = x|0; + return (((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24))|0; }, - readlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - readlink: function(path, buf, bufsize) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_READLINK') }}}, path, buf, bufsize); -#endif - // ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/readlink.html - path = Pointer_stringify(path); - var str; - try { - str = FS.readlink(path); - } catch (e) { - FS.handleFSError(e); - return -1; - } - str = str.slice(0, Math.max(0, bufsize - 1)); - writeStringToMemory(str, buf, true); - return str.length; + + llvm_bswap_i64__deps: ['llvm_bswap_i32'], + llvm_bswap_i64: function(l, h) { + var retl = _llvm_bswap_i32(h)>>>0; + var reth = _llvm_bswap_i32(l)>>>0; + {{{ makeStructuralReturn(['retl', 'reth']) }}}; }, - pwrite__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - pwrite: function(fildes, buf, nbyte, offset) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_PWRITE') }}}, fildes, buf, nbyte, offset); -#endif - // ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - try { - var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; - return FS.write(stream, slab, buf, nbyte, offset); - } catch (e) { - FS.handleFSError(e); - return -1; - } + + llvm_ctlz_i64__asm: true, + llvm_ctlz_i64__sig: 'iii', + llvm_ctlz_i64: function(l, h, isZeroUndef) { + l = l | 0; + h = h | 0; + isZeroUndef = isZeroUndef | 0; + var ret = 0; + ret = Math_clz32(h) | 0; + if ((ret | 0) == 32) ret = ret + (Math_clz32(l) | 0) | 0; + {{{ makeSetTempRet0('0') }}}; + return ret | 0; }, - write__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'send', 'pwrite'], - write: function(fildes, buf, nbyte) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_WRITE') }}}, fildes, buf, nbyte); -#endif - // ssize_t write(int fildes, const void *buf, size_t nbyte); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } -#if SOCKET_WEBRTC - if (stream && ('socket' in stream)) { - return _send(fildes, buf, nbyte, 0); + llvm_cttz_i32__deps: [function() { + function cttz(x) { + for (var i = 0; i < 8; i++) { + if (x & (1 << i)) { + return i; + } + } + return 8; } -#endif + if (SIDE_MODULE) return ''; // uses it from the parent - try { - var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; - return FS.write(stream, slab, buf, nbyte); - } catch (e) { - FS.handleFSError(e); - return -1; - } - }, - confstr__deps: ['__setErrNo', '$ERRNO_CODES', '$ENV'], - confstr: function(name, buf, len) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_CONFSTR') }}}, name, buf, len); + return 'var cttz_i8; if (ENVIRONMENT_IS_PTHREAD) cttz_i8 = PthreadWorkerInit.cttz_i8; else PthreadWorkerInit.cttz_i8 = cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);'; +#else + return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);'; #endif - // size_t confstr(int name, char *buf, size_t len); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/confstr.html - var value; - switch (name) { - case {{{ cDefine('_CS_PATH') }}}: - value = ENV['PATH'] || '/'; - break; - case {{{ cDefine('_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS') }}}: - // Mimicking glibc. - value = 'POSIX_V6_ILP32_OFF32\nPOSIX_V6_ILP32_OFFBIG'; - break; - case {{{ cDefine('_CS_GNU_LIBC_VERSION') }}}: - // This JS implementation was tested against this glibc version. - value = 'glibc 2.14'; - break; - case {{{ cDefine('_CS_GNU_LIBPTHREAD_VERSION') }}}: - // We don't support pthreads. - value = ''; - break; - case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LIBS') }}}: - case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LIBS') }}}: - case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_CFLAGS') }}}: - case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LDFLAGS') }}}: - case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LIBS') }}}: - case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS') }}}: - case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS') }}}: - case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LIBS') }}}: - value = ''; - break; - case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_CFLAGS') }}}: - case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LDFLAGS') }}}: - case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS') }}}: - value = '-m32'; - break; - case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS') }}}: - value = '-m32 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'; - break; - default: - ___setErrNo(ERRNO_CODES.EINVAL); - return 0; - } - if (len == 0 || buf == 0) { - return value.length + 1; - } else { - var length = Math.min(len, value.length); - for (var i = 0; i < length; i++) { - {{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}}; - } - if (len > length) {{{ makeSetValue('buf', 'i++', '0', 'i8') }}}; - return i; - } - }, - execl__deps: ['__setErrNo', '$ERRNO_CODES'], - execl: function(/* ... */) { - // int execl(const char *path, const char *arg0, ... /*, (char *)0 */); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html - // We don't support executing external code. - ___setErrNo(ERRNO_CODES.ENOEXEC); - return -1; - }, - execle: 'execl', - execlp: 'execl', - execv: 'execl', - execve: 'execl', - execvp: 'execl', - _exit: function(status) { - // void _exit(int status); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html - Module['exit'](status); - }, - fork__deps: ['__setErrNo', '$ERRNO_CODES'], - fork: function() { - // pid_t fork(void); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fork.html - // We don't support multiple processes. - ___setErrNo(ERRNO_CODES.EAGAIN); - return -1; + }], + llvm_cttz_i32__asm: true, + llvm_cttz_i32__sig: 'ii', + llvm_cttz_i32: function(x) { + x = x|0; + var ret = 0; + ret = {{{ makeGetValueAsm('cttz_i8', 'x & 0xff', 'i8') }}}; + if ((ret|0) < 8) return ret|0; + ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 8)&0xff', 'i8') }}}; + if ((ret|0) < 8) return (ret + 8)|0; + ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 16)&0xff', 'i8') }}}; + if ((ret|0) < 8) return (ret + 16)|0; + return ({{{ makeGetValueAsm('cttz_i8', 'x >>> 24', 'i8') }}} + 24)|0; }, - vfork: 'fork', - - getpid__deps: ['$PROCINFO'], - getpid: function() { return PROCINFO.pid; }, - - getppid__deps: ['$PROCINFO'], - getppid: function() { return PROCINFO.ppid; }, - - getpgrp__deps: ['$PROCINFO'], - getpgrp: function() { return PROCINFO.pgid; }, - setpgrp: function() { return 0; }, - getsid__deps: ['__setErrNo', '$ERRNO_CODES', '$PROCINFO'], - getsid: function(pid) { - if (pid && pid != PROCINFO.pid) { - ___setErrNo(ERRNO_CODES.ESRCH); - return -1; - } - return PROCINFO.sid; + llvm_cttz_i64__deps: ['llvm_cttz_i32'], + llvm_cttz_i64: function(l, h) { + var ret = _llvm_cttz_i32(l); + if (ret == 32) ret += _llvm_cttz_i32(h); + {{{ makeStructuralReturn(['ret', '0']) }}}; }, - setsid: function() { return 0; }, - getpgid__deps: ['__setErrNo', '$ERRNO_CODES', '$PROCINFO'], - getpgid: function(pid) { - if (pid && pid != PROCINFO.pid) { - ___setErrNo(ERRNO_CODES.ESRCH); - return -1; - } - return PROCINFO.pgid; - }, - setpgid__deps: ['__setErrNo', '$ERRNO_CODES', '$PROCINFO'], - setpgid: function(pid, pgid) { - if (pid && pid != PROCINFO.pid) { - ___setErrNo(ERRNO_CODES.ESRCH); - return -1; - } - if (pgid != PROCINFO.pgid) { - ___setErrNo(ERRNO_CODES.EPERM); - return -1; - } - return 0; // TODO: call setpgrp() - }, - tcgetpgrp__deps: ['$PROCINFO'], - tcgetpgrp: function(fildes) { - // TODO: check that filedes is terminal - return PROCINFO.pgid; - }, - tcsetpgrp__deps: ['__setErrNo', '$ERRNO_CODES', '$PROCINFO'], - tcsetpgrp: function(fildes, pgid_id) { - // TODO: check that filedes is terminal - if (pgid_id != PROCINFO.pgid) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; + llvm_ctpop_i32: function(x) { + var ret = 0; + while (x) { + if (x&1) ret++; + x >>>= 1; } - return 0; + return ret; }, - getuid: function() { return 0; }, - - setuid__deps: ['__setErrNo', '$ERRNO_CODES'], - setuid: function(uid) { - if (uid != 0) { - ___setErrNo(ERRNO_CODES.EPERM); - return -1; - } - return 0; + llvm_ctpop_i64__deps: ['llvm_ctpop_i32'], + llvm_ctpop_i64: function(l, h) { + return _llvm_ctpop_i32(l) + _llvm_ctpop_i32(h); }, - getegid: 'getgid', - setegid: 'setgid', - - getgid: 'getuid', - setgid: 'setuid', - - geteuid: 'getuid', - seteuid: 'setuid', + llvm_trap: function() { + abort('trap!'); + }, - // NOTE: These do not match the signatures, but they all use the same stub. - setregid: 'setgid', - setreuid: 'setuid', + llvm_prefetch: function(){}, - getresuid: function(ruid, euid, suid) { - {{{ makeSetValue('ruid', '0', '0', 'i32') }}}; - {{{ makeSetValue('euid', '0', '0', 'i32') }}}; - {{{ makeSetValue('suid', '0', '0', 'i32') }}}; - return 0; + __assert_fail: function(condition, filename, line, func) { + ABORT = true; + throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace(); }, - getresgid: 'getresuid', - // NOTE: These do not match the signatures, but they all use the same stub. - setresuid: 'setuid', - setresgid: 'setgid', + __assert_func: function(filename, line, func, condition) { + throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace(); + }, - getgroups__deps: ['__setErrNo', '$ERRNO_CODES'], - getgroups: function(gidsetsize, grouplist) { - // int getgroups(int gidsetsize, gid_t grouplist[]); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/getgroups.html - if (gidsetsize < 1) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else { - {{{ makeSetValue('grouplist', '0', '0', 'i32') }}}; + __cxa_guard_acquire: function(variable) { + if (!{{{ makeGetValue(0, 'variable', 'i8', null, null, 1) }}}) { // ignore SAFE_HEAP stuff because llvm mixes i64 and i8 here + {{{ makeSetValue(0, 'variable', '1', 'i8') }}}; return 1; } + return 0; }, - // TODO: Implement initgroups (grp.h). - setgroups__deps: ['__setErrNo', '$ERRNO_CODES', 'sysconf'], - setgroups: function(ngroups, gidset) { - // int setgroups(int ngroups, const gid_t *gidset); - // https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/setgroups.2.html - if (ngroups < 1 || ngroups > _sysconf({{{ cDefine('_SC_NGROUPS_MAX') }}})) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else { - // We have just one process/user/group, so it makes no sense to set groups. - ___setErrNo(ERRNO_CODES.EPERM); - return -1; - } + __cxa_guard_release: function() {}, + __cxa_guard_abort: function() {}, + + $EXCEPTIONS: { + last: 0, + caught: [], + infos: {}, + deAdjust: function(adjusted) { + if (!adjusted || EXCEPTIONS.infos[adjusted]) return adjusted; + for (var ptr in EXCEPTIONS.infos) { + var info = EXCEPTIONS.infos[ptr]; + if (info.adjusted === adjusted) { +#if EXCEPTION_DEBUG + Module.printErr('de-adjusted exception ptr ' + adjusted + ' to ' + ptr); +#endif + return ptr; + } + } +#if EXCEPTION_DEBUG + Module.printErr('no de-adjustment for unknown exception ptr ' + adjusted); +#endif + return adjusted; + }, + addRef: function(ptr) { +#if EXCEPTION_DEBUG + Module.printErr('addref ' + ptr); +#endif + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + info.refcount++; + }, + decRef: function(ptr) { +#if EXCEPTION_DEBUG + Module.printErr('decref ' + ptr); +#endif + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + assert(info.refcount > 0); + info.refcount--; + if (info.refcount === 0) { + if (info.destructor) { + Runtime.dynCall('vi', info.destructor, [ptr]); + } + delete EXCEPTIONS.infos[ptr]; + ___cxa_free_exception(ptr); +#if EXCEPTION_DEBUG + Module.printErr('decref freeing exception ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); +#endif + } + }, + clearRef: function(ptr) { + if (!ptr) return; + var info = EXCEPTIONS.infos[ptr]; + info.refcount = 0; + }, }, - gethostid: function() { - // long gethostid(void); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/gethostid.html - return 42; + + // Exceptions + __cxa_allocate_exception__deps: ['malloc'], + __cxa_allocate_exception: function(size) { + return _malloc(size); }, - gethostname__deps: ['__setErrNo', '$ERRNO_CODES'], - gethostname: function(name, namelen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_GETHOSTNAME') }}}, name, namelen); + __cxa_free_exception__deps: ['free'], + __cxa_free_exception: function(ptr) { + try { + return _free(ptr); + } catch(e) { // XXX FIXME +#if ASSERTIONS + Module.printErr('exception during cxa_free_exception: ' + e); #endif - // int gethostname(char *name, size_t namelen); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/gethostname.html - var host = 'emscripten'; - if (typeof window !== 'undefined' && window.location.host) { - host = window.location.host; - } - var length = Math.min(namelen, host.length); - for (var i = 0; i < length; i++) { - {{{ makeSetValue('name', 'i', 'host.charCodeAt(i)', 'i8') }}}; - } - if (namelen > length) { - {{{ makeSetValue('name', 'i', '0', 'i8') }}}; - return 0; - } else { - ___setErrNo(ERRNO_CODES.ENAMETOOLONG); - return -1; } }, - getlogin__deps: ['getlogin_r'], - getlogin: function() { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_0({{{ cDefine('EM_PROXIED_GETLOGIN') }}}); -#endif - // char *getlogin(void); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/getlogin.html - if (!_getlogin.ret) _getlogin.ret = _malloc(8); - return _getlogin_r(_getlogin.ret, 8) ? 0 : _getlogin.ret; + __cxa_increment_exception_refcount__deps: ['$EXCEPTIONS'], + __cxa_increment_exception_refcount: function(ptr) { + EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr)); }, - getlogin_r__deps: ['__setErrNo', '$ERRNO_CODES'], - getlogin_r: function(name, namesize) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_GETLOGIN_R') }}}, name, namesize); + __cxa_decrement_exception_refcount__deps: ['$EXCEPTIONS'], + __cxa_decrement_exception_refcount: function(ptr) { + EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr)); + }, + // Here, we throw an exception after recording a couple of values that we need to remember + // We also remember that it was the last exception thrown as we need to know that later. + __cxa_throw__sig: 'viii', + __cxa_throw__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch', '$EXCEPTIONS'], + __cxa_throw: function(ptr, type, destructor) { +#if EXCEPTION_DEBUG + Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor]); #endif - // int getlogin_r(char *name, size_t namesize); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/getlogin.html - var ret = 'root'; - if (namesize < ret.length + 1) { - return ___setErrNo(ERRNO_CODES.ERANGE); + EXCEPTIONS.infos[ptr] = { + ptr: ptr, + adjusted: ptr, + type: type, + destructor: destructor, + refcount: 0 + }; + EXCEPTIONS.last = ptr; + if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { + __ZSt18uncaught_exceptionv.uncaught_exception = 1; } else { - writeAsciiToMemory(ret, name); - return 0; + __ZSt18uncaught_exceptionv.uncaught_exception++; } + {{{ makeThrow('ptr') }}} }, - getpagesize: function() { - // int getpagesize(void); - return PAGE_SIZE; + // This exception will be caught twice, but while begin_catch runs twice, + // we early-exit from end_catch when the exception has been rethrown, so + // pop that here from the caught exceptions. + __cxa_rethrow__deps: ['__cxa_end_catch', '$EXCEPTIONS'], + __cxa_rethrow: function() { + ___cxa_end_catch.rethrown = true; + var ptr = EXCEPTIONS.caught.pop(); +#if EXCEPTION_DEBUG + Module.printErr('Compiled code RE-throwing an exception, popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); +#endif + EXCEPTIONS.last = ptr; + {{{ makeThrow('ptr') }}} }, - getopt: function(argc, argv, optstring) { - // int getopt(int argc, char * const argv[], const char *optstring); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html - // TODO: Implement (probably compile from C). - return -1; + llvm_eh_exception__deps: ['$EXCEPTIONS'], + llvm_eh_exception: function() { + return EXCEPTIONS.last; }, - nice__deps: ['__setErrNo', '$ERRNO_CODES'], - nice: function(incr) { - // int nice(int incr); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/nice.html - // Niceness makes no sense in a single-process environment. - ___setErrNo(ERRNO_CODES.EPERM); + llvm_eh_selector__jsargs: true, + llvm_eh_selector__deps: ['$EXCEPTIONS'], + llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) { + var type = EXCEPTIONS.last; + for (var i = 2; i < arguments.length; i++) { + if (arguments[i] == type) return type; + } return 0; }, - sleep__deps: ['usleep'], - sleep: function(seconds) { - // unsigned sleep(unsigned seconds); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/sleep.html - return _usleep(seconds * 1e6); - }, - - swab: function(src, dest, nbytes) { - // void swab(const void *restrict src, void *restrict dest, ssize_t nbytes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/swab.html - if (nbytes < 0) return; - nbytes -= nbytes % 2; - for (var i = 0; i < nbytes; i += 2) { - var first = {{{ makeGetValue('src', 'i', 'i8') }}}; - var second = {{{ makeGetValue('src', 'i + 1', 'i8') }}}; - {{{ makeSetValue('dest', 'i', 'second', 'i8') }}}; - {{{ makeSetValue('dest', 'i + 1', 'first', 'i8') }}}; + llvm_eh_typeid_for: function(type) { + return type; + }, + __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv', '$EXCEPTIONS'], + __cxa_begin_catch: function(ptr) { + __ZSt18uncaught_exceptionv.uncaught_exception--; + EXCEPTIONS.caught.push(ptr); +#if EXCEPTION_DEBUG + Module.printErr('cxa_begin_catch ' + [ptr, 'stack', EXCEPTIONS.caught]); +#endif + EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr)); + return ptr; + }, + // We're done with a catch. Now, we can run the destructor if there is one + // and free the exception. Note that if the dynCall on the destructor fails + // due to calling apply on undefined, that means that the destructor is + // an invalid index into the FUNCTION_TABLE, so something has gone wrong. + __cxa_end_catch__deps: ['__cxa_free_exception', '$EXCEPTIONS'], + __cxa_end_catch: function() { + if (___cxa_end_catch.rethrown) { + ___cxa_end_catch.rethrown = false; + return; + } + // Clear state flag. + asm['setThrew'](0); + // Call destructor if one is registered then clear it. + var ptr = EXCEPTIONS.caught.pop(); +#if EXCEPTION_DEBUG + Module.printErr('cxa_end_catch popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); +#endif + if (ptr) { + EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr)); + EXCEPTIONS.last = 0; // XXX in decRef? } }, - tcgetpgrp: function(fildes) { - // pid_t tcgetpgrp(int fildes); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/tcgetpgrp.html - // Our only process always runs with group ID 0. - return 0; + __cxa_get_exception_ptr: function(ptr) { +#if EXCEPTION_DEBUG + Module.printErr('cxa_get_exception_ptr ' + ptr); +#endif + // TODO: use info.adjusted? + return ptr; }, - tcsetpgrp__deps: ['__setErrNo', '$ERRNO_CODES'], - tcsetpgrp: function(fildes, pgid_id) { - // int tcsetpgrp(int fildes, pid_t pgid_id); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/tcsetpgrp.html - // We don't support multiple processes or groups with ID other than 0. - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; + _ZSt18uncaught_exceptionv: function() { // std::uncaught_exception() + return !!__ZSt18uncaught_exceptionv.uncaught_exception; }, - sysconf__deps: ['__setErrNo', '$ERRNO_CODES'], - sysconf: function(name) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_SYSCONF') }}}, name); + __cxa_uncaught_exception__deps: ['_ZSt18uncaught_exceptionv'], + __cxa_uncaught_exception: function() { + return !!__ZSt18uncaught_exceptionv.uncaught_exception; + }, + + __cxa_call_unexpected: function(exception) { + Module.printErr('Unexpected exception thrown, this is not properly supported - aborting'); + ABORT = true; + throw exception; + }, + + __cxa_current_primary_exception: function() { + var ret = EXCEPTIONS.caught[EXCEPTIONS.caught.length-1] || 0; + if (ret) EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ret)); + return ret; + }, + + __cxa_rethrow_primary_exception__deps: ['__cxa_rethrow'], + __cxa_rethrow_primary_exception: function(ptr) { + if (!ptr) return; + EXCEPTIONS.caught.push(ptr); + ___cxa_rethrow(); + }, + + terminate: '__cxa_call_unexpected', + + __gxx_personality_v0__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], + __gxx_personality_v0: function() { + }, + + // Finds a suitable catch clause for when an exception is thrown. + // In normal compilers, this functionality is handled by the C++ + // 'personality' routine. This is passed a fairly complex structure + // relating to the context of the exception and makes judgements + // about how to handle it. Some of it is about matching a suitable + // catch clause, and some of it is about unwinding. We already handle + // unwinding using 'if' blocks around each function, so the remaining + // functionality boils down to picking a suitable 'catch' block. + // We'll do that here, instead, to keep things simpler. + + __cxa_find_matching_catch__deps: ['__resumeException', '$EXCEPTIONS'], + __cxa_find_matching_catch: function() { + var thrown = EXCEPTIONS.last; + if (!thrown) { + // just pass through the null ptr + {{{ makeStructuralReturn([0, 0]) }}}; + } + var info = EXCEPTIONS.infos[thrown]; + var throwntype = info.type; + if (!throwntype) { + // just pass through the thrown ptr + {{{ makeStructuralReturn(['thrown', 0]) }}}; + } + var typeArray = Array.prototype.slice.call(arguments); + + var pointer = Module['___cxa_is_pointer_type'](throwntype); + // can_catch receives a **, add indirection + if (!___cxa_find_matching_catch.buffer) ___cxa_find_matching_catch.buffer = _malloc(4); +#if EXCEPTION_DEBUG + Module.print("can_catch on " + [thrown]); #endif - // long sysconf(int name); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/sysconf.html - switch(name) { - case {{{ cDefine('_SC_PAGE_SIZE') }}}: return PAGE_SIZE; - case {{{ cDefine('_SC_PHYS_PAGES') }}}: return totalMemory / PAGE_SIZE; - case {{{ cDefine('_SC_ADVISORY_INFO') }}}: - case {{{ cDefine('_SC_BARRIERS') }}}: - case {{{ cDefine('_SC_ASYNCHRONOUS_IO') }}}: - case {{{ cDefine('_SC_CLOCK_SELECTION') }}}: - case {{{ cDefine('_SC_CPUTIME') }}}: - case {{{ cDefine('_SC_FSYNC') }}}: - case {{{ cDefine('_SC_IPV6') }}}: - case {{{ cDefine('_SC_MAPPED_FILES') }}}: - case {{{ cDefine('_SC_MEMLOCK') }}}: - case {{{ cDefine('_SC_MEMLOCK_RANGE') }}}: - case {{{ cDefine('_SC_MEMORY_PROTECTION') }}}: - case {{{ cDefine('_SC_MESSAGE_PASSING') }}}: - case {{{ cDefine('_SC_MONOTONIC_CLOCK') }}}: - case {{{ cDefine('_SC_PRIORITIZED_IO') }}}: - case {{{ cDefine('_SC_PRIORITY_SCHEDULING') }}}: - case {{{ cDefine('_SC_RAW_SOCKETS') }}}: - case {{{ cDefine('_SC_READER_WRITER_LOCKS') }}}: - case {{{ cDefine('_SC_REALTIME_SIGNALS') }}}: - case {{{ cDefine('_SC_SEMAPHORES') }}}: - case {{{ cDefine('_SC_SHARED_MEMORY_OBJECTS') }}}: - case {{{ cDefine('_SC_SPAWN') }}}: - case {{{ cDefine('_SC_SPIN_LOCKS') }}}: - case {{{ cDefine('_SC_SYNCHRONIZED_IO') }}}: - case {{{ cDefine('_SC_THREAD_ATTR_STACKADDR') }}}: - case {{{ cDefine('_SC_THREAD_ATTR_STACKSIZE') }}}: - case {{{ cDefine('_SC_THREAD_CPUTIME') }}}: - case {{{ cDefine('_SC_THREAD_PRIO_INHERIT') }}}: - case {{{ cDefine('_SC_THREAD_PRIO_PROTECT') }}}: - case {{{ cDefine('_SC_THREAD_PROCESS_SHARED') }}}: - case {{{ cDefine('_SC_THREAD_SAFE_FUNCTIONS') }}}: - case {{{ cDefine('_SC_THREADS') }}}: - case {{{ cDefine('_SC_TIMEOUTS') }}}: - case {{{ cDefine('_SC_TIMERS') }}}: - case {{{ cDefine('_SC_VERSION') }}}: - case {{{ cDefine('_SC_2_C_BIND') }}}: - case {{{ cDefine('_SC_2_C_DEV') }}}: - case {{{ cDefine('_SC_2_CHAR_TERM') }}}: - case {{{ cDefine('_SC_2_LOCALEDEF') }}}: - case {{{ cDefine('_SC_2_SW_DEV') }}}: - case {{{ cDefine('_SC_2_VERSION') }}}: - return 200809; - case {{{ cDefine('_SC_THREAD_PRIORITY_SCHEDULING') }}}: - return 0; - case {{{ cDefine('_SC_MQ_OPEN_MAX') }}}: - case {{{ cDefine('_SC_XOPEN_STREAMS') }}}: - case {{{ cDefine('_SC_XBS5_LP64_OFF64') }}}: - case {{{ cDefine('_SC_XBS5_LPBIG_OFFBIG') }}}: - case {{{ cDefine('_SC_AIO_LISTIO_MAX') }}}: - case {{{ cDefine('_SC_AIO_MAX') }}}: - case {{{ cDefine('_SC_SPORADIC_SERVER') }}}: - case {{{ cDefine('_SC_THREAD_SPORADIC_SERVER') }}}: - case {{{ cDefine('_SC_TRACE') }}}: - case {{{ cDefine('_SC_TRACE_EVENT_FILTER') }}}: - case {{{ cDefine('_SC_TRACE_EVENT_NAME_MAX') }}}: - case {{{ cDefine('_SC_TRACE_INHERIT') }}}: - case {{{ cDefine('_SC_TRACE_LOG') }}}: - case {{{ cDefine('_SC_TRACE_NAME_MAX') }}}: - case {{{ cDefine('_SC_TRACE_SYS_MAX') }}}: - case {{{ cDefine('_SC_TRACE_USER_EVENT_MAX') }}}: - case {{{ cDefine('_SC_TYPED_MEMORY_OBJECTS') }}}: - case {{{ cDefine('_SC_V6_LP64_OFF64') }}}: - case {{{ cDefine('_SC_V6_LPBIG_OFFBIG') }}}: - case {{{ cDefine('_SC_2_FORT_DEV') }}}: - case {{{ cDefine('_SC_2_FORT_RUN') }}}: - case {{{ cDefine('_SC_2_PBS') }}}: - case {{{ cDefine('_SC_2_PBS_ACCOUNTING') }}}: - case {{{ cDefine('_SC_2_PBS_CHECKPOINT') }}}: - case {{{ cDefine('_SC_2_PBS_LOCATE') }}}: - case {{{ cDefine('_SC_2_PBS_MESSAGE') }}}: - case {{{ cDefine('_SC_2_PBS_TRACK') }}}: - case {{{ cDefine('_SC_2_UPE') }}}: - case {{{ cDefine('_SC_THREAD_THREADS_MAX') }}}: - case {{{ cDefine('_SC_SEM_NSEMS_MAX') }}}: - case {{{ cDefine('_SC_SYMLOOP_MAX') }}}: - case {{{ cDefine('_SC_TIMER_MAX') }}}: - return -1; - case {{{ cDefine('_SC_V6_ILP32_OFF32') }}}: - case {{{ cDefine('_SC_V6_ILP32_OFFBIG') }}}: - case {{{ cDefine('_SC_JOB_CONTROL') }}}: - case {{{ cDefine('_SC_REGEXP') }}}: - case {{{ cDefine('_SC_SAVED_IDS') }}}: - case {{{ cDefine('_SC_SHELL') }}}: - case {{{ cDefine('_SC_XBS5_ILP32_OFF32') }}}: - case {{{ cDefine('_SC_XBS5_ILP32_OFFBIG') }}}: - case {{{ cDefine('_SC_XOPEN_CRYPT') }}}: - case {{{ cDefine('_SC_XOPEN_ENH_I18N') }}}: - case {{{ cDefine('_SC_XOPEN_LEGACY') }}}: - case {{{ cDefine('_SC_XOPEN_REALTIME') }}}: - case {{{ cDefine('_SC_XOPEN_REALTIME_THREADS') }}}: - case {{{ cDefine('_SC_XOPEN_SHM') }}}: - case {{{ cDefine('_SC_XOPEN_UNIX') }}}: - return 1; - case {{{ cDefine('_SC_THREAD_KEYS_MAX') }}}: - case {{{ cDefine('_SC_IOV_MAX') }}}: - case {{{ cDefine('_SC_GETGR_R_SIZE_MAX') }}}: - case {{{ cDefine('_SC_GETPW_R_SIZE_MAX') }}}: - case {{{ cDefine('_SC_OPEN_MAX') }}}: - return 1024; - case {{{ cDefine('_SC_RTSIG_MAX') }}}: - case {{{ cDefine('_SC_EXPR_NEST_MAX') }}}: - case {{{ cDefine('_SC_TTY_NAME_MAX') }}}: - return 32; - case {{{ cDefine('_SC_ATEXIT_MAX') }}}: - case {{{ cDefine('_SC_DELAYTIMER_MAX') }}}: - case {{{ cDefine('_SC_SEM_VALUE_MAX') }}}: - return 2147483647; - case {{{ cDefine('_SC_SIGQUEUE_MAX') }}}: - case {{{ cDefine('_SC_CHILD_MAX') }}}: - return 47839; - case {{{ cDefine('_SC_BC_SCALE_MAX') }}}: - case {{{ cDefine('_SC_BC_BASE_MAX') }}}: - return 99; - case {{{ cDefine('_SC_LINE_MAX') }}}: - case {{{ cDefine('_SC_BC_DIM_MAX') }}}: - return 2048; - case {{{ cDefine('_SC_ARG_MAX') }}}: return 2097152; - case {{{ cDefine('_SC_NGROUPS_MAX') }}}: return 65536; - case {{{ cDefine('_SC_MQ_PRIO_MAX') }}}: return 32768; - case {{{ cDefine('_SC_RE_DUP_MAX') }}}: return 32767; - case {{{ cDefine('_SC_THREAD_STACK_MIN') }}}: return 16384; - case {{{ cDefine('_SC_BC_STRING_MAX') }}}: return 1000; - case {{{ cDefine('_SC_XOPEN_VERSION') }}}: return 700; - case {{{ cDefine('_SC_LOGIN_NAME_MAX') }}}: return 256; - case {{{ cDefine('_SC_COLL_WEIGHTS_MAX') }}}: return 255; - case {{{ cDefine('_SC_CLK_TCK') }}}: return 100; - case {{{ cDefine('_SC_HOST_NAME_MAX') }}}: return 64; - case {{{ cDefine('_SC_AIO_PRIO_DELTA_MAX') }}}: return 20; - case {{{ cDefine('_SC_STREAM_MAX') }}}: return 16; - case {{{ cDefine('_SC_TZNAME_MAX') }}}: return 6; - case {{{ cDefine('_SC_THREAD_DESTRUCTOR_ITERATIONS') }}}: return 4; - case {{{ cDefine('_SC_NPROCESSORS_ONLN') }}}: { - if (typeof navigator === 'object') return navigator['hardwareConcurrency'] || 1; - return 1; + {{{ makeSetValue('___cxa_find_matching_catch.buffer', '0', 'thrown', '*') }}}; + thrown = ___cxa_find_matching_catch.buffer; + // The different catch blocks are denoted by different types. + // Due to inheritance, those types may not precisely match the + // type of the thrown object. Find one which matches, and + // return the type of the catch block which should be called. + for (var i = 0; i < typeArray.length; i++) { + if (typeArray[i] && Module['___cxa_can_catch'](typeArray[i], throwntype, thrown)) { + thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection + info.adjusted = thrown; +#if EXCEPTION_DEBUG + Module.print(" can_catch found " + [thrown, typeArray[i]]); +#endif + {{{ makeStructuralReturn(['thrown', 'typeArray[i]']) }}}; } } - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; + // Shouldn't happen unless we have bogus data in typeArray + // or encounter a type for which emscripten doesn't have suitable + // typeinfo defined. Best-efforts match just in case. + thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection + {{{ makeStructuralReturn(['thrown', 'throwntype']) }}}; }, - sbrk: function(bytes) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_SBRK') }}}, bytes); + + __resumeException__deps: ['$EXCEPTIONS', function() { Functions.libraryFunctions['___resumeException'] = 1 }], // will be called directly from compiled code + __resumeException: function(ptr) { +#if EXCEPTION_DEBUG + Module.print("Resuming exception " + [ptr, EXCEPTIONS.last]); #endif - // Implement a Linux-like 'memory area' for our 'process'. - // Changes the size of the memory area by |bytes|; returns the - // address of the previous top ('break') of the memory area - // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP - var self = _sbrk; - if (!self.called) { - DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned - self.called = true; - assert(Runtime.dynamicAlloc); - self.alloc = Runtime.dynamicAlloc; - Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') }; - } - var ret = DYNAMICTOP; - if (bytes != 0) { - var success = self.alloc(bytes); - if (!success) return -1 >>> 0; // sbrk failure code - } - return ret; // Previous break location. + if (!EXCEPTIONS.last) { EXCEPTIONS.last = ptr; } + EXCEPTIONS.clearRef(EXCEPTIONS.deAdjust(ptr)); // exception refcount should be cleared, but don't free it + {{{ makeThrow('ptr') }}} }, - // ========================================================================== - // stdio.h - // ========================================================================== - - _getFloat: function(text) { - return /^[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?/.exec(text); + llvm_uadd_with_overflow_i8: function(x, y) { + x = x & 0xff; + y = y & 0xff; + {{{ makeStructuralReturn(['(x+y) & 0xff', 'x+y > 255']) }}}; }, - _scanString__deps: ['_getFloat'], - _scanString: function(format, get, unget, varargs) { - if (!__scanString.whiteSpace) { - __scanString.whiteSpace = {}; - __scanString.whiteSpace[{{{ charCode(' ') }}}] = 1; - __scanString.whiteSpace[{{{ charCode('\t') }}}] = 1; - __scanString.whiteSpace[{{{ charCode('\n') }}}] = 1; - __scanString.whiteSpace[{{{ charCode('\v') }}}] = 1; - __scanString.whiteSpace[{{{ charCode('\f') }}}] = 1; - __scanString.whiteSpace[{{{ charCode('\r') }}}] = 1; - } - // Supports %x, %4x, %d.%d, %lld, %s, %f, %lf. - // TODO: Support all format specifiers. - format = Pointer_stringify(format); - var soFar = 0; - if (format.indexOf('%n') >= 0) { - // need to track soFar - var _get = get; - get = function get() { - soFar++; - return _get(); - } - var _unget = unget; - unget = function unget() { - soFar--; - return _unget(); - } - } - var formatIndex = 0; - var argsi = 0; - var fields = 0; - var argIndex = 0; - var next; - - mainLoop: - for (var formatIndex = 0; formatIndex < format.length;) { - if (format[formatIndex] === '%' && format[formatIndex+1] == 'n') { - argIndex = Runtime.prepVararg(argIndex, '*'); - var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - argIndex += Runtime.getAlignSize('void*', null, true); - {{{ makeSetValue('argPtr', 0, 'soFar', 'i32') }}}; - formatIndex += 2; - continue; - } - - if (format[formatIndex] === '%') { - var nextC = format.indexOf('c', formatIndex+1); - if (nextC > 0) { - var maxx = 1; - if (nextC > formatIndex+1) { - var sub = format.substring(formatIndex+1, nextC); - maxx = parseInt(sub); - if (maxx != sub) maxx = 0; - } - if (maxx) { - argIndex = Runtime.prepVararg(argIndex, '*'); - var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - argIndex += Runtime.getAlignSize('void*', null, true); - fields++; - for (var i = 0; i < maxx; i++) { - next = get(); - {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}}; - if (next === 0) return i > 0 ? fields : fields-1; // we failed to read the full length of this field - } - formatIndex += nextC - formatIndex + 1; - continue; - } - } - } + llvm_umul_with_overflow_i8: function(x, y) { + x = x & 0xff; + y = y & 0xff; + {{{ makeStructuralReturn(['(x*y) & 0xff', 'x*y > 255']) }}}; + }, - // handle %[...] - if (format[formatIndex] === '%' && format.indexOf('[', formatIndex+1) > 0) { - var match = /\%([0-9]*)\[(\^)?(\]?[^\]]*)\]/.exec(format.substring(formatIndex)); - if (match) { - var maxNumCharacters = parseInt(match[1]) || Infinity; - var negateScanList = (match[2] === '^'); - var scanList = match[3]; - - // expand "middle" dashs into character sets - var middleDashMatch; - while ((middleDashMatch = /([^\-])\-([^\-])/.exec(scanList))) { - var rangeStartCharCode = middleDashMatch[1].charCodeAt(0); - var rangeEndCharCode = middleDashMatch[2].charCodeAt(0); - for (var expanded = ''; rangeStartCharCode <= rangeEndCharCode; expanded += String.fromCharCode(rangeStartCharCode++)); - scanList = scanList.replace(middleDashMatch[1] + '-' + middleDashMatch[2], expanded); - } + llvm_uadd_with_overflow_i16: function(x, y) { + x = x & 0xffff; + y = y & 0xffff; + {{{ makeStructuralReturn(['(x+y) & 0xffff', 'x+y > 65535']) }}}; + }, - argIndex = Runtime.prepVararg(argIndex, '*'); - var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - argIndex += Runtime.getAlignSize('void*', null, true); - fields++; - - for (var i = 0; i < maxNumCharacters; i++) { - next = get(); - if (negateScanList) { - if (scanList.indexOf(String.fromCharCode(next)) < 0) { - {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}}; - } else { - unget(); - break; - } - } else { - if (scanList.indexOf(String.fromCharCode(next)) >= 0) { - {{{ makeSetValue('argPtr++', 0, 'next', 'i8') }}}; - } else { - unget(); - break; - } - } - } + llvm_umul_with_overflow_i16: function(x, y) { + x = x & 0xffff; + y = y & 0xffff; + {{{ makeStructuralReturn(['(x*y) & 0xffff', 'x*y > 65535']) }}}; + }, - // write out null-terminating character - {{{ makeSetValue('argPtr++', 0, '0', 'i8') }}}; - formatIndex += match[0].length; - - continue; - } - } - // remove whitespace - while (1) { - next = get(); - if (next == 0) return fields; - if (!(next in __scanString.whiteSpace)) break; - } - unget(); - - if (format[formatIndex] === '%') { - formatIndex++; - var suppressAssignment = false; - if (format[formatIndex] == '*') { - suppressAssignment = true; - formatIndex++; - } - var maxSpecifierStart = formatIndex; - while (format[formatIndex].charCodeAt(0) >= {{{ charCode('0') }}} && - format[formatIndex].charCodeAt(0) <= {{{ charCode('9') }}}) { - formatIndex++; - } - var max_; - if (formatIndex != maxSpecifierStart) { - max_ = parseInt(format.slice(maxSpecifierStart, formatIndex), 10); - } - var long_ = false; - var half = false; - var quarter = false; - var longLong = false; - if (format[formatIndex] == 'l') { - long_ = true; - formatIndex++; - if (format[formatIndex] == 'l') { - longLong = true; - formatIndex++; - } - } else if (format[formatIndex] == 'h') { - half = true; - formatIndex++; - if (format[formatIndex] == 'h') { - quarter = true; - formatIndex++; - } - } - var type = format[formatIndex]; - formatIndex++; - var curr = 0; - var buffer = []; - // Read characters according to the format. floats are trickier, they may be in an unfloat state in the middle, then be a valid float later - if (type == 'f' || type == 'e' || type == 'g' || - type == 'F' || type == 'E' || type == 'G') { - next = get(); - while (next > 0 && (!(next in __scanString.whiteSpace))) { - buffer.push(String.fromCharCode(next)); - next = get(); - } - var m = __getFloat(buffer.join('')); - var last = m ? m[0].length : 0; - for (var i = 0; i < buffer.length - last + 1; i++) { - unget(); - } - buffer.length = last; - } else { - next = get(); - var first = true; - - // Strip the optional 0x prefix for %x. - if ((type == 'x' || type == 'X') && (next == {{{ charCode('0') }}})) { - var peek = get(); - if (peek == {{{ charCode('x') }}} || peek == {{{ charCode('X') }}}) { - next = get(); - } else { - unget(); - } - } - - while ((curr < max_ || isNaN(max_)) && next > 0) { - if (!(next in __scanString.whiteSpace) && // stop on whitespace - (type == 's' || - ((type === 'd' || type == 'u' || type == 'i') && ((next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}}) || - (first && next == {{{ charCode('-') }}}))) || - ((type === 'x' || type === 'X') && (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}} || - next >= {{{ charCode('a') }}} && next <= {{{ charCode('f') }}} || - next >= {{{ charCode('A') }}} && next <= {{{ charCode('F') }}}))) && - (formatIndex >= format.length || next !== format[formatIndex].charCodeAt(0))) { // Stop when we read something that is coming up - buffer.push(String.fromCharCode(next)); - next = get(); - curr++; - first = false; - } else { - break; - } - } - unget(); - } - if (buffer.length === 0) return fields; // Stop here. - if (suppressAssignment) continue; - - var text = buffer.join(''); - argIndex = Runtime.prepVararg(argIndex, '*'); - var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; - argIndex += Runtime.getAlignSize('void*', null, true); - var base = 10; - switch (type) { - case 'X': case 'x': - base = 16; - case 'd': case 'u': case 'i': - if (quarter) { - {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i8') }}}; - } else if (half) { - {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i16') }}}; - } else if (longLong) { - {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i64') }}}; - } else { - {{{ makeSetValue('argPtr', 0, 'parseInt(text, base)', 'i32') }}}; - } - break; - case 'F': - case 'f': - case 'E': - case 'e': - case 'G': - case 'g': - case 'E': - // fallthrough intended - if (long_) { - {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'double') }}}; - } else { - {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'float') }}}; - } - break; - case 's': - var array = intArrayFromString(text); - for (var j = 0; j < array.length; j++) { - {{{ makeSetValue('argPtr', 'j', 'array[j]', 'i8') }}}; - } - break; - } - fields++; - } else if (format[formatIndex].charCodeAt(0) in __scanString.whiteSpace) { - next = get(); - while (next in __scanString.whiteSpace) { - if (next <= 0) break mainLoop; // End of input. - next = get(); - } - unget(next); - formatIndex++; - } else { - // Not a specifier. - next = get(); - if (format[formatIndex].charCodeAt(0) !== next) { - unget(next); - break mainLoop; - } - formatIndex++; - } - } - return fields; + llvm_uadd_with_overflow_i32: function(x, y) { + x = x>>>0; + y = y>>>0; + {{{ makeStructuralReturn(['(x+y)>>>0', 'x+y > 4294967295']) }}}; }, - // NOTE: Invalid stream pointers passed to these functions would cause a crash - // in native code. We, on the other hand, just ignore them, since it's - // easier. - clearerr__deps: ['$FS'], - clearerr: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CLEARERR') }}}, stream); -#endif - // void clearerr(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html - stream = FS.getStreamFromPtr(stream); - if (!stream) { - return; - } - stream.eof = false; - stream.error = false; + + llvm_umul_with_overflow_i32: function(x, y) { + x = x>>>0; + y = y>>>0; + {{{ makeStructuralReturn(['(x*y)>>>0', 'x*y > 4294967295']) }}}; }, - fclose__deps: ['close', 'fileno'], - fclose: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FCLOSE') }}}, stream); + + llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }], + llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) { +#if ASSERTIONS + Runtime.warnOnce('no overflow support in llvm_umul_with_overflow_i64'); #endif - // int fclose(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fclose.html - var fd = _fileno(stream); - return _close(fd); + var low = ___muldi3(xl, xh, yl, yh); + {{{ makeStructuralReturn(['low', makeGetTempRet0(), '0']) }}}; }, - fdopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - fdopen: function(fildes, mode) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FDOPEN') }}}, fildes, mode); -#endif - // FILE *fdopen(int fildes, const char *mode); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fdopen.html - mode = Pointer_stringify(mode); - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return 0; - } - if ((mode.indexOf('w') != -1 && !stream.isWrite) || - (mode.indexOf('r') != -1 && !stream.isRead) || - (mode.indexOf('a') != -1 && !stream.isAppend) || - (mode.indexOf('+') != -1 && (!stream.isRead || !stream.isWrite))) { - ___setErrNo(ERRNO_CODES.EINVAL); - return 0; - } else { - stream.error = false; - stream.eof = false; - return FS.getPtrForStream(stream); + + llvm_stacksave: function() { + var self = _llvm_stacksave; + if (!self.LLVM_SAVEDSTACKS) { + self.LLVM_SAVEDSTACKS = []; } + self.LLVM_SAVEDSTACKS.push(Runtime.stackSave()); + return self.LLVM_SAVEDSTACKS.length-1; }, - feof__deps: ['$FS'], - feof: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FEOF') }}}, stream); -#endif - // int feof(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/feof.html - stream = FS.getStreamFromPtr(stream); - return Number(stream && stream.eof); + llvm_stackrestore: function(p) { + var self = _llvm_stacksave; + var ret = self.LLVM_SAVEDSTACKS[p]; + self.LLVM_SAVEDSTACKS.splice(p, 1); + Runtime.stackRestore(ret); }, - ferror__deps: ['$FS'], - ferror: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FERROR') }}}, stream); -#endif - // int ferror(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/ferror.html - stream = FS.getStreamFromPtr(stream); - return Number(stream && stream.error); + + __cxa_pure_virtual: function() { + ABORT = true; + throw 'Pure virtual function called!'; }, - fflush__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - fflush: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FFLUSH') }}}, stream); -#endif - // int fflush(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fflush.html - /* - // Disabled, see https://github.com/kripken/emscripten/issues/2770 - stream = FS.getStreamFromPtr(stream); - if (stream.stream_ops.flush) { - stream.stream_ops.flush(stream); - } - */ + llvm_flt_rounds: function() { + return -1; // 'indeterminable' for FLT_ROUNDS }, - fgetc__deps: ['$FS', 'fread'], -#if USE_PTHREADS - fgetc__postset: 'if (ENVIRONMENT_IS_PTHREAD) _fgetc.ret = PthreadWorkerInit._fgetc_ret; else PthreadWorkerInit._fgetc_ret = _fgetc.ret = allocate([0], "i8", ALLOC_STATIC);', -#else - fgetc__postset: '_fgetc.ret = allocate([0], "i8", ALLOC_STATIC);', -#endif - fgetc: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FGETC') }}}, stream); -#endif - // int fgetc(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetc.html - var streamObj = FS.getStreamFromPtr(stream); - if (!streamObj) return -1; - if (streamObj.eof || streamObj.error) return -1; - var ret = _fread(_fgetc.ret, 1, 1, stream); - if (ret == 0) { - return -1; - } else if (ret == -1) { - streamObj.error = true; - return -1; - } else { - return {{{ makeGetValue('_fgetc.ret', '0', 'i8', null, 1) }}}; - } + + llvm_memory_barrier: function(){}, + + llvm_atomic_load_add_i32_p0i32: function(ptr, delta) { + var ret = {{{ makeGetValue('ptr', '0', 'i32') }}}; + {{{ makeSetValue('ptr', '0', 'ret+delta', 'i32') }}}; + return ret; }, - getc: 'fgetc', - getc_unlocked: 'fgetc', - getchar__deps: ['fgetc', 'stdin'], - getchar: function() { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_0({{{ cDefine('EM_PROXIED_GETCHAR') }}}); -#endif - // int getchar(void); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/getchar.html - return _fgetc({{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}); + + llvm_expect_i32__inline: function(val, expected) { + return '(' + val + ')'; }, - fgetpos__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - fgetpos: function(stream, pos) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FGETPOS') }}}, stream, pos); -#endif - // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetpos.html - stream = FS.getStreamFromPtr(stream); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - if (FS.isChrdev(stream.node.mode)) { - ___setErrNo(ERRNO_CODES.ESPIPE); - return -1; - } - {{{ makeSetValue('pos', '0', 'stream.position', 'i32') }}}; - var state = (stream.eof ? 1 : 0) + (stream.error ? 2 : 0); - {{{ makeSetValue('pos', Runtime.getNativeTypeSize('i32'), 'state', 'i32') }}}; - return 0; + + llvm_lifetime_start: function() {}, + llvm_lifetime_end: function() {}, + + llvm_invariant_start: function() {}, + llvm_invariant_end: function() {}, + + llvm_objectsize_i32: function() { return -1 }, // TODO: support this + + llvm_dbg_declare__inline: function() { throw 'llvm_debug_declare' }, // avoid warning + + // llvm-nacl + + llvm_nacl_atomic_store_i32__inline: true, + + llvm_nacl_atomic_cmpxchg_i8__inline: true, + llvm_nacl_atomic_cmpxchg_i16__inline: true, + llvm_nacl_atomic_cmpxchg_i32__inline: true, + + // gnu atomics + + __atomic_is_lock_free: function(size, ptr) { + return size <= 4 && (ptr&(size-1)) == 0; }, - fgets__deps: ['fgetc'], - fgets: function(s, n, stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_FGETS') }}}, s, n, stream); -#endif - // char *fgets(char *restrict s, int n, FILE *restrict stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgets.html - var streamObj = FS.getStreamFromPtr(stream); - if (!streamObj) return 0; - if (streamObj.error || streamObj.eof) return 0; - var byte_; - for (var i = 0; i < n - 1 && byte_ != {{{ charCode('\n') }}}; i++) { - byte_ = _fgetc(stream); - if (byte_ == -1) { - if (streamObj.error || (streamObj.eof && i == 0)) return 0; - else if (streamObj.eof) break; - } - {{{ makeSetValue('s', 'i', 'byte_', 'i8') }}}; - } - {{{ makeSetValue('s', 'i', '0', 'i8') }}}; - return s; + + __atomic_load_8: function(ptr, memmodel) { + {{{ makeStructuralReturn([makeGetValue('ptr', 0, 'i32'), makeGetValue('ptr', 4, 'i32')]) }}}; }, - gets__deps: ['fgets'], - gets: function(s) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_GETS') }}}, s); -#endif - // char *gets(char *s); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/gets.html - return _fgets(s, 1e6, {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}); + + __atomic_store_8: function(ptr, vall, valh, memmodel) { + {{{ makeSetValue('ptr', 0, 'vall', 'i32') }}}; + {{{ makeSetValue('ptr', 4, 'valh', 'i32') }}}; }, - fileno: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FILENO') }}}, stream); -#endif - // int fileno(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fileno.html - stream = FS.getStreamFromPtr(stream); - if (!stream) return -1; - return stream.fd; - }, - ftrylockfile: function() { - // int ftrylockfile(FILE *file); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/flockfile.html - // Locking is useless in a single-threaded environment. Pretend to succeed. - return 0; + + __atomic_exchange_8: function(ptr, vall, valh, memmodel) { + var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; + var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; + {{{ makeSetValue('ptr', 0, 'vall', 'i32') }}}; + {{{ makeSetValue('ptr', 4, 'valh', 'i32') }}}; + {{{ makeStructuralReturn(['l', 'h']) }}}; }, - flockfile: 'ftrylockfile', - funlockfile: 'ftrylockfile', - fopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'open'], - fopen: function(filename, mode) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FOPEN') }}}, filename, mode); -#endif - // FILE *fopen(const char *restrict filename, const char *restrict mode); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fopen.html - var flags; - mode = Pointer_stringify(mode); - if (mode[0] == 'r') { - if (mode.indexOf('+') != -1) { - flags = {{{ cDefine('O_RDWR') }}}; - } else { - flags = {{{ cDefine('O_RDONLY') }}}; - } - } else if (mode[0] == 'w') { - if (mode.indexOf('+') != -1) { - flags = {{{ cDefine('O_RDWR') }}}; - } else { - flags = {{{ cDefine('O_WRONLY') }}}; - } - flags |= {{{ cDefine('O_CREAT') }}}; - flags |= {{{ cDefine('O_TRUNC') }}}; - } else if (mode[0] == 'a') { - if (mode.indexOf('+') != -1) { - flags = {{{ cDefine('O_RDWR') }}}; - } else { - flags = {{{ cDefine('O_WRONLY') }}}; - } - flags |= {{{ cDefine('O_CREAT') }}}; - flags |= {{{ cDefine('O_APPEND') }}}; + + __atomic_compare_exchange_8: function(ptr, expected, desiredl, desiredh, weak, success_memmodel, failure_memmodel) { + var pl = {{{ makeGetValue('ptr', 0, 'i32') }}}; + var ph = {{{ makeGetValue('ptr', 4, 'i32') }}}; + var el = {{{ makeGetValue('expected', 0, 'i32') }}}; + var eh = {{{ makeGetValue('expected', 4, 'i32') }}}; + if (pl === el && ph === eh) { + {{{ makeSetValue('ptr', 0, 'desiredl', 'i32') }}}; + {{{ makeSetValue('ptr', 4, 'desiredh', 'i32') }}}; + return 1; } else { - ___setErrNo(ERRNO_CODES.EINVAL); + {{{ makeSetValue('expected', 0, 'pl', 'i32') }}}; + {{{ makeSetValue('expected', 4, 'ph', 'i32') }}}; return 0; } - var fd = _open(filename, flags, allocate([0x1FF, 0, 0, 0], 'i32', ALLOC_STACK)); // All creation permissions. - return fd === -1 ? 0 : FS.getPtrForStream(FS.getStream(fd)); - }, - fputc__deps: ['$FS', 'write', 'fileno'], -#if USE_PTHREADS - fputc__postset: 'if (ENVIRONMENT_IS_PTHREAD) _fputc.ret = PthreadWorkerInit._fputc_ret; else PthreadWorkerInit._fputc_ret = _fputc.ret = allocate([0], "i8", ALLOC_STATIC);', -#else - fputc__postset: '_fputc.ret = allocate([0], "i8", ALLOC_STATIC);', -#endif - fputc: function(c, stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FPUTC') }}}, c, stream); -#endif - // int fputc(int c, FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputc.html - var chr = unSign(c & 0xFF); - {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}}; - var fd = _fileno(stream); - var ret = _write(fd, _fputc.ret, 1); - if (ret == -1) { - var streamObj = FS.getStreamFromPtr(stream); - if (streamObj) streamObj.error = true; - return -1; - } else { - return chr; - } - }, - putc: 'fputc', - putc_unlocked: 'fputc', - putchar__deps: ['fputc', 'stdout'], - putchar: function(c) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_PUTCHAR') }}}, c); -#endif - // int putchar(int c); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/putchar.html - return _fputc(c, {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); - }, - putchar_unlocked: 'putchar', - fputs__deps: ['write', 'strlen', 'fileno'], - fputs: function(s, stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FPUTS') }}}, s, stream); -#endif - // int fputs(const char *restrict s, FILE *restrict stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputs.html - var fd = _fileno(stream); - return _write(fd, s, _strlen(s)); }, - puts__deps: ['fputs', 'fputc', 'stdout'], - puts: function(s) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_PUTS') }}}, s); -#endif -#if NO_FILESYSTEM == 0 - // int puts(const char *s); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/puts.html - // NOTE: puts() always writes an extra newline. - var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; - var ret = _fputs(s, stdout); - if (ret < 0) { - return ret; - } else { - var newlineRet = _fputc({{{ charCode('\n') }}}, stdout); - return (newlineRet < 0) ? -1 : ret + 1; - } -#else - // extra effort to support puts, even without a filesystem. very partial, very hackish - var result = Pointer_stringify(s); - var string = result.substr(0); - if (string[string.length-1] === '\n') string = string.substr(0, string.length-1); // remove a final \n, as Module.print will do that - Module.print(string); - return result.length; -#endif + + __atomic_fetch_add_8__deps: ['llvm_uadd_with_overflow_i64'], + __atomic_fetch_add_8: function(ptr, vall, valh, memmodel) { + var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; + var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; + {{{ makeSetValue('ptr', 0, '_llvm_uadd_with_overflow_i64(l, h, vall, valh)', 'i32') }}}; + {{{ makeSetValue('ptr', 4, makeGetTempRet0(), 'i32') }}}; + {{{ makeStructuralReturn(['l', 'h']) }}}; }, - fread__deps: ['$FS', 'read'], - fread: function(ptr, size, nitems, stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_FREAD') }}}, ptr, size, nitems, stream); -#endif - // size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html - var bytesToRead = nitems * size; - if (bytesToRead == 0) { - return 0; - } - var bytesRead = 0; - var streamObj = FS.getStreamFromPtr(stream); - if (!streamObj) { - ___setErrNo(ERRNO_CODES.EBADF); - return 0; - } - while (streamObj.ungotten.length && bytesToRead > 0) { - {{{ makeSetValue('ptr++', '0', 'streamObj.ungotten.pop()', 'i8') }}}; - bytesToRead--; - bytesRead++; - } - var err = _read(streamObj.fd, ptr, bytesToRead); - if (err == -1) { - if (streamObj) streamObj.error = true; - return 0; - } - bytesRead += err; - if (bytesRead < bytesToRead) streamObj.eof = true; - return (bytesRead / size)|0; + + __atomic_fetch_sub_8__deps: ['i64Subtract'], + __atomic_fetch_sub_8: function(ptr, vall, valh, memmodel) { + var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; + var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; + {{{ makeSetValue('ptr', 0, '_i64Subtract(l, h, vall, valh)', 'i32') }}}; + {{{ makeSetValue('ptr', 4, makeGetTempRet0(), 'i32') }}}; + {{{ makeStructuralReturn(['l', 'h']) }}}; }, - freopen__deps: ['$FS', 'fclose', 'fopen', '__setErrNo', '$ERRNO_CODES'], - freopen: function(filename, mode, stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_FREOPEN') }}}, filename, mode, stream); -#endif - // FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/freopen.html - if (!filename) { - var streamObj = FS.getStreamFromPtr(stream); - if (!streamObj) { - ___setErrNo(ERRNO_CODES.EBADF); - return 0; - } - if (_freopen.buffer) _free(_freopen.buffer); - filename = intArrayFromString(streamObj.path); - filename = allocate(filename, 'i8', ALLOC_NORMAL); - } - _fclose(stream); - return _fopen(filename, mode); + + __atomic_fetch_and_8__deps: ['i64Subtract'], + __atomic_fetch_and_8: function(ptr, vall, valh, memmodel) { + var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; + var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; + {{{ makeSetValue('ptr', 0, 'l&vall', 'i32') }}}; + {{{ makeSetValue('ptr', 4, 'h&valh', 'i32') }}}; + {{{ makeStructuralReturn(['l', 'h']) }}}; }, - fseek__deps: ['$FS', 'lseek', 'fileno'], - fseek: function(stream, offset, whence) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_FSEEK') }}}, stream, offset, whence); -#endif - // int fseek(FILE *stream, long offset, int whence); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fseek.html - var fd = _fileno(stream); - var ret = _lseek(fd, offset, whence); - if (ret == -1) { - return -1; - } - stream = FS.getStreamFromPtr(stream); - stream.eof = false; - return 0; + + __atomic_fetch_or_8: function(ptr, vall, valh, memmodel) { + var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; + var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; + {{{ makeSetValue('ptr', 0, 'l|vall', 'i32') }}}; + {{{ makeSetValue('ptr', 4, 'h|valh', 'i32') }}}; + {{{ makeStructuralReturn(['l', 'h']) }}}; }, - fseeko: 'fseek', - fsetpos__deps: ['$FS', 'lseek', '__setErrNo', '$ERRNO_CODES'], - fsetpos: function(stream, pos) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FSETPOS') }}}, stream, pos); -#endif - // int fsetpos(FILE *stream, const fpos_t *pos); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsetpos.html - stream = FS.getStreamFromPtr(stream); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - if (FS.isChrdev(stream.node.mode)) { - ___setErrNo(ERRNO_CODES.EPIPE); - return -1; - } - stream.position = {{{ makeGetValue('pos', '0', 'i32') }}}; - var state = {{{ makeGetValue('pos', Runtime.getNativeTypeSize('i32'), 'i32') }}}; - stream.eof = Boolean(state & 1); - stream.error = Boolean(state & 2); - return 0; + + __atomic_fetch_xor_8: function(ptr, vall, valh, memmodel) { + var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; + var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; + {{{ makeSetValue('ptr', 0, 'l^vall', 'i32') }}}; + {{{ makeSetValue('ptr', 4, 'h^valh', 'i32') }}}; + {{{ makeStructuralReturn(['l', 'h']) }}}; }, - ftell__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], - ftell: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_FTELL') }}}, stream); -#endif - // long ftell(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftell.html - stream = FS.getStreamFromPtr(stream); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - if (FS.isChrdev(stream.node.mode)) { - ___setErrNo(ERRNO_CODES.ESPIPE); - return -1; - } else { - return stream.position; - } + + + // ========================================================================== + // llvm-mono integration + // ========================================================================== + + llvm_mono_load_i8_p0i8: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i8') }}}; }, - ftello: 'ftell', - fwrite__deps: ['$FS', 'write', 'fileno'], - fwrite: function(ptr, size, nitems, stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_FWRITE') }}}, ptr, size, nitems, stream); -#endif - // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/fwrite.html - var bytesToWrite = nitems * size; - if (bytesToWrite == 0) return 0; - var fd = _fileno(stream); - var bytesWritten = _write(fd, ptr, bytesToWrite); - if (bytesWritten == -1) { - var streamObj = FS.getStreamFromPtr(stream); - if (streamObj) streamObj.error = true; - return 0; - } else { - return (bytesWritten / size)|0; - } + + llvm_mono_store_i8_p0i8: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i8') }}}; }, - popen__deps: ['__setErrNo', '$ERRNO_CODES'], - popen: function(command, mode) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_POPEN') }}}, command, mode); -#endif - // FILE *popen(const char *command, const char *mode); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/popen.html - // We allow only one process, so no pipes. - ___setErrNo(ERRNO_CODES.EMFILE); - return 0; + + llvm_mono_load_i16_p0i16: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i16') }}}; }, - pclose__deps: ['__setErrNo', '$ERRNO_CODES'], - pclose: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_PCLOSE') }}}, stream); -#endif - // int pclose(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/pclose.html - // We allow only one process, so no pipes. - ___setErrNo(ERRNO_CODES.ECHILD); - return -1; + + llvm_mono_store_i16_p0i16: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i16') }}}; }, - perror__deps: ['puts', 'fputs', 'fputc', 'strerror', '__errno_location'], - perror: function(s) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_PERROR') }}}, s); -#endif - // void perror(const char *s); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/perror.html - var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; - if (s) { - _fputs(s, stdout); - _fputc({{{ charCode(':') }}}, stdout); - _fputc({{{ charCode(' ') }}}, stdout); - } - var errnum = {{{ makeGetValue('___errno_location()', '0', 'i32') }}}; - _puts(_strerror(errnum)); + + llvm_mono_load_i32_p0i32: function(ptr) { + return {{{ makeGetValue('ptr', 0, 'i32') }}}; }, - remove__deps: ['unlink', 'rmdir'], - remove: function(path) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_REMOVE') }}}, path); -#endif - // int remove(const char *path); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/remove.html - var ret = _unlink(path); - if (ret == -1) ret = _rmdir(path); - return ret; - }, - rename__deps: ['__setErrNo', '$ERRNO_CODES'], - rename: function(old_path, new_path) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_RENAME') }}}, old_path, new_path); -#endif - // int rename(const char *old, const char *new); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/rename.html - old_path = Pointer_stringify(old_path); - new_path = Pointer_stringify(new_path); - try { - FS.rename(old_path, new_path); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } - }, - rewind__deps: ['$FS', 'fseek'], - rewind: function(stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_REWIND') }}}, stream); -#endif - // void rewind(FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/rewind.html - _fseek(stream, 0, 0); // SEEK_SET. - var streamObj = FS.getStreamFromPtr(stream); - if (streamObj) streamObj.error = false; - }, - setvbuf: function(stream, buf, type, size) { - // int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/setvbuf.html - // TODO: Implement custom buffering. - return 0; - }, - setbuf__deps: ['setvbuf'], - setbuf: function(stream, buf) { - // void setbuf(FILE *restrict stream, char *restrict buf); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/setbuf.html - if (buf) _setvbuf(stream, buf, 0, 8192); // _IOFBF, BUFSIZ. - else _setvbuf(stream, buf, 2, 8192); // _IONBF, BUFSIZ. - }, - tmpnam__deps: ['$FS'], - tmpnam: function(s, dir, prefix) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_TMPNAM') }}}, s, dir, prefix); -#endif - // char *tmpnam(char *s); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpnam.html - // NOTE: The dir and prefix arguments are for internal use only. - dir = dir || '/tmp'; - var folder = FS.findObject(dir); - if (!folder || !folder.isFolder) { - dir = '/tmp'; - folder = FS.findObject(dir); - if (!folder || !folder.isFolder) return 0; - } - var name = prefix || 'file'; - do { - name += String.fromCharCode(65 + (Math.random() * 25)|0); - } while (name in folder.contents); - var result = dir + '/' + name; - if (!_tmpnam.buffer) _tmpnam.buffer = _malloc(256); - if (!s) s = _tmpnam.buffer; - assert(result.length <= 255); - writeAsciiToMemory(result, s); - return s; - }, - tempnam__deps: ['tmpnam'], - tempnam: function(dir, pfx) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_TEMPNAM') }}}, dir, pfx); -#endif - // char *tempnam(const char *dir, const char *pfx); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/tempnam.html - return _tmpnam(0, Pointer_stringify(dir), Pointer_stringify(pfx)); - }, - tmpfile__deps: ['tmpnam', 'fopen'], - tmpfile: function() { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_0({{{ cDefine('EM_PROXIED_TMPFILE') }}}); -#endif - // FILE *tmpfile(void); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpfile.html - // TODO: Delete the created file on closing. - if (!_tmpfile.mode) { - _tmpfile.mode = allocate(intArrayFromString('w+'), 'i8', ALLOC_NORMAL); - } - return _fopen(_tmpnam(0), _tmpfile.mode); - }, - ungetc__deps: ['$FS'], - ungetc: function(c, stream) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_UNGETC') }}}, c, stream); -#endif - // int ungetc(int c, FILE *stream); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/ungetc.html - stream = FS.getStreamFromPtr(stream); - if (!stream) { - return -1; - } - if (c === {{{ cDefine('EOF') }}}) { - // do nothing for EOF character - return c; - } - c = unSign(c & 0xFF); - stream.ungotten.push(c); - stream.eof = false; - return c; - }, - system__deps: ['__setErrNo', '$ERRNO_CODES'], - system: function(command) { - // int system(const char *command); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html - // Can't call external programs. - ___setErrNo(ERRNO_CODES.EAGAIN); - return -1; - }, - fscanf__deps: ['$FS', '_scanString', 'fgetc', 'ungetc'], - fscanf: function(stream, format, varargs) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_FSCANF') }}}, stream, format, varargs); -#endif - // int fscanf(FILE *restrict stream, const char *restrict format, ... ); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html - var streamObj = FS.getStreamFromPtr(stream); - if (!streamObj) { - return -1; - } - var buffer = []; - function get() { - var c = _fgetc(stream); - buffer.push(c); - return c; - }; - function unget() { - _ungetc(buffer.pop(), stream); - }; - return __scanString(format, get, unget, varargs); - }, - scanf__deps: ['fscanf'], - scanf: function(format, varargs) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_SCANF') }}}, format, varargs); -#endif - // int scanf(const char *restrict format, ... ); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html - var stdin = {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}; - return _fscanf(stdin, format, varargs); - }, - fprintf__deps: ['fwrite', '_formatString'], - fprintf: function(stream, format, varargs) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_xprintf_varargs({{{ cDefine('EM_PROXIED_FPRINTF') }}}, stream, format, varargs); -#endif - // int fprintf(FILE *restrict stream, const char *restrict format, ...); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html - var result = __formatString(format, varargs); - var stack = Runtime.stackSave(); - var ret = _fwrite(allocate(result, 'i8', ALLOC_STACK), 1, result.length, stream); - Runtime.stackRestore(stack); - return ret; - }, - printf__deps: ['fprintf'], - printf: function(format, varargs) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_xprintf_varargs({{{ cDefine('EM_PROXIED_PRINTF') }}}, 0, format, varargs); -#endif - // int printf(const char *restrict format, ...); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html -#if NO_FILESYSTEM == 0 - var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; - return _fprintf(stdout, format, varargs); -#else - // extra effort to support printf, even without a filesystem. very partial, very hackish - var result = __formatString(format, varargs); - var string = intArrayToString(result); - if (string[string.length-1] === '\n') string = string.substr(0, string.length-1); // remove a final \n, as Module.print will do that - Module.print(string); - return result.length; -#endif - }, - dprintf__deps: ['_formatString', 'write'], - dprintf: function(fd, format, varargs) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_xprintf_varargs({{{ cDefine('EM_PROXIED_DPRINTF') }}}, fd, format, varargs); -#endif - var result = __formatString(format, varargs); - var stack = Runtime.stackSave(); - var ret = _write(fd, allocate(result, 'i8', ALLOC_STACK), result.length); - Runtime.stackRestore(stack); + + llvm_mono_store_i32_p0i32: function(value, ptr) { + {{{ makeSetValue('ptr', 0, 'value', 'i32') }}}; }, - // convert va_arg into varargs - vfprintf__deps: ['fprintf'], - vfprintf: function(s, f, va_arg) { - return _fprintf(s, f, {{{ makeGetValue('va_arg', 0, '*') }}}); - }, - vprintf__deps: ['printf'], - vprintf: function(format, va_arg) { - return _printf(format, {{{ makeGetValue('va_arg', 0, '*') }}}); - }, - vdprintf__deps: ['dprintf'], - vdprintf: function (fd, format, va_arg) { - return _dprintf(fd, format, {{{ makeGetValue('va_arg', 0, '*') }}}); - }, - vscanf__deps: ['scanf'], - vscanf: function(format, va_arg) { - return _scanf(format, {{{ makeGetValue('va_arg', 0, '*') }}}); - }, - vfscanf__deps: ['fscanf'], - vfscanf: function(s, format, va_arg) { - return _fscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); + // ========================================================================== + // math.h + // ========================================================================== + + cos: 'Math_cos', + cosf: 'Math_cos', + cosl: 'Math_cos', + sin: 'Math_sin', + sinf: 'Math_sin', + sinl: 'Math_sin', + tan: 'Math_tan', + tanf: 'Math_tan', + tanl: 'Math_tan', + acos: 'Math_acos', + acosf: 'Math_acos', + acosl: 'Math_acos', + asin: 'Math_asin', + asinf: 'Math_asin', + asinl: 'Math_asin', + atan: 'Math_atan', + atanf: 'Math_atan', + atanl: 'Math_atan', + atan2: 'Math_atan2', + atan2f: 'Math_atan2', + atan2l: 'Math_atan2', + exp: 'Math_exp', + expf: 'Math_exp', + expl: 'Math_exp', + log: 'Math_log', + logf: 'Math_log', + logl: 'Math_log', + sqrt: 'Math_sqrt', + sqrtf: 'Math_sqrt', + sqrtl: 'Math_sqrt', + fabs: 'Math_abs', + fabsf: 'Math_abs', + fabsl: 'Math_abs', + llvm_fabs_f32: 'Math_abs', + llvm_fabs_f64: 'Math_abs', + ceil: 'Math_ceil', + ceilf: 'Math_ceil', + ceill: 'Math_ceil', + floor: 'Math_floor', + floorf: 'Math_floor', + floorl: 'Math_floor', + pow: 'Math_pow', + powf: 'Math_pow', + powl: 'Math_pow', + llvm_sqrt_f32: 'Math_sqrt', + llvm_sqrt_f64: 'Math_sqrt', + llvm_pow_f32: 'Math_pow', + llvm_pow_f64: 'Math_pow', + llvm_log_f32: 'Math_log', + llvm_log_f64: 'Math_log', + llvm_exp_f32: 'Math_exp', + llvm_exp_f64: 'Math_exp', + + _reallyNegative: function(x) { + return x < 0 || (x === 0 && (1/x) === -Infinity); }, // ========================================================================== - // sys/mman.h + // dlfcn.h - Dynamic library loading + // + // Some limitations: + // + // * Minification on each file separately may not work, as they will + // have different shortened names. You can in theory combine them, then + // minify, then split... perhaps. + // + // * LLVM optimizations may fail. If the child wants to access a function + // in the parent, LLVM opts may remove it from the parent when it is + // being compiled. Not sure how to tell LLVM to not do so. // ========================================================================== - mmap__deps: ['$FS', 'memset'], - mmap: function(start, num, prot, flags, fd, offset) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_6({{{ cDefine('EM_PROXIED_MMAP') }}}, start, num, prot, flags, fd, offset); -#endif - /* FIXME: Since mmap is normally implemented at the kernel level, - * this implementation simply uses malloc underneath the call to - * mmap. - */ - var MAP_PRIVATE = 2; - var ptr; - var allocated = false; + $DLFCN: { + error: null, + errorMsg: null, + loadedLibs: {}, // handle -> [refcount, name, lib_object] + loadedLibNames: {}, // name -> handle + }, + // void* dlopen(const char* filename, int flag); + dlopen__deps: ['$DLFCN', '$FS', '$ENV'], + dlopen: function(filename, flag) { + // void *dlopen(const char *file, int mode); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html + filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename); - if (!_mmap.mappings) _mmap.mappings = {}; + if (DLFCN.loadedLibNames[filename]) { + // Already loaded; increment ref count and return. + var handle = DLFCN.loadedLibNames[filename]; + DLFCN.loadedLibs[handle].refcount++; + return handle; + } - if (fd == -1) { - ptr = _malloc(num); - if (!ptr) return -1; - _memset(ptr, 0, num); - allocated = true; + if (filename === '__self__') { + var handle = -1; + var lib_module = Module; + var cached_functions = {}; } else { - var info = FS.getStream(fd); - if (!info) return -1; + var target = FS.findObject(filename); + if (!target || target.isFolder || target.isDevice) { + DLFCN.errorMsg = 'Could not find dynamic lib: ' + filename; + return 0; + } else { + FS.forceLoadFile(target); + var lib_data = FS.readFile(filename, { encoding: 'utf8' }); + } + try { - var res = FS.mmap(info, HEAPU8, start, num, offset, prot, flags); - ptr = res.ptr; - allocated = res.allocated; + var lib_module = eval(lib_data)( + Runtime.alignFunctionTables(), + Module + ); } catch (e) { - FS.handleFSError(e); - return -1; +#if ASSERTIONS + Module.printErr('Error in loading dynamic library: ' + e); +#endif + DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename; + return 0; } - } - _mmap.mappings[ptr] = { malloc: ptr, num: num, allocated: allocated, fd: fd, flags: flags }; - return ptr; - }, + // Not all browsers support Object.keys(). + var handle = 1; + for (var key in DLFCN.loadedLibs) { + if (DLFCN.loadedLibs.hasOwnProperty(key)) handle++; + } - munmap__deps: ['msync'], - munmap: function(start, num) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_MUNMAP') }}}, start, num); -#endif - if (!_mmap.mappings) _mmap.mappings = {}; - // TODO: support unmmap'ing parts of allocations - var info = _mmap.mappings[start]; - if (!info) return 0; - if (num == info.num) { - // At the Linux man page, it says: - // "The file may not actually be updated until msync(2) or munmap(2) are called." - // I guess that means we need to call msync when doing munmap - _msync(start, num); // todo: which flags? - - FS.munmap(FS.getStream(info.fd)); - - _mmap.mappings[start] = null; - if (info.allocated) { - _free(info.malloc); + // We don't care about RTLD_NOW and RTLD_LAZY. + if (flag & 256) { // RTLD_GLOBAL + for (var ident in lib_module) { + if (lib_module.hasOwnProperty(ident)) { + Module[ident] = lib_module[ident]; + } + } } - } - return 0; - }, - // TODO: Implement mremap. + var cached_functions = {}; + } + DLFCN.loadedLibs[handle] = { + refcount: 1, + name: filename, + module: lib_module, + cached_functions: cached_functions + }; + DLFCN.loadedLibNames[filename] = handle; - mprotect: function(addr, len, prot) { - // int mprotect(void *addr, size_t len, int prot); - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mprotect.html - // Pretend to succeed - return 0; + return handle; }, - - msync: function(addr, len, flags) { - // int msync(void *addr, size_t len, int flags); - // http://pubs.opengroup.org/onlinepubs/009696799/functions/msync.html - // TODO: support sync'ing parts of allocations - var info = _mmap.mappings[addr]; - if (!info) return 0; - if (len == info.num) { - var buffer = new Uint8Array(HEAPU8.buffer, addr, len); - return FS.msync(FS.getStream(info.fd), buffer, 0, len, info.flags); + // int dlclose(void* handle); + dlclose__deps: ['$DLFCN'], + dlclose: function(handle) { + // int dlclose(void *handle); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html + if (!DLFCN.loadedLibs[handle]) { + DLFCN.errorMsg = 'Tried to dlclose() unopened handle: ' + handle; + return 1; + } else { + var lib_record = DLFCN.loadedLibs[handle]; + if (--lib_record.refcount == 0) { + if (lib_record.module.cleanups) { + lib_record.module.cleanups.forEach(function(cleanup) { cleanup() }); + } + delete DLFCN.loadedLibNames[lib_record.name]; + delete DLFCN.loadedLibs[handle]; + } + return 0; } - - return 0; }, + // void* dlsym(void* handle, const char* symbol); + dlsym__deps: ['$DLFCN'], + dlsym: function(handle, symbol) { + // void *dlsym(void *restrict handle, const char *restrict name); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html + symbol = Pointer_stringify(symbol); - // ========================================================================== - // stdlib.h - // ========================================================================== - - // tiny, fake malloc/free implementation. If the program actually uses malloc, - // a compiled version will be used; this will only be used if the runtime - // needs to allocate something, for which this is good enough if otherwise - // no malloc is needed. - malloc: function(bytes) { - /* Over-allocate to make sure it is byte-aligned by 8. - * This will leak memory, but this is only the dummy - * implementation (replaced by dlmalloc normally) so - * not an issue. - */ -#if ASSERTIONS == 2 - Runtime.warnOnce('using stub malloc (reference it from C to have the real one included)'); -#endif - var ptr = Runtime.dynamicAlloc(bytes + 8); - return (ptr+8) & 0xFFFFFFF8; + if (!DLFCN.loadedLibs[handle]) { + DLFCN.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle; + return 0; + } else { + var lib = DLFCN.loadedLibs[handle]; + symbol = '_' + symbol; + if (lib.cached_functions.hasOwnProperty(symbol)) { + return lib.cached_functions[symbol]; + } + if (!lib.module.hasOwnProperty(symbol)) { + DLFCN.errorMsg = ('Tried to lookup unknown symbol "' + symbol + + '" in dynamic lib: ' + lib.name); + return 0; + } else { + var result = lib.module[symbol]; + if (typeof result == 'function') { + result = Runtime.addFunction(result); + lib.cached_functions = result; + } + return result; + } + } }, - free: function() { -#if ASSERTIONS == 2 - Runtime.warnOnce('using stub free (reference it from C to have the real one included)'); -#endif -}, - - calloc: function(n, s) { - var ret = _malloc(n*s); - _memset(ret, 0, n*s); - return ret; + // char* dlerror(void); + dlerror__deps: ['$DLFCN'], + dlerror: function() { + // char *dlerror(void); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlerror.html + if (DLFCN.errorMsg === null) { + return 0; + } else { + if (DLFCN.error) _free(DLFCN.error); + var msgArr = intArrayFromString(DLFCN.errorMsg); + DLFCN.error = allocate(msgArr, 'i8', ALLOC_NORMAL); + DLFCN.errorMsg = null; + return DLFCN.error; + } }, - abs: 'Math_abs', - labs: 'Math_abs', - llabs__deps: [function() { Types.preciseI64MathUsed = 1 }], - llabs: function(lo, hi) { - i64Math.abs(lo, hi); - {{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32')]) }}}; + dladdr: function(addr, info) { + // report all function pointers as coming from this program itself XXX not really correct in any way + var fname = allocate(intArrayFromString(Module['thisProgram'] || './this.program'), 'i8', ALLOC_NORMAL); // XXX leak + {{{ makeSetValue('addr', 0, 'fname', 'i32') }}}; + {{{ makeSetValue('addr', QUANTUM_SIZE, '0', 'i32') }}}; + {{{ makeSetValue('addr', QUANTUM_SIZE*2, '0', 'i32') }}}; + {{{ makeSetValue('addr', QUANTUM_SIZE*3, '0', 'i32') }}}; + return 1; }, - exit__deps: ['_exit'], - exit: function(status) { - __exit(status); - }, + // ========================================================================== + // pwd.h + // ========================================================================== - _ZSt9terminatev__deps: ['exit'], - _ZSt9terminatev: function() { - _exit(-1234); + // TODO: Implement. + // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/pwd.h.html + getpwuid: function(uid) { + return 0; // NULL }, - atexit: function(func, arg) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_ATEXIT') }}}, func, arg); -#endif - __ATEXIT__.unshift({ func: func, arg: arg }); - }, - __cxa_atexit: 'atexit', - abort: function() { - Module['abort'](); + // ========================================================================== + // time.h + // ========================================================================== + + clock: function() { + if (_clock.start === undefined) _clock.start = Date.now(); + return ((Date.now() - _clock.start) * ({{{ cDefine('CLOCKS_PER_SEC') }}} / 1000))|0; }, - realloc__deps: ['memcpy'], - realloc: function(ptr, size) { - // Very simple, inefficient implementation - if you use a real malloc, best to use - // a real realloc with it - if (!size) { - if (ptr) _free(ptr); - return 0; - } - var ret = _malloc(size); + time: function(ptr) { + var ret = (Date.now()/1000)|0; if (ptr) { - _memcpy(ret, ptr, size); // might be some invalid reads - _free(ptr); + {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}}; } return ret; }, - _parseInt__deps: ['isspace', '__setErrNo', '$ERRNO_CODES'], - _parseInt: function(str, endptr, base, min, max, bits, unsign) { - // Skip space. - while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; + difftime: function(time1, time0) { + return time1 - time0; + }, - // Check for a plus/minus sign. - var multiplier = 1; - if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) { - multiplier = -1; - str++; - } else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) { - str++; - } + // Statically allocated time struct. +#if USE_PTHREADS + __tm_current: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_current = PthreadWorkerInit.___tm_current; else PthreadWorkerInit.___tm_current = ___tm_current = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', + __tm_timezone: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_timezone = PthreadWorkerInit.___tm_timezone; else PthreadWorkerInit.___tm_timezone = ___tm_timezone = allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', + __tm_formatted: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_formatted = PthreadWorkerInit.___tm_formatted; else PthreadWorkerInit.___tm_formatted = ___tm_formatted = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', +#else + __tm_current: 'allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', + // Statically allocated copy of the string "GMT" for gmtime() to point to + __tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', + // Statically allocated time strings. + __tm_formatted: 'allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', +#endif + mktime__deps: ['tzset'], + mktime: function(tmPtr) { + _tzset(); + var date = new Date({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, + 0); - // Find base. - var finalBase = base; - if (!finalBase) { - if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { - if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || - {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { - finalBase = 16; - str += 2; - } else { - finalBase = 8; - str++; - } - } - } else if (finalBase==16) { - if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { - if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || - {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { - str += 2; - } - } + // There's an ambiguous hour when the time goes back; the tm_isdst field is + // used to disambiguate it. Date() basically guesses, so we fix it up if it + // guessed wrong, or fill in tm_isdst with the guess if it's -1. + var dst = {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'i32') }}}; + var guessedOffset = date.getTimezoneOffset(); + var start = new Date(date.getFullYear(), 0, 1); + var summerOffset = new Date(2000, 6, 1).getTimezoneOffset(); + var winterOffset = start.getTimezoneOffset(); + var dstOffset = Math.min(winterOffset, summerOffset); // DST is in December in South + if (dst < 0) { + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'Number(winterOffset != guessedOffset)', 'i32') }}}; + } else if ((dst > 0) != (winterOffset != guessedOffset)) { + var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset(); + var trueOffset = dst > 0 ? summerOffset : winterOffset; + // Don't try setMinutes(date.getMinutes() + ...) -- it's messed up. + date.setTime(date.getTime() + (trueOffset - guessedOffset)*60000); } - if (!finalBase) finalBase = 10; - // Get digits. - var chr; - var ret = 0; - while ((chr = {{{ makeGetValue('str', 0, 'i8') }}}) != 0) { - var digit = parseInt(String.fromCharCode(chr), finalBase); - if (isNaN(digit)) { - break; - } else { - ret = ret * finalBase + digit; - str++; - } - } + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}}; + var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; - // Apply sign. - ret *= multiplier; + return (date.getTime() / 1000)|0; + }, + timelocal: 'mktime', - // Set end pointer. - if (endptr) { - {{{ makeSetValue('endptr', 0, 'str', '*') }}}; - } + gmtime__deps: ['__tm_current', 'gmtime_r'], + gmtime: function(time) { + return _gmtime_r(time, ___tm_current); + }, - // Unsign if needed. - if (unsign) { - if (Math.abs(ret) > max) { - ret = max; - ___setErrNo(ERRNO_CODES.ERANGE); - } else { - ret = unSign(ret, bits); - } - } + gmtime_r__deps: ['__tm_timezone'], + gmtime_r: function(time, tmPtr) { + var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; + var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); + var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}}; - // Validate range. - if (ret > max || ret < min) { - ret = ret > max ? max : min; - ___setErrNo(ERRNO_CODES.ERANGE); - } + return tmPtr; + }, + timegm__deps: ['tzset'], + timegm: function(tmPtr) { + _tzset(); + var time = Date.UTC({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, + {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, + 0); + var date = new Date(time); - if (bits == 64) { - {{{ makeStructuralReturn(splitI64('ret')) }}}; - } + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}}; + var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); + var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; - return ret; + return (date.getTime() / 1000)|0; }, - _parseInt64__deps: ['isspace', '__setErrNo', '$ERRNO_CODES', function() { Types.preciseI64MathUsed = 1 }], - _parseInt64: function(str, endptr, base, min, max, unsign) { - var isNegative = false; - // Skip space. - while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; - - // Check for a plus/minus sign. - if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) { - str++; - isNegative = true; - } else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) { - str++; - } - // Find base. - var ok = false; - var finalBase = base; - if (!finalBase) { - if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { - if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || - {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { - finalBase = 16; - str += 2; - } else { - finalBase = 8; - ok = true; // we saw an initial zero, perhaps the entire thing is just "0" - } - } - } else if (finalBase==16) { - if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { - if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || - {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { - str += 2; - } - } - } - if (!finalBase) finalBase = 10; - var start = str; - - // Get digits. - var chr; - while ((chr = {{{ makeGetValue('str', 0, 'i8') }}}) != 0) { - var digit = parseInt(String.fromCharCode(chr), finalBase); - if (isNaN(digit)) { - break; - } else { - str++; - ok = true; - } - } + localtime__deps: ['__tm_current', 'localtime_r'], + localtime: function(time) { + return _localtime_r(time, ___tm_current); + }, - if (!ok) { - ___setErrNo(ERRNO_CODES.EINVAL); - {{{ makeStructuralReturn(['0', '0']) }}}; - } + localtime_r__deps: ['__tm_timezone', 'tzset'], + localtime_r: function(time, tmPtr) { + _tzset(); + var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}}; - // Set end pointer. - if (endptr) { - {{{ makeSetValue('endptr', 0, 'str', '*') }}}; - } + var start = new Date(date.getFullYear(), 0, 1); + var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '-(date.getTimezoneOffset() * 60)', 'i32') }}}; - try { - var numberString = isNegative ? '-'+Pointer_stringify(start, str - start) : Pointer_stringify(start, str - start); - i64Math.fromString(numberString, finalBase, min, max, unsign); - } catch(e) { - ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct - } + // DST is in December in South + var summerOffset = new Date(2000, 6, 1).getTimezoneOffset(); + var winterOffset = start.getTimezoneOffset(); + var dst = (date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}}; + + var zonePtr = {{{ makeGetValue(makeGlobalUse('_tzname'), 'dst ? Runtime.QUANTUM_SIZE : 0', 'i32') }}}; + {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, 'zonePtr', 'i32') }}}; - {{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32')]) }}}; + return tmPtr; }, - environ__deps: ['$ENV'], -#if USE_PTHREADS - environ: '; if (ENVIRONMENT_IS_PTHREAD) _environ = PthreadWorkerInit._environ; else PthreadWorkerInit._environ = _environ = allocate(1, "i32*", ALLOC_STATIC)', -#else - environ: 'allocate(1, "i32*", ALLOC_STATIC)', -#endif - __environ__deps: ['environ'], - __environ: 'environ', - __buildEnvironment__deps: ['__environ'], - __buildEnvironment: function(env) { - // WARNING: Arbitrary limit! - var MAX_ENV_VALUES = 64; - var TOTAL_ENV_SIZE = 1024; - // Statically allocate memory for the environment. - var poolPtr; - var envPtr; - if (!___buildEnvironment.called) { - ___buildEnvironment.called = true; - // Set default values. Use string keys for Closure Compiler compatibility. - ENV['USER'] = ENV['LOGNAME'] = 'web_user'; - ENV['PATH'] = '/'; - ENV['PWD'] = '/'; - ENV['HOME'] = '/home/web_user'; - ENV['LANG'] = 'C'; - ENV['_'] = Module['thisProgram']; - // Allocate memory. - poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC); - envPtr = allocate(MAX_ENV_VALUES * {{{ Runtime.QUANTUM_SIZE }}}, - 'i8*', ALLOC_STATIC); - {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}}; - {{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}}; - } else { - envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}}; - poolPtr = {{{ makeGetValue('envPtr', '0', 'i8*') }}}; - } - - // Collect key=value lines. - var strings = []; - var totalSize = 0; - for (var key in env) { - if (typeof env[key] === 'string') { - var line = key + '=' + env[key]; - strings.push(line); - totalSize += line.length; - } - } - if (totalSize > TOTAL_ENV_SIZE) { - throw new Error('Environment size exceeded TOTAL_ENV_SIZE!'); - } + asctime__deps: ['__tm_formatted', 'asctime_r'], + asctime: function(tmPtr) { + return _asctime_r(tmPtr, ___tm_formatted); + }, - // Make new. - var ptrSize = {{{ Runtime.getNativeTypeSize('i8*') }}}; - for (var i = 0; i < strings.length; i++) { - var line = strings[i]; - writeAsciiToMemory(line, poolPtr); - {{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}}; - poolPtr += line.length + 1; - } - {{{ makeSetValue('envPtr', 'strings.length * ptrSize', '0', 'i8*') }}}; + asctime_r__deps: ['__tm_formatted', 'mktime'], + asctime_r: function(tmPtr, buf) { + var date = { + tm_sec: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, + tm_min: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, + tm_hour: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, + tm_mday: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, + tm_mon: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, + tm_year: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}}, + tm_wday: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'i32') }}} + }; + var days = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]; + var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; + var s = days[date.tm_wday] + ' ' + months[date.tm_mon] + + (date.tm_mday < 10 ? ' ' : ' ') + date.tm_mday + + (date.tm_hour < 10 ? ' 0' : ' ') + date.tm_hour + + (date.tm_min < 10 ? ':0' : ':') + date.tm_min + + (date.tm_sec < 10 ? ':0' : ':') + date.tm_sec + + ' ' + (1900 + date.tm_year) + "\n"; + writeStringToMemory(s, buf); + return buf; }, - $ENV__deps: ['__buildEnvironment'], -#if USE_PTHREADS - $ENV__postset: 'if (!ENVIRONMENT_IS_PTHREAD) ___buildEnvironment(ENV);', -#else - $ENV__postset: '___buildEnvironment(ENV);', -#endif - $ENV: {}, - getenv__deps: ['$ENV'], - getenv: function(name) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_GETENV') }}}, name); -#endif - // char *getenv(const char *name); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/getenv.html - if (name === 0) return 0; - name = Pointer_stringify(name); - if (!ENV.hasOwnProperty(name)) return 0; - if (_getenv.ret) _free(_getenv.ret); - _getenv.ret = allocate(intArrayFromString(ENV[name]), 'i8', ALLOC_NORMAL); - return _getenv.ret; + ctime__deps: ['__tm_current', 'ctime_r'], + ctime: function(timer) { + return _ctime_r(timer, ___tm_current); }, - clearenv__deps: ['$ENV', '__buildEnvironment'], - clearenv: function(name) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CLEARENV') }}}, name); -#endif - // int clearenv (void); - // http://www.gnu.org/s/hello/manual/libc/Environment-Access.html#index-clearenv-3107 - ENV = {}; - ___buildEnvironment(ENV); - return 0; + + ctime_r__deps: ['localtime_r', 'asctime_r'], + ctime_r: function(time, buf) { + var stack = Runtime.stackSave(); + var rv = _asctime_r(_localtime_r(time, Runtime.stackAlloc({{{ C_STRUCTS.tm.__size__ }}})), buf); + Runtime.stackRestore(stack); + return rv; }, - setenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], - setenv: function(envname, envval, overwrite) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_SETENV') }}}, envname, envval, overwrite); -#endif - // int setenv(const char *envname, const char *envval, int overwrite); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/setenv.html - if (envname === 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - var name = Pointer_stringify(envname); - var val = Pointer_stringify(envval); - if (name === '' || name.indexOf('=') !== -1) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - if (ENV.hasOwnProperty(name) && !overwrite) return 0; - ENV[name] = val; - ___buildEnvironment(ENV); - return 0; + + dysize: function(year) { + var leap = ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))); + return leap ? 366 : 365; }, - unsetenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], - unsetenv: function(name) { + + // TODO: Initialize these to defaults on startup from system settings. + // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_UNSETENV') }}}, name); + tzname: '; if (ENVIRONMENT_IS_PTHREAD) _tzname = PthreadWorkerInit._tzname; else PthreadWorkerInit._tzname = _tzname = allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)', + daylight: '; if (ENVIRONMENT_IS_PTHREAD) _daylight = PthreadWorkerInit._daylight; else PthreadWorkerInit._daylight = _daylight = allocate(1, "i32*", ALLOC_STATIC)', + timezone: '; if (ENVIRONMENT_IS_PTHREAD) _timezone = PthreadWorkerInit._timezone; else PthreadWorkerInit._timezone = _timezone = allocate(1, "i32*", ALLOC_STATIC)', +#else + tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)', + daylight: 'allocate(1, "i32*", ALLOC_STATIC)', + timezone: 'allocate(1, "i32*", ALLOC_STATIC)', #endif - // int unsetenv(const char *name); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/unsetenv.html - if (name === 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - name = Pointer_stringify(name); - if (name === '' || name.indexOf('=') !== -1) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - if (ENV.hasOwnProperty(name)) { - delete ENV[name]; - ___buildEnvironment(ENV); - } - return 0; - }, - putenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], - putenv: function(string) { + tzset__deps: ['tzname', 'daylight', 'timezone'], + tzset: function() { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_PUTENV') }}}, string); + if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_0({{{ cDefine('EM_PROXIED_TZSET') }}}); #endif - // int putenv(char *string); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/putenv.html - // WARNING: According to the standard (and the glibc implementation), the - // string is taken by reference so future changes are reflected. - // We copy it instead, possibly breaking some uses. - if (string === 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - string = Pointer_stringify(string); - var splitPoint = string.indexOf('=') - if (string === '' || string.indexOf('=') === -1) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - var name = string.slice(0, splitPoint); - var value = string.slice(splitPoint + 1); - if (!(name in ENV) || ENV[name] !== value) { - ENV[name] = value; - ___buildEnvironment(ENV); - } - return 0; - }, + // TODO: Use (malleable) environment variables instead of system settings. + if (_tzset.called) return; + _tzset.called = true; - getloadavg: function(loadavg, nelem) { - // int getloadavg(double loadavg[], int nelem); - // http://linux.die.net/man/3/getloadavg - var limit = Math.min(nelem, 3); - var doubleSize = {{{ Runtime.getNativeTypeSize('double') }}}; - for (var i = 0; i < limit; i++) { - {{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}}; - } - return limit; - }, + {{{ makeSetValue(makeGlobalUse('_timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}}; - realpath__deps: ['$FS', '__setErrNo'], - realpath: function(file_name, resolved_name) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_REALPATH') }}}, file_name, resolved_name); -#endif - // char *realpath(const char *restrict file_name, char *restrict resolved_name); - // http://pubs.opengroup.org/onlinepubs/009604499/functions/realpath.html - var absolute = FS.analyzePath(Pointer_stringify(file_name)); - if (absolute.error) { - ___setErrNo(absolute.error); - return 0; + var winter = new Date(2000, 0, 1); + var summer = new Date(2000, 6, 1); + {{{ makeSetValue(makeGlobalUse('_daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}}; + + function extractZone(date) { + var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/); + return match ? match[1] : "GMT"; + }; + var winterName = extractZone(winter); + var summerName = extractZone(summer); + var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); + var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); + if (summer.getTimezoneOffset() < winter.getTimezoneOffset()) { + // Northern hemisphere + {{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'winterNamePtr', 'i32') }}}; + {{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}}; } else { - var size = Math.min(4095, absolute.path.length); // PATH_MAX - 1. - if (resolved_name === 0) resolved_name = _malloc(size+1); - for (var i = 0; i < size; i++) { - {{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}}; - } - {{{ makeSetValue('resolved_name', 'size', '0', 'i8') }}}; - return resolved_name; + {{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'summerNamePtr', 'i32') }}}; + {{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'winterNamePtr', 'i32') }}}; } }, - // For compatibility, call to rand() when code requests arc4random(), although this is *not* at all - // as strong as rc4 is. See https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/arc4random.3.html - arc4random: 'rand', + stime__deps: ['$ERRNO_CODES', '__setErrNo'], + stime: function(when) { + ___setErrNo(ERRNO_CODES.EPERM); + return -1; + }, - // ========================================================================== - // string.h - // ========================================================================== + _MONTH_DAYS_REGULAR: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + _MONTH_DAYS_LEAP: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], - memcpy__inline: function(dest, src, num, align) { - var ret = ''; - ret += makeCopyValues(dest, src, num, 'null', null, align); - return ret; + _isLeapYear: function(year) { + return year%4 === 0 && (year%100 !== 0 || year%400 === 0); }, - emscripten_memcpy_big: function(dest, src, num) { - HEAPU8.set(HEAPU8.subarray(src, src+num), dest); - return dest; + _arraySum: function(array, index) { + var sum = 0; + for (var i = 0; i <= index; sum += array[i++]); + return sum; }, - memcpy__asm: true, - memcpy__sig: 'iiii', - memcpy__deps: ['emscripten_memcpy_big'], - memcpy: function(dest, src, num) { - dest = dest|0; src = src|0; num = num|0; - var ret = 0; - if ((num|0) >= 4096) return _emscripten_memcpy_big(dest|0, src|0, num|0)|0; - ret = dest|0; - if ((dest&3) == (src&3)) { - while (dest & 3) { - if ((num|0) == 0) return ret|0; - {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; - dest = (dest+1)|0; - src = (src+1)|0; - num = (num-1)|0; - } - while ((num|0) >= 4) { - {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i32'), 'i32') }}}; - dest = (dest+4)|0; - src = (src+4)|0; - num = (num-4)|0; - } - } - while ((num|0) > 0) { - {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; - dest = (dest+1)|0; - src = (src+1)|0; - num = (num-1)|0; - } - return ret|0; - }, - - llvm_memcpy_i32: 'memcpy', - llvm_memcpy_i64: 'memcpy', - llvm_memcpy_p0i8_p0i8_i32: 'memcpy', - llvm_memcpy_p0i8_p0i8_i64: 'memcpy', - - memmove__sig: 'iiii', - memmove__asm: true, - memmove__deps: ['memcpy'], - memmove: function(dest, src, num) { - dest = dest|0; src = src|0; num = num|0; - var ret = 0; - if (((src|0) < (dest|0)) & ((dest|0) < ((src + num)|0))) { - // Unlikely case: Copy backwards in a safe manner - ret = dest; - src = (src + num)|0; - dest = (dest + num)|0; - while ((num|0) > 0) { - dest = (dest - 1)|0; - src = (src - 1)|0; - num = (num - 1)|0; - {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; - } - dest = ret; - } else { - _memcpy(dest, src, num) | 0; - } - return dest | 0; - }, - llvm_memmove_i32: 'memmove', - llvm_memmove_i64: 'memmove', - llvm_memmove_p0i8_p0i8_i32: 'memmove', - llvm_memmove_p0i8_p0i8_i64: 'memmove', + _addDays__deps: ['_isLeapYear', '_MONTH_DAYS_LEAP', '_MONTH_DAYS_REGULAR'], + _addDays: function(date, days) { + var newDate = new Date(date.getTime()); + while(days > 0) { + var leap = __isLeapYear(newDate.getFullYear()); + var currentMonth = newDate.getMonth(); + var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth]; - memset__inline: function(ptr, value, num, align) { - return makeSetValues(ptr, 0, value, 'null', num, align); - }, - memset__sig: 'iiii', - memset__asm: true, - memset: function(ptr, value, num) { - ptr = ptr|0; value = value|0; num = num|0; - var stop = 0, value4 = 0, stop4 = 0, unaligned = 0; - stop = (ptr + num)|0; - if ((num|0) >= {{{ Math.round(2.5*UNROLL_LOOP_MAX) }}}) { - // This is unaligned, but quite large, so work hard to get to aligned settings - value = value & 0xff; - unaligned = ptr & 3; - value4 = value | (value << 8) | (value << 16) | (value << 24); - stop4 = stop & ~3; - if (unaligned) { - unaligned = (ptr + 4 - unaligned)|0; - while ((ptr|0) < (unaligned|0)) { // no need to check for stop, since we have large num - {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; - ptr = (ptr+1)|0; + if (days > daysInCurrentMonth-newDate.getDate()) { + // we spill over to next month + days -= (daysInCurrentMonth-newDate.getDate()+1); + newDate.setDate(1); + if (currentMonth < 11) { + newDate.setMonth(currentMonth+1) + } else { + newDate.setMonth(0); + newDate.setFullYear(newDate.getFullYear()+1); } - } - while ((ptr|0) < (stop4|0)) { - {{{ makeSetValueAsm('ptr', 0, 'value4', 'i32') }}}; - ptr = (ptr+4)|0; - } - } - while ((ptr|0) < (stop|0)) { - {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; - ptr = (ptr+1)|0; - } - return (ptr-num)|0; - }, - llvm_memset_i32: 'memset', - llvm_memset_p0i8_i32: 'memset', - llvm_memset_p0i8_i64: 'memset', - - strerror_r__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo'], - strerror_r: function(errnum, strerrbuf, buflen) { - if (errnum in ERRNO_MESSAGES) { - if (ERRNO_MESSAGES[errnum].length > buflen - 1) { - return ___setErrNo(ERRNO_CODES.ERANGE); } else { - var msg = ERRNO_MESSAGES[errnum]; - writeAsciiToMemory(msg, strerrbuf); - return 0; + // we stay in current month + newDate.setDate(newDate.getDate()+days); + return newDate; } - } else { - return ___setErrNo(ERRNO_CODES.EINVAL); } - }, - strerror__deps: ['strerror_r'], - strerror: function(errnum) { - if (!_strerror.buffer) _strerror.buffer = _malloc(256); - _strerror_r(errnum, _strerror.buffer, 256); - return _strerror.buffer; - }, - - // ========================================================================== - // ctype.h - // ========================================================================== - // Lookup tables for glibc ctype implementation. - __ctype_b_loc: function() { - // http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html - var me = ___ctype_b_loc; - if (!me.ret) { - var values = [ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,8195,8194,8194,8194,8194,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,24577,49156,49156,49156, - 49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,55304,55304,55304,55304,55304,55304,55304,55304, - 55304,55304,49156,49156,49156,49156,49156,49156,49156,54536,54536,54536,54536,54536,54536,50440,50440,50440,50440,50440, - 50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,49156,49156,49156,49156,49156, - 49156,54792,54792,54792,54792,54792,54792,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696, - 50696,50696,50696,50696,50696,50696,50696,49156,49156,49156,49156,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - ]; - var i16size = {{{ Runtime.getNativeTypeSize('i16') }}}; - var arr = _malloc(values.length * i16size); - for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i16size', 'values[i]', 'i16') }}}; - } - me.ret = allocate([arr + 128 * i16size], 'i16*', ALLOC_NORMAL); - } - return me.ret; - }, - __ctype_tolower_loc: function() { - // http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/libutil---ctype-tolower-loc.html - var me = ___ctype_tolower_loc; - if (!me.ret) { - var values = [ - 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, - 158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187, - 188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217, - 218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, - 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95,96,97,98,99,100,101,102,103, - 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133, - 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163, - 164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193, - 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, - 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, - 254,255 - ]; - var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; - var arr = _malloc(values.length * i32size); - for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}}; - } - me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); - } - return me.ret; - }, - __ctype_toupper_loc: function() { - // http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/libutil---ctype-toupper-loc.html - var me = ___ctype_toupper_loc; - if (!me.ret) { - var values = [ - 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, - 158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187, - 188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217, - 218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, - 248,249,250,251,252,253,254,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, - 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72, - 73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, - 81,82,83,84,85,86,87,88,89,90,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144, - 145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, - 175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204, - 205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234, - 235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 - ]; - var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; - var arr = _malloc(values.length * i32size); - for (var i = 0; i < values.length; i++) { - {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}}; - } - me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); - } - return me.ret; + return newDate; }, - // ========================================================================== - // GCC/LLVM specifics - // ========================================================================== - __builtin_prefetch: function(){}, + strftime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], + strftime: function(s, maxsize, format, tm) { + // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html - // ========================================================================== - // LLVM specifics - // ========================================================================== + var tm_zone = {{{ makeGetValue('tm', C_STRUCTS.tm.tm_zone, 'i32') }}}; - llvm_va_start__inline: function(ptr) { - // varargs - we received a pointer to the varargs as a final 'extra' parameter called 'varrp' - // 2-word structure: struct { void* start; void* currentOffset; } - return makeSetValue(ptr, 0, 'varrp', 'void*') + ';' + makeSetValue(ptr, Runtime.QUANTUM_SIZE, 0, 'void*'); - }, + var date = { + tm_sec: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32') }}}, + tm_min: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32') }}}, + tm_hour: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32') }}}, + tm_mday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32') }}}, + tm_mon: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32') }}}, + tm_year: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32') }}}, + tm_wday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_wday, 'i32') }}}, + tm_yday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_yday, 'i32') }}}, + tm_isdst: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_isdst, 'i32') }}}, + tm_gmtoff: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_gmtoff, 'i32') }}}, + tm_zone: tm_zone ? Pointer_stringify(tm_zone) : '' + }; - llvm_va_end: function() {}, + var pattern = Pointer_stringify(format); - llvm_va_copy: function(ppdest, ppsrc) { - // copy the list start - {{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; - - // copy the list's current offset (will be advanced with each call to va_arg) - {{{ makeCopyValues('(ppdest+'+Runtime.QUANTUM_SIZE+')', '(ppsrc+'+Runtime.QUANTUM_SIZE+')', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; - }, + // expand format + var EXPANSION_RULES_1 = { + '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013 + '%D': '%m/%d/%y', // Equivalent to %m / %d / %y + '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d + '%h': '%b', // Equivalent to %b + '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation + '%R': '%H:%M', // Replaced by the time in 24-hour notation + '%T': '%H:%M:%S', // Replaced by the time + '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation + '%X': '%H:%M:%S' // Replaced by the locale's appropriate date representation + }; + for (var rule in EXPANSION_RULES_1) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); + } - llvm_bswap_i16__asm: true, - llvm_bswap_i16__sig: 'ii', - llvm_bswap_i16: function(x) { - x = x|0; - return (((x&0xff)<<8) | ((x>>8)&0xff))|0; - }, + var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - llvm_bswap_i32__asm: true, - llvm_bswap_i32__sig: 'ii', - llvm_bswap_i32: function(x) { - x = x|0; - return (((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24))|0; - }, + function leadingSomething(value, digits, character) { + var str = typeof value === 'number' ? value.toString() : (value || ''); + while (str.length < digits) { + str = character[0]+str; + } + return str; + }; - llvm_bswap_i64__deps: ['llvm_bswap_i32'], - llvm_bswap_i64: function(l, h) { - var retl = _llvm_bswap_i32(h)>>>0; - var reth = _llvm_bswap_i32(l)>>>0; - {{{ makeStructuralReturn(['retl', 'reth']) }}}; - }, + function leadingNulls(value, digits) { + return leadingSomething(value, digits, '0'); + }; - llvm_ctlz_i64__asm: true, - llvm_ctlz_i64__sig: 'iii', - llvm_ctlz_i64: function(l, h, isZeroUndef) { - l = l | 0; - h = h | 0; - isZeroUndef = isZeroUndef | 0; - var ret = 0; - ret = Math_clz32(h) | 0; - if ((ret | 0) == 32) ret = ret + (Math_clz32(l) | 0) | 0; - {{{ makeSetTempRet0('0') }}}; - return ret | 0; - }, + function compareByDay(date1, date2) { + function sgn(value) { + return value < 0 ? -1 : (value > 0 ? 1 : 0); + }; - llvm_cttz_i32__deps: [function() { - function cttz(x) { - for (var i = 0; i < 8; i++) { - if (x & (1 << i)) { - return i; + var compare; + if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) { + if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) { + compare = sgn(date1.getDate()-date2.getDate()); } } - return 8; - } - if (SIDE_MODULE) return ''; // uses it from the parent + return compare; + }; -#if USE_PTHREADS - return 'var cttz_i8; if (ENVIRONMENT_IS_PTHREAD) cttz_i8 = PthreadWorkerInit.cttz_i8; else PthreadWorkerInit.cttz_i8 = cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);'; -#else - return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);'; -#endif - }], - llvm_cttz_i32__asm: true, - llvm_cttz_i32__sig: 'ii', - llvm_cttz_i32: function(x) { - x = x|0; - var ret = 0; - ret = {{{ makeGetValueAsm('cttz_i8', 'x & 0xff', 'i8') }}}; - if ((ret|0) < 8) return ret|0; - ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 8)&0xff', 'i8') }}}; - if ((ret|0) < 8) return (ret + 8)|0; - ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 16)&0xff', 'i8') }}}; - if ((ret|0) < 8) return (ret + 16)|0; - return ({{{ makeGetValueAsm('cttz_i8', 'x >>> 24', 'i8') }}} + 24)|0; - }, + function getFirstWeekStartDate(janFourth) { + switch (janFourth.getDay()) { + case 0: // Sunday + return new Date(janFourth.getFullYear()-1, 11, 29); + case 1: // Monday + return janFourth; + case 2: // Tuesday + return new Date(janFourth.getFullYear(), 0, 3); + case 3: // Wednesday + return new Date(janFourth.getFullYear(), 0, 2); + case 4: // Thursday + return new Date(janFourth.getFullYear(), 0, 1); + case 5: // Friday + return new Date(janFourth.getFullYear()-1, 11, 31); + case 6: // Saturday + return new Date(janFourth.getFullYear()-1, 11, 30); + } + }; - llvm_cttz_i64__deps: ['llvm_cttz_i32'], - llvm_cttz_i64: function(l, h) { - var ret = _llvm_cttz_i32(l); - if (ret == 32) ret += _llvm_cttz_i32(h); - {{{ makeStructuralReturn(['ret', '0']) }}}; - }, + function getWeekBasedYear(date) { + var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); - llvm_ctpop_i32: function(x) { - var ret = 0; - while (x) { - if (x&1) ret++; - x >>>= 1; - } - return ret; - }, + var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); + var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4); - llvm_ctpop_i64__deps: ['llvm_ctpop_i32'], - llvm_ctpop_i64: function(l, h) { - return _llvm_ctpop_i32(l) + _llvm_ctpop_i32(h); - }, + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); - llvm_trap: function() { - abort('trap!'); - }, + if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { + // this date is after the start of the first week of this year + if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { + return thisDate.getFullYear()+1; + } else { + return thisDate.getFullYear(); + } + } else { + return thisDate.getFullYear()-1; + } + }; - llvm_prefetch: function(){}, + var EXPANSION_RULES_2 = { + '%a': function(date) { + return WEEKDAYS[date.tm_wday].substring(0,3); + }, + '%A': function(date) { + return WEEKDAYS[date.tm_wday]; + }, + '%b': function(date) { + return MONTHS[date.tm_mon].substring(0,3); + }, + '%B': function(date) { + return MONTHS[date.tm_mon]; + }, + '%C': function(date) { + var year = date.tm_year+1900; + return leadingNulls((year/100)|0,2); + }, + '%d': function(date) { + return leadingNulls(date.tm_mday, 2); + }, + '%e': function(date) { + return leadingSomething(date.tm_mday, 2, ' '); + }, + '%g': function(date) { + // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. + // In this system, weeks begin on a Monday and week 1 of the year is the week that includes + // January 4th, which is also the week that includes the first Thursday of the year, and + // is also the first week that contains at least four days in the year. + // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of + // the last week of the preceding year; thus, for Saturday 2nd January 1999, + // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, + // or 31st is a Monday, it and any following days are part of week 1 of the following year. + // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. + + return getWeekBasedYear(date).toString().substring(2); + }, + '%G': function(date) { + return getWeekBasedYear(date); + }, + '%H': function(date) { + return leadingNulls(date.tm_hour, 2); + }, + '%I': function(date) { + return leadingNulls(date.tm_hour < 13 ? date.tm_hour : date.tm_hour-12, 2); + }, + '%j': function(date) { + // Day of the year (001-366) + return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3); + }, + '%m': function(date) { + return leadingNulls(date.tm_mon+1, 2); + }, + '%M': function(date) { + return leadingNulls(date.tm_min, 2); + }, + '%n': function() { + return '\n'; + }, + '%p': function(date) { + if (date.tm_hour > 0 && date.tm_hour < 13) { + return 'AM'; + } else { + return 'PM'; + } + }, + '%S': function(date) { + return leadingNulls(date.tm_sec, 2); + }, + '%t': function() { + return '\t'; + }, + '%u': function(date) { + var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0); + return day.getDay() || 7; + }, + '%U': function(date) { + // Replaced by the week number of the year as a decimal number [00,53]. + // The first Sunday of January is the first day of week 1; + // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + var janFirst = new Date(date.tm_year+1900, 0, 1); + var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay()); + var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); + + // is target date after the first Sunday? + if (compareByDay(firstSunday, endDate) < 0) { + // calculate difference in days between first Sunday and endDate + var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; + var firstSundayUntilEndJanuary = 31-firstSunday.getDate(); + var days = firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); + return leadingNulls(Math.ceil(days/7), 2); + } - __assert_fail: function(condition, filename, line, func) { - ABORT = true; - throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace(); - }, + return compareByDay(firstSunday, janFirst) === 0 ? '01': '00'; + }, + '%V': function(date) { + // Replaced by the week number of the year (Monday as the first day of the week) + // as a decimal number [01,53]. If the week containing 1 January has four + // or more days in the new year, then it is considered week 1. + // Otherwise, it is the last week of the previous year, and the next week is week 1. + // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] + var janFourthThisYear = new Date(date.tm_year+1900, 0, 4); + var janFourthNextYear = new Date(date.tm_year+1901, 0, 4); - __assert_func: function(filename, line, func, condition) { - throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace(); - }, + var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); + var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); - __cxa_guard_acquire: function(variable) { - if (!{{{ makeGetValue(0, 'variable', 'i8', null, null, 1) }}}) { // ignore SAFE_HEAP stuff because llvm mixes i64 and i8 here - {{{ makeSetValue(0, 'variable', '1', 'i8') }}}; - return 1; - } - return 0; - }, - __cxa_guard_release: function() {}, - __cxa_guard_abort: function() {}, + var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); - $EXCEPTIONS: { - last: 0, - caught: [], - infos: {}, - deAdjust: function(adjusted) { - if (!adjusted || EXCEPTIONS.infos[adjusted]) return adjusted; - for (var ptr in EXCEPTIONS.infos) { - var info = EXCEPTIONS.infos[ptr]; - if (info.adjusted === adjusted) { -#if EXCEPTION_DEBUG - Module.printErr('de-adjusted exception ptr ' + adjusted + ' to ' + ptr); -#endif - return ptr; - } - } -#if EXCEPTION_DEBUG - Module.printErr('no de-adjustment for unknown exception ptr ' + adjusted); -#endif - return adjusted; - }, - addRef: function(ptr) { -#if EXCEPTION_DEBUG - Module.printErr('addref ' + ptr); -#endif - if (!ptr) return; - var info = EXCEPTIONS.infos[ptr]; - info.refcount++; - }, - decRef: function(ptr) { -#if EXCEPTION_DEBUG - Module.printErr('decref ' + ptr); -#endif - if (!ptr) return; - var info = EXCEPTIONS.infos[ptr]; - assert(info.refcount > 0); - info.refcount--; - if (info.refcount === 0) { - if (info.destructor) { - Runtime.dynCall('vi', info.destructor, [ptr]); + if (compareByDay(endDate, firstWeekStartThisYear) < 0) { + // if given date is before this years first week, then it belongs to the 53rd week of last year + return '53'; + } + + if (compareByDay(firstWeekStartNextYear, endDate) <= 0) { + // if given date is after next years first week, then it belongs to the 01th week of next year + return '01'; } - delete EXCEPTIONS.infos[ptr]; - ___cxa_free_exception(ptr); -#if EXCEPTION_DEBUG - Module.printErr('decref freeing exception ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); -#endif - } - }, - clearRef: function(ptr) { - if (!ptr) return; - var info = EXCEPTIONS.infos[ptr]; - info.refcount = 0; - }, - }, - // Exceptions - __cxa_allocate_exception__deps: ['malloc'], - __cxa_allocate_exception: function(size) { - return _malloc(size); - }, - __cxa_free_exception__deps: ['free'], - __cxa_free_exception: function(ptr) { - try { - return _free(ptr); - } catch(e) { // XXX FIXME -#if ASSERTIONS - Module.printErr('exception during cxa_free_exception: ' + e); -#endif - } - }, - __cxa_increment_exception_refcount__deps: ['$EXCEPTIONS'], - __cxa_increment_exception_refcount: function(ptr) { - EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr)); - }, - __cxa_decrement_exception_refcount__deps: ['$EXCEPTIONS'], - __cxa_decrement_exception_refcount: function(ptr) { - EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr)); - }, - // Here, we throw an exception after recording a couple of values that we need to remember - // We also remember that it was the last exception thrown as we need to know that later. - __cxa_throw__sig: 'viii', - __cxa_throw__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch', '$EXCEPTIONS'], - __cxa_throw: function(ptr, type, destructor) { -#if EXCEPTION_DEBUG - Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor]); -#endif - EXCEPTIONS.infos[ptr] = { - ptr: ptr, - adjusted: ptr, - type: type, - destructor: destructor, - refcount: 0 + // given date is in between CW 01..53 of this calendar year + var daysDifference; + if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) { + // first CW of this year starts last year + daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate() + } else { + // first CW of this year starts this year + daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate(); + } + return leadingNulls(Math.ceil(daysDifference/7), 2); + }, + '%w': function(date) { + var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0); + return day.getDay(); + }, + '%W': function(date) { + // Replaced by the week number of the year as a decimal number [00,53]. + // The first Monday of January is the first day of week 1; + // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] + var janFirst = new Date(date.tm_year, 0, 1); + var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1); + var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); + + // is target date after the first Monday? + if (compareByDay(firstMonday, endDate) < 0) { + var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; + var firstMondayUntilEndJanuary = 31-firstMonday.getDate(); + var days = firstMondayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); + return leadingNulls(Math.ceil(days/7), 2); + } + return compareByDay(firstMonday, janFirst) === 0 ? '01': '00'; + }, + '%y': function(date) { + // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] + return (date.tm_year+1900).toString().substring(2); + }, + '%Y': function(date) { + // Replaced by the year as a decimal number (for example, 1997). [ tm_year] + return date.tm_year+1900; + }, + '%z': function(date) { + // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ). + // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). + var off = date.tm_gmtoff; + var ahead = off >= 0; + off = Math.abs(off) / 60; + // convert from minutes into hhmm format (which means 60 minutes = 100 units) + off = (off / 60)*100 + (off % 60); + return (ahead ? '+' : '-') + String("0000" + off).slice(-4); + }, + '%Z': function(date) { + return date.tm_zone; + }, + '%%': function() { + return '%'; + } }; - EXCEPTIONS.last = ptr; - if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { - __ZSt18uncaught_exceptionv.uncaught_exception = 1; - } else { - __ZSt18uncaught_exceptionv.uncaught_exception++; - } - {{{ makeThrow('ptr') }}} - }, - // This exception will be caught twice, but while begin_catch runs twice, - // we early-exit from end_catch when the exception has been rethrown, so - // pop that here from the caught exceptions. - __cxa_rethrow__deps: ['__cxa_end_catch', '$EXCEPTIONS'], - __cxa_rethrow: function() { - ___cxa_end_catch.rethrown = true; - var ptr = EXCEPTIONS.caught.pop(); -#if EXCEPTION_DEBUG - Module.printErr('Compiled code RE-throwing an exception, popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); -#endif - EXCEPTIONS.last = ptr; - {{{ makeThrow('ptr') }}} - }, - llvm_eh_exception__deps: ['$EXCEPTIONS'], - llvm_eh_exception: function() { - return EXCEPTIONS.last; - }, - llvm_eh_selector__jsargs: true, - llvm_eh_selector__deps: ['$EXCEPTIONS'], - llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) { - var type = EXCEPTIONS.last; - for (var i = 2; i < arguments.length; i++) { - if (arguments[i] == type) return type; - } - return 0; - }, - llvm_eh_typeid_for: function(type) { - return type; - }, - __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv', '$EXCEPTIONS'], - __cxa_begin_catch: function(ptr) { - __ZSt18uncaught_exceptionv.uncaught_exception--; - EXCEPTIONS.caught.push(ptr); -#if EXCEPTION_DEBUG - Module.printErr('cxa_begin_catch ' + [ptr, 'stack', EXCEPTIONS.caught]); -#endif - EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr)); - return ptr; - }, - // We're done with a catch. Now, we can run the destructor if there is one - // and free the exception. Note that if the dynCall on the destructor fails - // due to calling apply on undefined, that means that the destructor is - // an invalid index into the FUNCTION_TABLE, so something has gone wrong. - __cxa_end_catch__deps: ['__cxa_free_exception', '$EXCEPTIONS'], - __cxa_end_catch: function() { - if (___cxa_end_catch.rethrown) { - ___cxa_end_catch.rethrown = false; - return; - } - // Clear state flag. - asm['setThrew'](0); - // Call destructor if one is registered then clear it. - var ptr = EXCEPTIONS.caught.pop(); -#if EXCEPTION_DEBUG - Module.printErr('cxa_end_catch popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]); -#endif - if (ptr) { - EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr)); - EXCEPTIONS.last = 0; // XXX in decRef? + for (var rule in EXPANSION_RULES_2) { + if (pattern.indexOf(rule) >= 0) { + pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); + } } - }, - __cxa_get_exception_ptr: function(ptr) { -#if EXCEPTION_DEBUG - Module.printErr('cxa_get_exception_ptr ' + ptr); -#endif - // TODO: use info.adjusted? - return ptr; - }, - _ZSt18uncaught_exceptionv: function() { // std::uncaught_exception() - return !!__ZSt18uncaught_exceptionv.uncaught_exception; - }, - __cxa_uncaught_exception__deps: ['_ZSt18uncaught_exceptionv'], - __cxa_uncaught_exception: function() { - return !!__ZSt18uncaught_exceptionv.uncaught_exception; - }, - - __cxa_call_unexpected: function(exception) { - Module.printErr('Unexpected exception thrown, this is not properly supported - aborting'); - ABORT = true; - throw exception; - }, - __cxa_current_primary_exception: function() { - var ret = EXCEPTIONS.caught[EXCEPTIONS.caught.length-1] || 0; - if (ret) EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ret)); - return ret; - }, + var bytes = intArrayFromString(pattern, false); + if (bytes.length > maxsize) { + return 0; + } - __cxa_rethrow_primary_exception__deps: ['__cxa_rethrow'], - __cxa_rethrow_primary_exception: function(ptr) { - if (!ptr) return; - EXCEPTIONS.caught.push(ptr); - ___cxa_rethrow(); + writeArrayToMemory(bytes, s); + return bytes.length-1; }, - - terminate: '__cxa_call_unexpected', - - __gxx_personality_v0__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], - __gxx_personality_v0: function() { + strftime_l__deps: ['strftime'], + strftime_l: function(s, maxsize, format, tm) { + return _strftime(s, maxsize, format, tm); // no locale support yet }, - // Finds a suitable catch clause for when an exception is thrown. - // In normal compilers, this functionality is handled by the C++ - // 'personality' routine. This is passed a fairly complex structure - // relating to the context of the exception and makes judgements - // about how to handle it. Some of it is about matching a suitable - // catch clause, and some of it is about unwinding. We already handle - // unwinding using 'if' blocks around each function, so the remaining - // functionality boils down to picking a suitable 'catch' block. - // We'll do that here, instead, to keep things simpler. + strptime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], + strptime: function(buf, format, tm) { + // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html + var pattern = Pointer_stringify(format); - __cxa_find_matching_catch__deps: ['__resumeException', '$EXCEPTIONS'], - __cxa_find_matching_catch: function() { - var thrown = EXCEPTIONS.last; - if (!thrown) { - // just pass through the null ptr - {{{ makeStructuralReturn([0, 0]) }}}; + // escape special characters + // TODO: not sure we really need to escape all of these in JS regexps + var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.'; + for (var i=0, ii=SPECIAL_CHARS.length; i 255']) }}}; - }, - - llvm_umul_with_overflow_i8: function(x, y) { - x = x & 0xff; - y = y & 0xff; - {{{ makeStructuralReturn(['(x*y) & 0xff', 'x*y > 255']) }}}; - }, - - llvm_uadd_with_overflow_i16: function(x, y) { - x = x & 0xffff; - y = y & 0xffff; - {{{ makeStructuralReturn(['(x+y) & 0xffff', 'x+y > 65535']) }}}; - }, - - llvm_umul_with_overflow_i16: function(x, y) { - x = x & 0xffff; - y = y & 0xffff; - {{{ makeStructuralReturn(['(x*y) & 0xffff', 'x*y > 65535']) }}}; - }, - - llvm_uadd_with_overflow_i32: function(x, y) { - x = x>>>0; - y = y>>>0; - {{{ makeStructuralReturn(['(x+y)>>>0', 'x+y > 4294967295']) }}}; - }, - - llvm_umul_with_overflow_i32: function(x, y) { - x = x>>>0; - y = y>>>0; - {{{ makeStructuralReturn(['(x*y)>>>0', 'x*y > 4294967295']) }}}; - }, + var DATE_PATTERNS = { + /* weeday name */ '%a': '(?:Sun(?:day)?)|(?:Mon(?:day)?)|(?:Tue(?:sday)?)|(?:Wed(?:nesday)?)|(?:Thu(?:rsday)?)|(?:Fri(?:day)?)|(?:Sat(?:urday)?)', + /* month name */ '%b': '(?:Jan(?:uary)?)|(?:Feb(?:ruary)?)|(?:Mar(?:ch)?)|(?:Apr(?:il)?)|May|(?:Jun(?:e)?)|(?:Jul(?:y)?)|(?:Aug(?:ust)?)|(?:Sep(?:tember)?)|(?:Oct(?:ober)?)|(?:Nov(?:ember)?)|(?:Dec(?:ember)?)', + /* century */ '%C': '\\d\\d', + /* day of month */ '%d': '0[1-9]|[1-9](?!\\d)|1\\d|2\\d|30|31', + /* hour (24hr) */ '%H': '\\d(?!\\d)|[0,1]\\d|20|21|22|23', + /* hour (12hr) */ '%I': '\\d(?!\\d)|0\\d|10|11|12', + /* day of year */ '%j': '00[1-9]|0?[1-9](?!\\d)|0?[1-9]\\d(?!\\d)|[1,2]\\d\\d|3[0-6]\\d', + /* month */ '%m': '0[1-9]|[1-9](?!\\d)|10|11|12', + /* minutes */ '%M': '0\\d|\\d(?!\\d)|[1-5]\\d', + /* whitespace */ '%n': '\\s', + /* AM/PM */ '%p': 'AM|am|PM|pm|A\\.M\\.|a\\.m\\.|P\\.M\\.|p\\.m\\.', + /* seconds */ '%S': '0\\d|\\d(?!\\d)|[1-5]\\d|60', + /* week number */ '%U': '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53', + /* week number */ '%W': '0\\d|\\d(?!\\d)|[1-4]\\d|50|51|52|53', + /* weekday number */ '%w': '[0-6]', + /* 2-digit year */ '%y': '\\d\\d', + /* 4-digit year */ '%Y': '\\d\\d\\d\\d', + /* % */ '%%': '%', + /* whitespace */ '%t': '\\s', + }; - llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }], - llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) { -#if ASSERTIONS - Runtime.warnOnce('no overflow support in llvm_umul_with_overflow_i64'); -#endif - var low = ___muldi3(xl, xh, yl, yh); - {{{ makeStructuralReturn(['low', makeGetTempRet0(), '0']) }}}; - }, + var MONTH_NUMBERS = {JAN: 0, FEB: 1, MAR: 2, APR: 3, MAY: 4, JUN: 5, JUL: 6, AUG: 7, SEP: 8, OCT: 9, NOV: 10, DEC: 11}; + var DAY_NUMBERS_SUN_FIRST = {SUN: 0, MON: 1, TUE: 2, WED: 3, THU: 4, FRI: 5, SAT: 6}; + var DAY_NUMBERS_MON_FIRST = {MON: 0, TUE: 1, WED: 2, THU: 3, FRI: 4, SAT: 5, SUN: 6}; - llvm_stacksave: function() { - var self = _llvm_stacksave; - if (!self.LLVM_SAVEDSTACKS) { - self.LLVM_SAVEDSTACKS = []; + for (var datePattern in DATE_PATTERNS) { + pattern = pattern.replace(datePattern, '('+datePattern+DATE_PATTERNS[datePattern]+')'); } - self.LLVM_SAVEDSTACKS.push(Runtime.stackSave()); - return self.LLVM_SAVEDSTACKS.length-1; - }, - llvm_stackrestore: function(p) { - var self = _llvm_stacksave; - var ret = self.LLVM_SAVEDSTACKS[p]; - self.LLVM_SAVEDSTACKS.splice(p, 1); - Runtime.stackRestore(ret); - }, - __cxa_pure_virtual: function() { - ABORT = true; - throw 'Pure virtual function called!'; - }, + // take care of capturing groups + var capture = []; + for (var i=pattern.indexOf('%'); i>=0; i=pattern.indexOf('%')) { + capture.push(pattern[i+1]); + pattern = pattern.replace(new RegExp('\\%'+pattern[i+1], 'g'), ''); + } - llvm_flt_rounds: function() { - return -1; // 'indeterminable' for FLT_ROUNDS - }, + var matches = new RegExp('^'+pattern, "i").exec(Pointer_stringify(buf)) + // Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches)); - llvm_memory_barrier: function(){}, + function initDate() { + function fixup(value, min, max) { + return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min); + }; + return { + year: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32', 0, 0, 1) }}} + 1900 , 1970, 9999), + month: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32', 0, 0, 1) }}}, 0, 11), + day: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32', 0, 0, 1) }}}, 1, 31), + hour: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32', 0, 0, 1) }}}, 0, 23), + min: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32', 0, 0, 1) }}}, 0, 59), + sec: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32', 0, 0, 1) }}}, 0, 59) + }; + }; - llvm_atomic_load_add_i32_p0i32: function(ptr, delta) { - var ret = {{{ makeGetValue('ptr', '0', 'i32') }}}; - {{{ makeSetValue('ptr', '0', 'ret+delta', 'i32') }}}; - return ret; - }, + if (matches) { + var date = initDate(); + var value; - llvm_expect_i32__inline: function(val, expected) { - return '(' + val + ')'; - }, + function getMatch(symbol) { + var pos = capture.indexOf(symbol); + // check if symbol appears in regexp + if (pos >= 0) { + // return matched value or null (falsy!) for non-matches + return matches[pos+1]; + } + return; + } - llvm_lifetime_start: function() {}, - llvm_lifetime_end: function() {}, + // seconds + if ((value=getMatch('S'))) { + date.sec = parseInt(value); + } - llvm_invariant_start: function() {}, - llvm_invariant_end: function() {}, + // minutes + if ((value=getMatch('M'))) { + date.min = parseInt(value); + } - llvm_objectsize_i32: function() { return -1 }, // TODO: support this + // hours + if ((value=getMatch('H'))) { + // 24h clock + date.hour = parseInt(value); + } else if ((value = getMatch('I'))) { + // AM/PM clock + var hour = parseInt(value); + if ((value=getMatch('p'))) { + hour += value.toUpperCase()[0] === 'P' ? 12 : 0; + } + date.hour = hour; + } - llvm_dbg_declare__inline: function() { throw 'llvm_debug_declare' }, // avoid warning + // year + if ((value=getMatch('Y'))) { + // parse from four-digit year + date.year = parseInt(value); + } else if ((value=getMatch('y'))) { + // parse from two-digit year... + var year = parseInt(value); + if ((value=getMatch('C'))) { + // ...and century + year += parseInt(value)*100; + } else { + // ...and rule-of-thumb + year += year<69 ? 2000 : 1900; + } + date.year = year; + } - // llvm-nacl + // month + if ((value=getMatch('m'))) { + // parse from month number + date.month = parseInt(value)-1; + } else if ((value=getMatch('b'))) { + // parse from month name + date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0; + // TODO: derive month from day in year+year, week number+day of week+year + } - llvm_nacl_atomic_store_i32__inline: true, + // day + if ((value=getMatch('d'))) { + // get day of month directly + date.day = parseInt(value); + } else if ((value=getMatch('j'))) { + // get day of month from day of year ... + var day = parseInt(value); + var leapYear = __isLeapYear(date.year); + for (var month=0; month<12; ++month) { + var daysUntilMonth = __arraySum(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, month-1); + if (day<=daysUntilMonth+(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[month]) { + date.day = day-daysUntilMonth; + } + } + } else if ((value=getMatch('a'))) { + // get day of month from weekday ... + var weekDay = value.substring(0,3).toUpperCase(); + if ((value=getMatch('U'))) { + // ... and week number (Sunday being first day of week) + // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. + // All days in a new year preceding the first Sunday are considered to be in week 0. + var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay]; + var weekNumber = parseInt(value); - llvm_nacl_atomic_cmpxchg_i8__inline: true, - llvm_nacl_atomic_cmpxchg_i16__inline: true, - llvm_nacl_atomic_cmpxchg_i32__inline: true, + // January 1st + var janFirst = new Date(date.year, 0, 1); + var endDate; + if (janFirst.getDay() === 0) { + // Jan 1st is a Sunday, and, hence in the 1st CW + endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1)); + } else { + // Jan 1st is not a Sunday, and, hence still in the 0th CW + endDate = __addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1)); + } + date.day = endDate.getDate(); + date.month = endDate.getMonth(); + } else if ((value=getMatch('W'))) { + // ... and week number (Monday being first day of week) + // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. + // All days in a new year preceding the first Monday are considered to be in week 0. + var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay]; + var weekNumber = parseInt(value); - // gnu atomics + // January 1st + var janFirst = new Date(date.year, 0, 1); + var endDate; + if (janFirst.getDay()===1) { + // Jan 1st is a Monday, and, hence in the 1st CW + endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1)); + } else { + // Jan 1st is not a Monday, and, hence still in the 0th CW + endDate = __addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1)); + } - __atomic_is_lock_free: function(size, ptr) { - return size <= 4 && (ptr&(size-1)) == 0; - }, + date.day = endDate.getDate(); + date.month = endDate.getMonth(); + } + } - __atomic_load_8: function(ptr, memmodel) { - {{{ makeStructuralReturn([makeGetValue('ptr', 0, 'i32'), makeGetValue('ptr', 4, 'i32')]) }}}; - }, + /* + tm_sec int seconds after the minute 0-61* + tm_min int minutes after the hour 0-59 + tm_hour int hours since midnight 0-23 + tm_mday int day of the month 1-31 + tm_mon int months since January 0-11 + tm_year int years since 1900 + tm_wday int days since Sunday 0-6 + tm_yday int days since January 1 0-365 + tm_isdst int Daylight Saving Time flag + */ - __atomic_store_8: function(ptr, vall, valh, memmodel) { - {{{ makeSetValue('ptr', 0, 'vall', 'i32') }}}; - {{{ makeSetValue('ptr', 4, 'valh', 'i32') }}}; + var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}}; + {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; + + // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F + // TODO: not sure that intArrayFromString handles all unicode characters correctly + return buf+intArrayFromString(matches[0]).length-1; + } + + return 0; + }, + strptime_l__deps: ['strptime'], + strptime_l: function(buf, format, tm) { + return _strptime(buf, format, tm); // no locale support yet }, - __atomic_exchange_8: function(ptr, vall, valh, memmodel) { - var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; - var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; - {{{ makeSetValue('ptr', 0, 'vall', 'i32') }}}; - {{{ makeSetValue('ptr', 4, 'valh', 'i32') }}}; - {{{ makeStructuralReturn(['l', 'h']) }}}; + getdate: function(string) { + // struct tm *getdate(const char *string); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/getdate.html + // TODO: Implement. + return 0; }, - __atomic_compare_exchange_8: function(ptr, expected, desiredl, desiredh, weak, success_memmodel, failure_memmodel) { - var pl = {{{ makeGetValue('ptr', 0, 'i32') }}}; - var ph = {{{ makeGetValue('ptr', 4, 'i32') }}}; - var el = {{{ makeGetValue('expected', 0, 'i32') }}}; - var eh = {{{ makeGetValue('expected', 4, 'i32') }}}; - if (pl === el && ph === eh) { - {{{ makeSetValue('ptr', 0, 'desiredl', 'i32') }}}; - {{{ makeSetValue('ptr', 4, 'desiredh', 'i32') }}}; - return 1; - } else { - {{{ makeSetValue('expected', 0, 'pl', 'i32') }}}; - {{{ makeSetValue('expected', 4, 'ph', 'i32') }}}; - return 0; + // ========================================================================== + // sys/time.h + // ========================================================================== + + nanosleep__deps: ['usleep'], + nanosleep: function(rqtp, rmtp) { + // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); + var seconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_sec, 'i32') }}}; + var nanoseconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_nsec, 'i32') }}}; + if (rmtp !== 0) { + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}}; + {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}}; } + return _usleep((seconds * 1e6) + (nanoseconds / 1000)); }, - - __atomic_fetch_add_8__deps: ['llvm_uadd_with_overflow_i64'], - __atomic_fetch_add_8: function(ptr, vall, valh, memmodel) { - var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; - var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; - {{{ makeSetValue('ptr', 0, '_llvm_uadd_with_overflow_i64(l, h, vall, valh)', 'i32') }}}; - {{{ makeSetValue('ptr', 4, makeGetTempRet0(), 'i32') }}}; - {{{ makeStructuralReturn(['l', 'h']) }}}; + clock_gettime__deps: ['emscripten_get_now', 'emscripten_get_now_is_monotonic', '$ERRNO_CODES', '__setErrNo'], + clock_gettime: function(clk_id, tp) { + // int clock_gettime(clockid_t clk_id, struct timespec *tp); + var now; + if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { + now = Date.now(); + } else if (clk_id === {{{ cDefine('CLOCK_MONOTONIC') }}} && _emscripten_get_now_is_monotonic()) { + now = _emscripten_get_now(); + } else { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_sec, '(now/1000)|0', 'i32') }}}; // seconds + {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_nsec, '((now % 1000)*1000*1000)|0', 'i32') }}}; // nanoseconds + return 0; }, - - __atomic_fetch_sub_8__deps: ['i64Subtract'], - __atomic_fetch_sub_8: function(ptr, vall, valh, memmodel) { - var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; - var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; - {{{ makeSetValue('ptr', 0, '_i64Subtract(l, h, vall, valh)', 'i32') }}}; - {{{ makeSetValue('ptr', 4, makeGetTempRet0(), 'i32') }}}; - {{{ makeStructuralReturn(['l', 'h']) }}}; + __clock_gettime: 'clock_gettime', // musl internal alias + clock_settime__deps: ['$ERRNO_CODES', '__setErrNo'], + clock_settime: function(clk_id, tp) { + // int clock_settime(clockid_t clk_id, const struct timespec *tp); + // Nothing. + ___setErrNo(clk_id === {{{ cDefine('CLOCK_REALTIME') }}} ? ERRNO_CODES.EPERM + : ERRNO_CODES.EINVAL); + return -1; }, - - __atomic_fetch_and_8__deps: ['i64Subtract'], - __atomic_fetch_and_8: function(ptr, vall, valh, memmodel) { - var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; - var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; - {{{ makeSetValue('ptr', 0, 'l&vall', 'i32') }}}; - {{{ makeSetValue('ptr', 4, 'h&valh', 'i32') }}}; - {{{ makeStructuralReturn(['l', 'h']) }}}; + clock_getres__deps: ['emscripten_get_now_res', 'emscripten_get_now_is_monotonic', '$ERRNO_CODES', '__setErrNo'], + clock_getres: function(clk_id, res) { + // int clock_getres(clockid_t clk_id, struct timespec *res); + var nsec; + if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { + nsec = 1000 * 1000; // educated guess that it's milliseconds + } else if (clk_id === {{{ cDefine('CLOCK_MONOTONIC') }}} && _emscripten_get_now_is_monotonic()) { + nsec = _emscripten_get_now_res(); + } else { + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '(nsec/1000000000)|0', 'i32') }}}; + {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, 'nsec', 'i32') }}} // resolution is nanoseconds + return 0; }, - - __atomic_fetch_or_8: function(ptr, vall, valh, memmodel) { - var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; - var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; - {{{ makeSetValue('ptr', 0, 'l|vall', 'i32') }}}; - {{{ makeSetValue('ptr', 4, 'h|valh', 'i32') }}}; - {{{ makeStructuralReturn(['l', 'h']) }}}; + clock_getcpuclockid__deps: ['$PROCINFO'], + clock_getcpuclockid: function(pid, clk_id) { + if (pid < 0) return ERRNO_CODES.ESRCH; + if (pid !== 0 && pid !== PROCINFO.pid) return ERRNO_CODES.ENOSYS; + if (clk_id) {{{ makeSetValue('clk_id', 0, 2/*CLOCK_PROCESS_CPUTIME_ID*/, 'i32') }}}; + return 0; }, - - __atomic_fetch_xor_8: function(ptr, vall, valh, memmodel) { - var l = {{{ makeGetValue('ptr', 0, 'i32') }}}; - var h = {{{ makeGetValue('ptr', 4, 'i32') }}}; - {{{ makeSetValue('ptr', 0, 'l^vall', 'i32') }}}; - {{{ makeSetValue('ptr', 4, 'h^valh', 'i32') }}}; - {{{ makeStructuralReturn(['l', 'h']) }}}; + // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/sys/time.h.html + gettimeofday: function(ptr) { + var now = Date.now(); + {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_sec, '(now/1000)|0', 'i32') }}}; // seconds + {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_usec, '((now % 1000)*1000)|0', 'i32') }}}; // microseconds + return 0; }, - // ========================================================================== - // llvm-mono integration + // sys/timeb.h // ========================================================================== - - llvm_mono_load_i8_p0i8: function(ptr) { - return {{{ makeGetValue('ptr', 0, 'i8') }}}; + + ftime: function(p) { + var millis = Date.now(); + {{{ makeSetValue('p', C_STRUCTS.timeb.time, '(millis/1000)|0', 'i32') }}}; + {{{ makeSetValue('p', C_STRUCTS.timeb.millitm, 'millis % 1000', 'i16') }}}; + {{{ makeSetValue('p', C_STRUCTS.timeb.timezone, '0', 'i16') }}}; // Obsolete field + {{{ makeSetValue('p', C_STRUCTS.timeb.dstflag, '0', 'i16') }}}; // Obsolete field + return 0; }, - llvm_mono_store_i8_p0i8: function(value, ptr) { - {{{ makeSetValue('ptr', 0, 'value', 'i8') }}}; - }, + // ========================================================================== + // sys/times.h + // ========================================================================== - llvm_mono_load_i16_p0i16: function(ptr) { - return {{{ makeGetValue('ptr', 0, 'i16') }}}; + times__deps: ['memset'], + times: function(buffer) { + // clock_t times(struct tms *buffer); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/times.html + // NOTE: This is fake, since we can't calculate real CPU time usage in JS. + if (buffer !== 0) { + _memset(buffer, 0, {{{ C_STRUCTS.tms.__size__ }}}); + } + return 0; }, - llvm_mono_store_i16_p0i16: function(value, ptr) { - {{{ makeSetValue('ptr', 0, 'value', 'i16') }}}; + // ========================================================================== + // sys/types.h + // ========================================================================== + // http://www.kernel.org/doc/man-pages/online/pages/man3/minor.3.html + makedev: function(maj, min) { + return ((maj) << 8 | (min)); }, - - llvm_mono_load_i32_p0i32: function(ptr) { - return {{{ makeGetValue('ptr', 0, 'i32') }}}; + gnu_dev_makedev: 'makedev', + major: function(dev) { + return ((dev) >> 8); }, - - llvm_mono_store_i32_p0i32: function(value, ptr) { - {{{ makeSetValue('ptr', 0, 'value', 'i32') }}}; + gnu_dev_major: 'major', + minor: function(dev) { + return ((dev) & 0xff); }, + gnu_dev_minor: 'minor', // ========================================================================== - // math.h + // setjmp.h // ========================================================================== - cos: 'Math_cos', - cosf: 'Math_cos', - cosl: 'Math_cos', - sin: 'Math_sin', - sinf: 'Math_sin', - sinl: 'Math_sin', - tan: 'Math_tan', - tanf: 'Math_tan', - tanl: 'Math_tan', - acos: 'Math_acos', - acosf: 'Math_acos', - acosl: 'Math_acos', - asin: 'Math_asin', - asinf: 'Math_asin', - asinl: 'Math_asin', - atan: 'Math_atan', - atanf: 'Math_atan', - atanl: 'Math_atan', - atan2: 'Math_atan2', - atan2f: 'Math_atan2', - atan2l: 'Math_atan2', - exp: 'Math_exp', - expf: 'Math_exp', - expl: 'Math_exp', - - // The erf and erfc functions are inspired from - // http://www.digitalmars.com/archives/cplusplus/3634.html - // and mruby source code at - // https://github.com/mruby/mruby/blob/master/src/math.c - erfc: function(x) { - var MATH_TOLERANCE = 1E-12; - var ONE_SQRTPI = 0.564189583547756287; - var a = 1; - var b = x; - var c = x; - var d = x * x + 0.5; - var n = 1.0; - var q2 = b / d; - var q1, t; - - if (Math.abs(x) < 2.2) { - return 1.0 - _erf(x); - } - if (x < 0) { - return 2.0 - _erfc(-x); + saveSetjmp__asm: true, + saveSetjmp__sig: 'iii', + saveSetjmp__deps: ['realloc'], + saveSetjmp: function(env, label, table, size) { + // Not particularly fast: slow table lookup of setjmpId to label. But setjmp + // prevents relooping anyhow, so slowness is to be expected. And typical case + // is 1 setjmp per invocation, or less. + env = env|0; + label = label|0; + table = table|0; + size = size|0; + var i = 0; + setjmpId = (setjmpId+1)|0; + {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; + while ((i|0) < (size|0)) { + if ({{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}} == 0) { + {{{ makeSetValueAsm('table', '(i<<3)', 'setjmpId', 'i32') }}}; + {{{ makeSetValueAsm('table', '(i<<3)+4', 'label', 'i32') }}}; + // prepare next slot + {{{ makeSetValueAsm('table', '(i<<3)+8', '0', 'i32') }}}; + {{{ makeSetTempRet0('size') }}}; + return table | 0; + } + i = i+1|0; } - do { - t = a * n + b * x; - a = b; - b = t; - t = c * n + d * x; - c = d; - d = t; - n += 0.5; - q1 = q2; - q2 = b / d; - } while (Math.abs(q1 - q2) / q2 > MATH_TOLERANCE); - return (ONE_SQRTPI * Math.exp(- x * x) * q2); - }, - erfcf: 'erfc', - erfcl: 'erfc', - erf__deps: ['erfc'], - erf: function(x) { - var MATH_TOLERANCE = 1E-12; - var TWO_SQRTPI = 1.128379167095512574; - var sum = x; - var term = x; - var xsqr = x*x; - var j = 1; - - if (Math.abs(x) > 2.2) { - return 1.0 - _erfc(x); + // grow the table + size = (size*2)|0; + table = _realloc(table|0, 8*(size+1|0)|0) | 0; + table = _saveSetjmp(env|0, label|0, table|0, size|0) | 0; + {{{ makeSetTempRet0('size') }}}; + return table | 0; + }, + + testSetjmp__asm: true, + testSetjmp__sig: 'iii', + testSetjmp: function(id, table, size) { + id = id|0; + table = table|0; + size = size|0; + var i = 0, curr = 0; + while ((i|0) < (size|0)) { + curr = {{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}}; + if ((curr|0) == 0) break; + if ((curr|0) == (id|0)) { + return {{{ makeGetValueAsm('table', '(i<<3)+4', 'i32') }}}; + } + i = i+1|0; } - do { - term *= xsqr / j; - sum -= term / (2 * j + 1); - ++j; - term *= xsqr / j; - sum += term / (2 * j + 1); - ++j; - } while (Math.abs(term / sum) > MATH_TOLERANCE); - return (TWO_SQRTPI * sum); - }, - erff: 'erf', - erfl: 'erf', - log: 'Math_log', - logf: 'Math_log', - logl: 'Math_log', - sqrt: 'Math_sqrt', - sqrtf: 'Math_sqrt', - sqrtl: 'Math_sqrt', - fabs: 'Math_abs', - fabsf: 'Math_abs', - fabsl: 'Math_abs', - llvm_fabs_f32: 'Math_abs', - llvm_fabs_f64: 'Math_abs', - ceil: 'Math_ceil', - ceilf: 'Math_ceil', - ceill: 'Math_ceil', - floor: 'Math_floor', - floorf: 'Math_floor', - floorl: 'Math_floor', - pow: 'Math_pow', - powf: 'Math_pow', - powl: 'Math_pow', - llvm_sqrt_f32: 'Math_sqrt', - llvm_sqrt_f64: 'Math_sqrt', - llvm_pow_f32: 'Math_pow', - llvm_pow_f64: 'Math_pow', - llvm_log_f32: 'Math_log', - llvm_log_f64: 'Math_log', - llvm_exp_f32: 'Math_exp', - llvm_exp_f64: 'Math_exp', + return 0; + }, - _reallyNegative: function(x) { - return x < 0 || (x === 0 && (1/x) === -Infinity); + setjmp__deps: ['saveSetjmp', 'testSetjmp'], + setjmp__inline: function(env) { + // Save the label + return '_saveSetjmp(' + env + ', label, setjmpTable)|0'; }, - div: function(divt, numer, denom) { - var quot = (numer / denom) | 0; - var rem = numer - quot * denom; - {{{ makeSetValue('divt', C_STRUCTS.div_t.quot, 'quot', 'i32') }}}; - {{{ makeSetValue('divt', C_STRUCTS.div_t.rem, 'rem', 'i32') }}}; - return divt; + longjmp__deps: ['saveSetjmp', 'testSetjmp'], + longjmp: function(env, value) { + asm['setThrew'](env, value || 1); + throw 'longjmp'; + }, + emscripten_longjmp__deps: ['longjmp'], + emscripten_longjmp: function(env, value) { + _longjmp(env, value); }, // ========================================================================== - // sys/utsname.h + // sys/wait.h // ========================================================================== - uname: function(name) { - // int uname(struct utsname *name); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/uname.html - var layout = {{{ JSON.stringify(C_STRUCTS.utsname) }}}; - function copyString(element, value) { - var offset = layout[element]; - writeAsciiToMemory(value, name + offset); - } - if (name === 0) { - return -1; - } else { - copyString('sysname', 'Emscripten'); - copyString('nodename', 'emscripten'); - copyString('release', '1.0'); - copyString('version', '#1'); - copyString('machine', 'x86-JS'); - return 0; - } + wait__deps: ['$ERRNO_CODES', '__setErrNo'], + wait: function(stat_loc) { + // pid_t wait(int *stat_loc); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/wait.html + // Makes no sense in a single-process environment. + ___setErrNo(ERRNO_CODES.ECHILD); + return -1; }, + // NOTE: These aren't really the same, but we use the same stub for them all. + waitid: 'wait', + waitpid: 'wait', + wait3: 'wait', + wait4: 'wait', // ========================================================================== - // dlfcn.h - Dynamic library loading - // - // Some limitations: - // - // * Minification on each file separately may not work, as they will - // have different shortened names. You can in theory combine them, then - // minify, then split... perhaps. - // - // * LLVM optimizations may fail. If the child wants to access a function - // in the parent, LLVM opts may remove it from the parent when it is - // being compiled. Not sure how to tell LLVM to not do so. + // errno.h // ========================================================================== - $DLFCN: { - error: null, - errorMsg: null, - loadedLibs: {}, // handle -> [refcount, name, lib_object] - loadedLibNames: {}, // name -> handle + $ERRNO_CODES: { + EPERM: {{{ cDefine('EPERM') }}}, + ENOENT: {{{ cDefine('ENOENT') }}}, + ESRCH: {{{ cDefine('ESRCH') }}}, + EINTR: {{{ cDefine('EINTR') }}}, + EIO: {{{ cDefine('EIO') }}}, + ENXIO: {{{ cDefine('ENXIO') }}}, + E2BIG: {{{ cDefine('E2BIG') }}}, + ENOEXEC: {{{ cDefine('ENOEXEC') }}}, + EBADF: {{{ cDefine('EBADF') }}}, + ECHILD: {{{ cDefine('ECHILD') }}}, + EAGAIN: {{{ cDefine('EAGAIN') }}}, + EWOULDBLOCK: {{{ cDefine('EWOULDBLOCK') }}}, + ENOMEM: {{{ cDefine('ENOMEM') }}}, + EACCES: {{{ cDefine('EACCES') }}}, + EFAULT: {{{ cDefine('EFAULT') }}}, + ENOTBLK: {{{ cDefine('ENOTBLK') }}}, + EBUSY: {{{ cDefine('EBUSY') }}}, + EEXIST: {{{ cDefine('EEXIST') }}}, + EXDEV: {{{ cDefine('EXDEV') }}}, + ENODEV: {{{ cDefine('ENODEV') }}}, + ENOTDIR: {{{ cDefine('ENOTDIR') }}}, + EISDIR: {{{ cDefine('EISDIR') }}}, + EINVAL: {{{ cDefine('EINVAL') }}}, + ENFILE: {{{ cDefine('ENFILE') }}}, + EMFILE: {{{ cDefine('EMFILE') }}}, + ENOTTY: {{{ cDefine('ENOTTY') }}}, + ETXTBSY: {{{ cDefine('ETXTBSY') }}}, + EFBIG: {{{ cDefine('EFBIG') }}}, + ENOSPC: {{{ cDefine('ENOSPC') }}}, + ESPIPE: {{{ cDefine('ESPIPE') }}}, + EROFS: {{{ cDefine('EROFS') }}}, + EMLINK: {{{ cDefine('EMLINK') }}}, + EPIPE: {{{ cDefine('EPIPE') }}}, + EDOM: {{{ cDefine('EDOM') }}}, + ERANGE: {{{ cDefine('ERANGE') }}}, + ENOMSG: {{{ cDefine('ENOMSG') }}}, + EIDRM: {{{ cDefine('EIDRM') }}}, + ECHRNG: {{{ cDefine('ECHRNG') }}}, + EL2NSYNC: {{{ cDefine('EL2NSYNC') }}}, + EL3HLT: {{{ cDefine('EL3HLT') }}}, + EL3RST: {{{ cDefine('EL3RST') }}}, + ELNRNG: {{{ cDefine('ELNRNG') }}}, + EUNATCH: {{{ cDefine('EUNATCH') }}}, + ENOCSI: {{{ cDefine('ENOCSI') }}}, + EL2HLT: {{{ cDefine('EL2HLT') }}}, + EDEADLK: {{{ cDefine('EDEADLK') }}}, + ENOLCK: {{{ cDefine('ENOLCK') }}}, + EBADE: {{{ cDefine('EBADE') }}}, + EBADR: {{{ cDefine('EBADR') }}}, + EXFULL: {{{ cDefine('EXFULL') }}}, + ENOANO: {{{ cDefine('ENOANO') }}}, + EBADRQC: {{{ cDefine('EBADRQC') }}}, + EBADSLT: {{{ cDefine('EBADSLT') }}}, + EDEADLOCK: {{{ cDefine('EDEADLOCK') }}}, + EBFONT: {{{ cDefine('EBFONT') }}}, + ENOSTR: {{{ cDefine('ENOSTR') }}}, + ENODATA: {{{ cDefine('ENODATA') }}}, + ETIME: {{{ cDefine('ETIME') }}}, + ENOSR: {{{ cDefine('ENOSR') }}}, + ENONET: {{{ cDefine('ENONET') }}}, + ENOPKG: {{{ cDefine('ENOPKG') }}}, + EREMOTE: {{{ cDefine('EREMOTE') }}}, + ENOLINK: {{{ cDefine('ENOLINK') }}}, + EADV: {{{ cDefine('EADV') }}}, + ESRMNT: {{{ cDefine('ESRMNT') }}}, + ECOMM: {{{ cDefine('ECOMM') }}}, + EPROTO: {{{ cDefine('EPROTO') }}}, + EMULTIHOP: {{{ cDefine('EMULTIHOP') }}}, + EDOTDOT: {{{ cDefine('EDOTDOT') }}}, + EBADMSG: {{{ cDefine('EBADMSG') }}}, + ENOTUNIQ: {{{ cDefine('ENOTUNIQ') }}}, + EBADFD: {{{ cDefine('EBADFD') }}}, + EREMCHG: {{{ cDefine('EREMCHG') }}}, + ELIBACC: {{{ cDefine('ELIBACC') }}}, + ELIBBAD: {{{ cDefine('ELIBBAD') }}}, + ELIBSCN: {{{ cDefine('ELIBSCN') }}}, + ELIBMAX: {{{ cDefine('ELIBMAX') }}}, + ELIBEXEC: {{{ cDefine('ELIBEXEC') }}}, + ENOSYS: {{{ cDefine('ENOSYS') }}}, + ENOTEMPTY: {{{ cDefine('ENOTEMPTY') }}}, + ENAMETOOLONG: {{{ cDefine('ENAMETOOLONG') }}}, + ELOOP: {{{ cDefine('ELOOP') }}}, + EOPNOTSUPP: {{{ cDefine('EOPNOTSUPP') }}}, + EPFNOSUPPORT: {{{ cDefine('EPFNOSUPPORT') }}}, + ECONNRESET: {{{ cDefine('ECONNRESET') }}}, + ENOBUFS: {{{ cDefine('ENOBUFS') }}}, + EAFNOSUPPORT: {{{ cDefine('EAFNOSUPPORT') }}}, + EPROTOTYPE: {{{ cDefine('EPROTOTYPE') }}}, + ENOTSOCK: {{{ cDefine('ENOTSOCK') }}}, + ENOPROTOOPT: {{{ cDefine('ENOPROTOOPT') }}}, + ESHUTDOWN: {{{ cDefine('ESHUTDOWN') }}}, + ECONNREFUSED: {{{ cDefine('ECONNREFUSED') }}}, + EADDRINUSE: {{{ cDefine('EADDRINUSE') }}}, + ECONNABORTED: {{{ cDefine('ECONNABORTED') }}}, + ENETUNREACH: {{{ cDefine('ENETUNREACH') }}}, + ENETDOWN: {{{ cDefine('ENETDOWN') }}}, + ETIMEDOUT: {{{ cDefine('ETIMEDOUT') }}}, + EHOSTDOWN: {{{ cDefine('EHOSTDOWN') }}}, + EHOSTUNREACH: {{{ cDefine('EHOSTUNREACH') }}}, + EINPROGRESS: {{{ cDefine('EINPROGRESS') }}}, + EALREADY: {{{ cDefine('EALREADY') }}}, + EDESTADDRREQ: {{{ cDefine('EDESTADDRREQ') }}}, + EMSGSIZE: {{{ cDefine('EMSGSIZE') }}}, + EPROTONOSUPPORT: {{{ cDefine('EPROTONOSUPPORT') }}}, + ESOCKTNOSUPPORT: {{{ cDefine('ESOCKTNOSUPPORT') }}}, + EADDRNOTAVAIL: {{{ cDefine('EADDRNOTAVAIL') }}}, + ENETRESET: {{{ cDefine('ENETRESET') }}}, + EISCONN: {{{ cDefine('EISCONN') }}}, + ENOTCONN: {{{ cDefine('ENOTCONN') }}}, + ETOOMANYREFS: {{{ cDefine('ETOOMANYREFS') }}}, + EUSERS: {{{ cDefine('EUSERS') }}}, + EDQUOT: {{{ cDefine('EDQUOT') }}}, + ESTALE: {{{ cDefine('ESTALE') }}}, + ENOTSUP: {{{ cDefine('ENOTSUP') }}}, + ENOMEDIUM: {{{ cDefine('ENOMEDIUM') }}}, + EILSEQ: {{{ cDefine('EILSEQ') }}}, + EOVERFLOW: {{{ cDefine('EOVERFLOW') }}}, + ECANCELED: {{{ cDefine('ECANCELED') }}}, + ENOTRECOVERABLE: {{{ cDefine('ENOTRECOVERABLE') }}}, + EOWNERDEAD: {{{ cDefine('EOWNERDEAD') }}}, + ESTRPIPE: {{{ cDefine('ESTRPIPE') }}}, }, - // void* dlopen(const char* filename, int flag); - dlopen__deps: ['$DLFCN', '$FS', '$ENV'], - dlopen: function(filename, flag) { - // void *dlopen(const char *file, int mode); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html - filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename); - - if (DLFCN.loadedLibNames[filename]) { - // Already loaded; increment ref count and return. - var handle = DLFCN.loadedLibNames[filename]; - DLFCN.loadedLibs[handle].refcount++; - return handle; - } - - if (filename === '__self__') { - var handle = -1; - var lib_module = Module; - var cached_functions = {}; - } else { - var target = FS.findObject(filename); - if (!target || target.isFolder || target.isDevice) { - DLFCN.errorMsg = 'Could not find dynamic lib: ' + filename; - return 0; - } else { - FS.forceLoadFile(target); - var lib_data = FS.readFile(filename, { encoding: 'utf8' }); - } - - try { - var lib_module = eval(lib_data)( - Runtime.alignFunctionTables(), - Module - ); - } catch (e) { -#if ASSERTIONS - Module.printErr('Error in loading dynamic library: ' + e); -#endif - DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename; - return 0; - } - - // Not all browsers support Object.keys(). - var handle = 1; - for (var key in DLFCN.loadedLibs) { - if (DLFCN.loadedLibs.hasOwnProperty(key)) handle++; - } - - // We don't care about RTLD_NOW and RTLD_LAZY. - if (flag & 256) { // RTLD_GLOBAL - for (var ident in lib_module) { - if (lib_module.hasOwnProperty(ident)) { - Module[ident] = lib_module[ident]; - } - } - } - - var cached_functions = {}; - } - DLFCN.loadedLibs[handle] = { - refcount: 1, - name: filename, - module: lib_module, - cached_functions: cached_functions - }; - DLFCN.loadedLibNames[filename] = handle; - - return handle; - }, - // int dlclose(void* handle); - dlclose__deps: ['$DLFCN'], - dlclose: function(handle) { - // int dlclose(void *handle); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html - if (!DLFCN.loadedLibs[handle]) { - DLFCN.errorMsg = 'Tried to dlclose() unopened handle: ' + handle; - return 1; - } else { - var lib_record = DLFCN.loadedLibs[handle]; - if (--lib_record.refcount == 0) { - if (lib_record.module.cleanups) { - lib_record.module.cleanups.forEach(function(cleanup) { cleanup() }); - } - delete DLFCN.loadedLibNames[lib_record.name]; - delete DLFCN.loadedLibs[handle]; - } - return 0; - } - }, - // void* dlsym(void* handle, const char* symbol); - dlsym__deps: ['$DLFCN'], - dlsym: function(handle, symbol) { - // void *dlsym(void *restrict handle, const char *restrict name); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html - symbol = Pointer_stringify(symbol); - - if (!DLFCN.loadedLibs[handle]) { - DLFCN.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle; - return 0; - } else { - var lib = DLFCN.loadedLibs[handle]; - symbol = '_' + symbol; - if (lib.cached_functions.hasOwnProperty(symbol)) { - return lib.cached_functions[symbol]; - } - if (!lib.module.hasOwnProperty(symbol)) { - DLFCN.errorMsg = ('Tried to lookup unknown symbol "' + symbol + - '" in dynamic lib: ' + lib.name); - return 0; - } else { - var result = lib.module[symbol]; - if (typeof result == 'function') { - result = Runtime.addFunction(result); - lib.cached_functions = result; - } - return result; - } - } - }, - // char* dlerror(void); - dlerror__deps: ['$DLFCN'], - dlerror: function() { - // char *dlerror(void); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlerror.html - if (DLFCN.errorMsg === null) { - return 0; - } else { - if (DLFCN.error) _free(DLFCN.error); - var msgArr = intArrayFromString(DLFCN.errorMsg); - DLFCN.error = allocate(msgArr, 'i8', ALLOC_NORMAL); - DLFCN.errorMsg = null; - return DLFCN.error; - } - }, - - dladdr: function(addr, info) { - // report all function pointers as coming from this program itself XXX not really correct in any way - var fname = allocate(intArrayFromString(Module['thisProgram'] || './this.program'), 'i8', ALLOC_NORMAL); // XXX leak - {{{ makeSetValue('addr', 0, 'fname', 'i32') }}}; - {{{ makeSetValue('addr', QUANTUM_SIZE, '0', 'i32') }}}; - {{{ makeSetValue('addr', QUANTUM_SIZE*2, '0', 'i32') }}}; - {{{ makeSetValue('addr', QUANTUM_SIZE*3, '0', 'i32') }}}; - return 1; - }, - - // ========================================================================== - // pwd.h - // ========================================================================== - - // TODO: Implement. - // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/pwd.h.html - getpwuid: function(uid) { - return 0; // NULL - }, - - // ========================================================================== - // termios.h - // ========================================================================== - tcgetattr: function(fildes, termios_p) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_TCGETATTR') }}}, fildes, termios_p); -#endif - // http://pubs.opengroup.org/onlinepubs/009695399/functions/tcgetattr.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - if (!stream.tty) { - ___setErrNo(ERRNO_CODES.ENOTTY); - return -1; - } - return 0; - }, - - tcsetattr: function(fildes, optional_actions, termios_p) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_TCSETATTR') }}}, fildes, optional_actions, termios_p); -#endif - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/tcsetattr.html - var stream = FS.getStream(fildes); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - if (!stream.tty) { - ___setErrNo(ERRNO_CODES.ENOTTY); - return -1; - } - return 0; - }, - - cfgetospeed: function(termios_p) { -#if ASSERTIONS - Runtime.warnOnce('cfgetospeed() returning a fake value'); -#endif - return 15; - }, - - // ========================================================================== - // time.h - // ========================================================================== - - clock: function() { - if (_clock.start === undefined) _clock.start = Date.now(); - return ((Date.now() - _clock.start) * ({{{ cDefine('CLOCKS_PER_SEC') }}} / 1000))|0; - }, - - time: function(ptr) { - var ret = (Date.now()/1000)|0; - if (ptr) { - {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}}; - } - return ret; - }, - - difftime: function(time1, time0) { - return time1 - time0; - }, - - // Statically allocated time struct. -#if USE_PTHREADS - __tm_current: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_current = PthreadWorkerInit.___tm_current; else PthreadWorkerInit.___tm_current = ___tm_current = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', - __tm_timezone: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_timezone = PthreadWorkerInit.___tm_timezone; else PthreadWorkerInit.___tm_timezone = ___tm_timezone = allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', - __tm_formatted: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_formatted = PthreadWorkerInit.___tm_formatted; else PthreadWorkerInit.___tm_formatted = ___tm_formatted = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', -#else - __tm_current: 'allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', - // Statically allocated copy of the string "GMT" for gmtime() to point to - __tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)', - // Statically allocated time strings. - __tm_formatted: 'allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)', -#endif - mktime__deps: ['tzset'], - mktime: function(tmPtr) { - _tzset(); - var date = new Date({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, - 0); - - // There's an ambiguous hour when the time goes back; the tm_isdst field is - // used to disambiguate it. Date() basically guesses, so we fix it up if it - // guessed wrong, or fill in tm_isdst with the guess if it's -1. - var dst = {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'i32') }}}; - var guessedOffset = date.getTimezoneOffset(); - var start = new Date(date.getFullYear(), 0, 1); - var summerOffset = new Date(2000, 6, 1).getTimezoneOffset(); - var winterOffset = start.getTimezoneOffset(); - var dstOffset = Math.min(winterOffset, summerOffset); // DST is in December in South - if (dst < 0) { - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'Number(winterOffset != guessedOffset)', 'i32') }}}; - } else if ((dst > 0) != (winterOffset != guessedOffset)) { - var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset(); - var trueOffset = dst > 0 ? summerOffset : winterOffset; - // Don't try setMinutes(date.getMinutes() + ...) -- it's messed up. - date.setTime(date.getTime() + (trueOffset - guessedOffset)*60000); - } - - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}}; - var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; - - return (date.getTime() / 1000)|0; - }, - timelocal: 'mktime', - - gmtime__deps: ['__tm_current', 'gmtime_r'], - gmtime: function(time) { - return _gmtime_r(time, ___tm_current); - }, - - gmtime_r__deps: ['__tm_timezone'], - gmtime_r: function(time, tmPtr) { - var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; - var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); - var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}}; - - return tmPtr; - }, - timegm__deps: ['tzset'], - timegm: function(tmPtr) { - _tzset(); - var time = Date.UTC({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, - {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, - 0); - var date = new Date(time); - - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}}; - var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0); - var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; - - return (date.getTime() / 1000)|0; - }, - - localtime__deps: ['__tm_current', 'localtime_r'], - localtime: function(time) { - return _localtime_r(time, ___tm_current); - }, - - localtime_r__deps: ['__tm_timezone', 'tzset'], - localtime_r: function(time, tmPtr) { - _tzset(); - var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}}; - - var start = new Date(date.getFullYear(), 0, 1); - var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '-(date.getTimezoneOffset() * 60)', 'i32') }}}; - - // DST is in December in South - var summerOffset = new Date(2000, 6, 1).getTimezoneOffset(); - var winterOffset = start.getTimezoneOffset(); - var dst = (date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}}; - - var zonePtr = {{{ makeGetValue(makeGlobalUse('_tzname'), 'dst ? Runtime.QUANTUM_SIZE : 0', 'i32') }}}; - {{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, 'zonePtr', 'i32') }}}; - - return tmPtr; - }, - - asctime__deps: ['__tm_formatted', 'asctime_r'], - asctime: function(tmPtr) { - return _asctime_r(tmPtr, ___tm_formatted); - }, - - asctime_r__deps: ['__tm_formatted', 'mktime'], - asctime_r: function(tmPtr, buf) { - var date = { - tm_sec: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}}, - tm_min: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}}, - tm_hour: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}}, - tm_mday: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}}, - tm_mon: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}}, - tm_year: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}}, - tm_wday: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'i32') }}} - }; - var days = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]; - var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; - var s = days[date.tm_wday] + ' ' + months[date.tm_mon] + - (date.tm_mday < 10 ? ' ' : ' ') + date.tm_mday + - (date.tm_hour < 10 ? ' 0' : ' ') + date.tm_hour + - (date.tm_min < 10 ? ':0' : ':') + date.tm_min + - (date.tm_sec < 10 ? ':0' : ':') + date.tm_sec + - ' ' + (1900 + date.tm_year) + "\n"; - writeStringToMemory(s, buf); - return buf; - }, - - ctime__deps: ['__tm_current', 'ctime_r'], - ctime: function(timer) { - return _ctime_r(timer, ___tm_current); - }, - - ctime_r__deps: ['localtime_r', 'asctime_r'], - ctime_r: function(time, buf) { - var stack = Runtime.stackSave(); - var rv = _asctime_r(_localtime_r(time, Runtime.stackAlloc({{{ C_STRUCTS.tm.__size__ }}})), buf); - Runtime.stackRestore(stack); - return rv; - }, - - dysize: function(year) { - var leap = ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))); - return leap ? 366 : 365; - }, - - // TODO: Initialize these to defaults on startup from system settings. - // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) -#if USE_PTHREADS - tzname: '; if (ENVIRONMENT_IS_PTHREAD) _tzname = PthreadWorkerInit._tzname; else PthreadWorkerInit._tzname = _tzname = allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)', - daylight: '; if (ENVIRONMENT_IS_PTHREAD) _daylight = PthreadWorkerInit._daylight; else PthreadWorkerInit._daylight = _daylight = allocate(1, "i32*", ALLOC_STATIC)', - timezone: '; if (ENVIRONMENT_IS_PTHREAD) _timezone = PthreadWorkerInit._timezone; else PthreadWorkerInit._timezone = _timezone = allocate(1, "i32*", ALLOC_STATIC)', -#else - tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)', - daylight: 'allocate(1, "i32*", ALLOC_STATIC)', - timezone: 'allocate(1, "i32*", ALLOC_STATIC)', -#endif - tzset__deps: ['tzname', 'daylight', 'timezone'], - tzset: function() { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_0({{{ cDefine('EM_PROXIED_TZSET') }}}); -#endif - // TODO: Use (malleable) environment variables instead of system settings. - if (_tzset.called) return; - _tzset.called = true; - - {{{ makeSetValue(makeGlobalUse('_timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}}; - - var winter = new Date(2000, 0, 1); - var summer = new Date(2000, 6, 1); - {{{ makeSetValue(makeGlobalUse('_daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}}; - - function extractZone(date) { - var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/); - return match ? match[1] : "GMT"; - }; - var winterName = extractZone(winter); - var summerName = extractZone(summer); - var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); - var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); - if (summer.getTimezoneOffset() < winter.getTimezoneOffset()) { - // Northern hemisphere - {{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'winterNamePtr', 'i32') }}}; - {{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}}; - } else { - {{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'summerNamePtr', 'i32') }}}; - {{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'winterNamePtr', 'i32') }}}; - } - }, - - stime__deps: ['$ERRNO_CODES', '__setErrNo'], - stime: function(when) { - ___setErrNo(ERRNO_CODES.EPERM); - return -1; - }, - - _MONTH_DAYS_REGULAR: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], - _MONTH_DAYS_LEAP: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], - - _isLeapYear: function(year) { - return year%4 === 0 && (year%100 !== 0 || year%400 === 0); - }, - - _arraySum: function(array, index) { - var sum = 0; - for (var i = 0; i <= index; sum += array[i++]); - return sum; - }, - - _addDays__deps: ['_isLeapYear', '_MONTH_DAYS_LEAP', '_MONTH_DAYS_REGULAR'], - _addDays: function(date, days) { - var newDate = new Date(date.getTime()); - while(days > 0) { - var leap = __isLeapYear(newDate.getFullYear()); - var currentMonth = newDate.getMonth(); - var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth]; - - if (days > daysInCurrentMonth-newDate.getDate()) { - // we spill over to next month - days -= (daysInCurrentMonth-newDate.getDate()+1); - newDate.setDate(1); - if (currentMonth < 11) { - newDate.setMonth(currentMonth+1) - } else { - newDate.setMonth(0); - newDate.setFullYear(newDate.getFullYear()+1); - } - } else { - // we stay in current month - newDate.setDate(newDate.getDate()+days); - return newDate; - } - } - - return newDate; - }, - - strftime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], - strftime: function(s, maxsize, format, tm) { - // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html - - var tm_zone = {{{ makeGetValue('tm', C_STRUCTS.tm.tm_zone, 'i32') }}}; - - var date = { - tm_sec: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32') }}}, - tm_min: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32') }}}, - tm_hour: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32') }}}, - tm_mday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32') }}}, - tm_mon: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32') }}}, - tm_year: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32') }}}, - tm_wday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_wday, 'i32') }}}, - tm_yday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_yday, 'i32') }}}, - tm_isdst: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_isdst, 'i32') }}}, - tm_gmtoff: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_gmtoff, 'i32') }}}, - tm_zone: tm_zone ? Pointer_stringify(tm_zone) : '' - }; - - var pattern = Pointer_stringify(format); - - // expand format - var EXPANSION_RULES_1 = { - '%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013 - '%D': '%m/%d/%y', // Equivalent to %m / %d / %y - '%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d - '%h': '%b', // Equivalent to %b - '%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation - '%R': '%H:%M', // Replaced by the time in 24-hour notation - '%T': '%H:%M:%S', // Replaced by the time - '%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation - '%X': '%H:%M:%S' // Replaced by the locale's appropriate date representation - }; - for (var rule in EXPANSION_RULES_1) { - pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]); - } - - var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; - var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - - function leadingSomething(value, digits, character) { - var str = typeof value === 'number' ? value.toString() : (value || ''); - while (str.length < digits) { - str = character[0]+str; - } - return str; - }; - - function leadingNulls(value, digits) { - return leadingSomething(value, digits, '0'); - }; - - function compareByDay(date1, date2) { - function sgn(value) { - return value < 0 ? -1 : (value > 0 ? 1 : 0); - }; - - var compare; - if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) { - if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) { - compare = sgn(date1.getDate()-date2.getDate()); - } - } - return compare; - }; - - function getFirstWeekStartDate(janFourth) { - switch (janFourth.getDay()) { - case 0: // Sunday - return new Date(janFourth.getFullYear()-1, 11, 29); - case 1: // Monday - return janFourth; - case 2: // Tuesday - return new Date(janFourth.getFullYear(), 0, 3); - case 3: // Wednesday - return new Date(janFourth.getFullYear(), 0, 2); - case 4: // Thursday - return new Date(janFourth.getFullYear(), 0, 1); - case 5: // Friday - return new Date(janFourth.getFullYear()-1, 11, 31); - case 6: // Saturday - return new Date(janFourth.getFullYear()-1, 11, 30); - } - }; - - function getWeekBasedYear(date) { - var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); - - var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4); - var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4); - - var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); - var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); - - if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) { - // this date is after the start of the first week of this year - if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) { - return thisDate.getFullYear()+1; - } else { - return thisDate.getFullYear(); - } - } else { - return thisDate.getFullYear()-1; - } - }; - - var EXPANSION_RULES_2 = { - '%a': function(date) { - return WEEKDAYS[date.tm_wday].substring(0,3); - }, - '%A': function(date) { - return WEEKDAYS[date.tm_wday]; - }, - '%b': function(date) { - return MONTHS[date.tm_mon].substring(0,3); - }, - '%B': function(date) { - return MONTHS[date.tm_mon]; - }, - '%C': function(date) { - var year = date.tm_year+1900; - return leadingNulls((year/100)|0,2); - }, - '%d': function(date) { - return leadingNulls(date.tm_mday, 2); - }, - '%e': function(date) { - return leadingSomething(date.tm_mday, 2, ' '); - }, - '%g': function(date) { - // %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year. - // In this system, weeks begin on a Monday and week 1 of the year is the week that includes - // January 4th, which is also the week that includes the first Thursday of the year, and - // is also the first week that contains at least four days in the year. - // If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of - // the last week of the preceding year; thus, for Saturday 2nd January 1999, - // %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th, - // or 31st is a Monday, it and any following days are part of week 1 of the following year. - // Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01. - - return getWeekBasedYear(date).toString().substring(2); - }, - '%G': function(date) { - return getWeekBasedYear(date); - }, - '%H': function(date) { - return leadingNulls(date.tm_hour, 2); - }, - '%I': function(date) { - return leadingNulls(date.tm_hour < 13 ? date.tm_hour : date.tm_hour-12, 2); - }, - '%j': function(date) { - // Day of the year (001-366) - return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3); - }, - '%m': function(date) { - return leadingNulls(date.tm_mon+1, 2); - }, - '%M': function(date) { - return leadingNulls(date.tm_min, 2); - }, - '%n': function() { - return '\n'; - }, - '%p': function(date) { - if (date.tm_hour > 0 && date.tm_hour < 13) { - return 'AM'; - } else { - return 'PM'; - } - }, - '%S': function(date) { - return leadingNulls(date.tm_sec, 2); - }, - '%t': function() { - return '\t'; - }, - '%u': function(date) { - var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0); - return day.getDay() || 7; - }, - '%U': function(date) { - // Replaced by the week number of the year as a decimal number [00,53]. - // The first Sunday of January is the first day of week 1; - // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] - var janFirst = new Date(date.tm_year+1900, 0, 1); - var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay()); - var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); - - // is target date after the first Sunday? - if (compareByDay(firstSunday, endDate) < 0) { - // calculate difference in days between first Sunday and endDate - var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; - var firstSundayUntilEndJanuary = 31-firstSunday.getDate(); - var days = firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); - return leadingNulls(Math.ceil(days/7), 2); - } - - return compareByDay(firstSunday, janFirst) === 0 ? '01': '00'; - }, - '%V': function(date) { - // Replaced by the week number of the year (Monday as the first day of the week) - // as a decimal number [01,53]. If the week containing 1 January has four - // or more days in the new year, then it is considered week 1. - // Otherwise, it is the last week of the previous year, and the next week is week 1. - // Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday] - var janFourthThisYear = new Date(date.tm_year+1900, 0, 4); - var janFourthNextYear = new Date(date.tm_year+1901, 0, 4); - - var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear); - var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear); - - var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday); - - if (compareByDay(endDate, firstWeekStartThisYear) < 0) { - // if given date is before this years first week, then it belongs to the 53rd week of last year - return '53'; - } - - if (compareByDay(firstWeekStartNextYear, endDate) <= 0) { - // if given date is after next years first week, then it belongs to the 01th week of next year - return '01'; - } - - // given date is in between CW 01..53 of this calendar year - var daysDifference; - if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) { - // first CW of this year starts last year - daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate() - } else { - // first CW of this year starts this year - daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate(); - } - return leadingNulls(Math.ceil(daysDifference/7), 2); - }, - '%w': function(date) { - var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0); - return day.getDay(); - }, - '%W': function(date) { - // Replaced by the week number of the year as a decimal number [00,53]. - // The first Monday of January is the first day of week 1; - // days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday] - var janFirst = new Date(date.tm_year, 0, 1); - var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1); - var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday); - - // is target date after the first Monday? - if (compareByDay(firstMonday, endDate) < 0) { - var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31; - var firstMondayUntilEndJanuary = 31-firstMonday.getDate(); - var days = firstMondayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate(); - return leadingNulls(Math.ceil(days/7), 2); - } - return compareByDay(firstMonday, janFirst) === 0 ? '01': '00'; - }, - '%y': function(date) { - // Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year] - return (date.tm_year+1900).toString().substring(2); - }, - '%Y': function(date) { - // Replaced by the year as a decimal number (for example, 1997). [ tm_year] - return date.tm_year+1900; - }, - '%z': function(date) { - // Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ). - // For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). - var off = date.tm_gmtoff; - var ahead = off >= 0; - off = Math.abs(off) / 60; - // convert from minutes into hhmm format (which means 60 minutes = 100 units) - off = (off / 60)*100 + (off % 60); - return (ahead ? '+' : '-') + String("0000" + off).slice(-4); - }, - '%Z': function(date) { - return date.tm_zone; - }, - '%%': function() { - return '%'; - } - }; - for (var rule in EXPANSION_RULES_2) { - if (pattern.indexOf(rule) >= 0) { - pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date)); - } - } - - var bytes = intArrayFromString(pattern, false); - if (bytes.length > maxsize) { - return 0; - } - - writeArrayToMemory(bytes, s); - return bytes.length-1; - }, - strftime_l__deps: ['strftime'], - strftime_l: function(s, maxsize, format, tm) { - return _strftime(s, maxsize, format, tm); // no locale support yet - }, - - strptime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'], - strptime: function(buf, format, tm) { - // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html - var pattern = Pointer_stringify(format); - - // escape special characters - // TODO: not sure we really need to escape all of these in JS regexps - var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.'; - for (var i=0, ii=SPECIAL_CHARS.length; i=0; i=pattern.indexOf('%')) { - capture.push(pattern[i+1]); - pattern = pattern.replace(new RegExp('\\%'+pattern[i+1], 'g'), ''); - } - - var matches = new RegExp('^'+pattern, "i").exec(Pointer_stringify(buf)) - // Module['print'](Pointer_stringify(buf)+ ' is matched by '+((new RegExp('^'+pattern)).source)+' into: '+JSON.stringify(matches)); - - function initDate() { - function fixup(value, min, max) { - return (typeof value !== 'number' || isNaN(value)) ? min : (value>=min ? (value<=max ? value: max): min); - }; - return { - year: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32', 0, 0, 1) }}} + 1900 , 1970, 9999), - month: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32', 0, 0, 1) }}}, 0, 11), - day: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32', 0, 0, 1) }}}, 1, 31), - hour: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32', 0, 0, 1) }}}, 0, 23), - min: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32', 0, 0, 1) }}}, 0, 59), - sec: fixup({{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32', 0, 0, 1) }}}, 0, 59) - }; - }; - - if (matches) { - var date = initDate(); - var value; - - function getMatch(symbol) { - var pos = capture.indexOf(symbol); - // check if symbol appears in regexp - if (pos >= 0) { - // return matched value or null (falsy!) for non-matches - return matches[pos+1]; - } - return; - } - - // seconds - if ((value=getMatch('S'))) { - date.sec = parseInt(value); - } - - // minutes - if ((value=getMatch('M'))) { - date.min = parseInt(value); - } - - // hours - if ((value=getMatch('H'))) { - // 24h clock - date.hour = parseInt(value); - } else if ((value = getMatch('I'))) { - // AM/PM clock - var hour = parseInt(value); - if ((value=getMatch('p'))) { - hour += value.toUpperCase()[0] === 'P' ? 12 : 0; - } - date.hour = hour; - } - - // year - if ((value=getMatch('Y'))) { - // parse from four-digit year - date.year = parseInt(value); - } else if ((value=getMatch('y'))) { - // parse from two-digit year... - var year = parseInt(value); - if ((value=getMatch('C'))) { - // ...and century - year += parseInt(value)*100; - } else { - // ...and rule-of-thumb - year += year<69 ? 2000 : 1900; - } - date.year = year; - } - - // month - if ((value=getMatch('m'))) { - // parse from month number - date.month = parseInt(value)-1; - } else if ((value=getMatch('b'))) { - // parse from month name - date.month = MONTH_NUMBERS[value.substring(0,3).toUpperCase()] || 0; - // TODO: derive month from day in year+year, week number+day of week+year - } - - // day - if ((value=getMatch('d'))) { - // get day of month directly - date.day = parseInt(value); - } else if ((value=getMatch('j'))) { - // get day of month from day of year ... - var day = parseInt(value); - var leapYear = __isLeapYear(date.year); - for (var month=0; month<12; ++month) { - var daysUntilMonth = __arraySum(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, month-1); - if (day<=daysUntilMonth+(leapYear ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[month]) { - date.day = day-daysUntilMonth; - } - } - } else if ((value=getMatch('a'))) { - // get day of month from weekday ... - var weekDay = value.substring(0,3).toUpperCase(); - if ((value=getMatch('U'))) { - // ... and week number (Sunday being first day of week) - // Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. - // All days in a new year preceding the first Sunday are considered to be in week 0. - var weekDayNumber = DAY_NUMBERS_SUN_FIRST[weekDay]; - var weekNumber = parseInt(value); - - // January 1st - var janFirst = new Date(date.year, 0, 1); - var endDate; - if (janFirst.getDay() === 0) { - // Jan 1st is a Sunday, and, hence in the 1st CW - endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1)); - } else { - // Jan 1st is not a Sunday, and, hence still in the 0th CW - endDate = __addDays(janFirst, 7-janFirst.getDay()+weekDayNumber+7*(weekNumber-1)); - } - date.day = endDate.getDate(); - date.month = endDate.getMonth(); - } else if ((value=getMatch('W'))) { - // ... and week number (Monday being first day of week) - // Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. - // All days in a new year preceding the first Monday are considered to be in week 0. - var weekDayNumber = DAY_NUMBERS_MON_FIRST[weekDay]; - var weekNumber = parseInt(value); - - // January 1st - var janFirst = new Date(date.year, 0, 1); - var endDate; - if (janFirst.getDay()===1) { - // Jan 1st is a Monday, and, hence in the 1st CW - endDate = __addDays(janFirst, weekDayNumber+7*(weekNumber-1)); - } else { - // Jan 1st is not a Monday, and, hence still in the 0th CW - endDate = __addDays(janFirst, 7-janFirst.getDay()+1+weekDayNumber+7*(weekNumber-1)); - } - - date.day = endDate.getDate(); - date.month = endDate.getMonth(); - } - } - - /* - tm_sec int seconds after the minute 0-61* - tm_min int minutes after the hour 0-59 - tm_hour int hours since midnight 0-23 - tm_mday int day of the month 1-31 - tm_mon int months since January 0-11 - tm_year int years since 1900 - tm_wday int days since Sunday 0-6 - tm_yday int days since January 1 0-365 - tm_isdst int Daylight Saving Time flag - */ - - var fullDate = new Date(date.year, date.month, date.day, date.hour, date.min, date.sec, 0); - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_sec, 'fullDate.getSeconds()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_min, 'fullDate.getMinutes()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_hour, 'fullDate.getHours()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mday, 'fullDate.getDate()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_mon, 'fullDate.getMonth()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_year, 'fullDate.getFullYear()-1900', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_wday, 'fullDate.getDay()', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_yday, '__arraySum(__isLeapYear(fullDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, fullDate.getMonth()-1)+fullDate.getDate()-1', 'i32') }}}; - {{{ makeSetValue('tm', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}}; - - // we need to convert the matched sequence into an integer array to take care of UTF-8 characters > 0x7F - // TODO: not sure that intArrayFromString handles all unicode characters correctly - return buf+intArrayFromString(matches[0]).length-1; - } - - return 0; - }, - strptime_l__deps: ['strptime'], - strptime_l: function(buf, format, tm) { - return _strptime(buf, format, tm); // no locale support yet - }, - - getdate: function(string) { - // struct tm *getdate(const char *string); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/getdate.html - // TODO: Implement. - return 0; - }, - - // ========================================================================== - // sys/time.h - // ========================================================================== - - nanosleep__deps: ['usleep'], - nanosleep: function(rqtp, rmtp) { - // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); - var seconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_sec, 'i32') }}}; - var nanoseconds = {{{ makeGetValue('rqtp', C_STRUCTS.timespec.tv_nsec, 'i32') }}}; - if (rmtp !== 0) { - {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_sec, '0', 'i32') }}}; - {{{ makeSetValue('rmtp', C_STRUCTS.timespec.tv_nsec, '0', 'i32') }}}; - } - return _usleep((seconds * 1e6) + (nanoseconds / 1000)); - }, - clock_gettime__deps: ['emscripten_get_now', 'emscripten_get_now_is_monotonic', '$ERRNO_CODES', '__setErrNo'], - clock_gettime: function(clk_id, tp) { - // int clock_gettime(clockid_t clk_id, struct timespec *tp); - var now; - if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { - now = Date.now(); - } else if (clk_id === {{{ cDefine('CLOCK_MONOTONIC') }}} && _emscripten_get_now_is_monotonic()) { - now = _emscripten_get_now(); - } else { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_sec, '(now/1000)|0', 'i32') }}}; // seconds - {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_nsec, '((now % 1000)*1000*1000)|0', 'i32') }}}; // nanoseconds - return 0; - }, - __clock_gettime: 'clock_gettime', // musl internal alias - clock_settime__deps: ['$ERRNO_CODES', '__setErrNo'], - clock_settime: function(clk_id, tp) { - // int clock_settime(clockid_t clk_id, const struct timespec *tp); - // Nothing. - ___setErrNo(clk_id === {{{ cDefine('CLOCK_REALTIME') }}} ? ERRNO_CODES.EPERM - : ERRNO_CODES.EINVAL); - return -1; - }, - clock_getres__deps: ['emscripten_get_now_res', 'emscripten_get_now_is_monotonic', '$ERRNO_CODES', '__setErrNo'], - clock_getres: function(clk_id, res) { - // int clock_getres(clockid_t clk_id, struct timespec *res); - var nsec; - if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { - nsec = 1000 * 1000; // educated guess that it's milliseconds - } else if (clk_id === {{{ cDefine('CLOCK_MONOTONIC') }}} && _emscripten_get_now_is_monotonic()) { - nsec = _emscripten_get_now_res(); - } else { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } - {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '(nsec/1000000000)|0', 'i32') }}}; - {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, 'nsec', 'i32') }}} // resolution is nanoseconds - return 0; - }, - clock_getcpuclockid__deps: ['$PROCINFO'], - clock_getcpuclockid: function(pid, clk_id) { - if (pid < 0) return ERRNO_CODES.ESRCH; - if (pid !== 0 && pid !== PROCINFO.pid) return ERRNO_CODES.ENOSYS; - if (clk_id) {{{ makeSetValue('clk_id', 0, 2/*CLOCK_PROCESS_CPUTIME_ID*/, 'i32') }}}; - return 0; - }, - // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/sys/time.h.html - gettimeofday: function(ptr) { - var now = Date.now(); - {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_sec, '(now/1000)|0', 'i32') }}}; // seconds - {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_usec, '((now % 1000)*1000)|0', 'i32') }}}; // microseconds - return 0; - }, - - // ========================================================================== - // sys/timeb.h - // ========================================================================== - - ftime: function(p) { - var millis = Date.now(); - {{{ makeSetValue('p', C_STRUCTS.timeb.time, '(millis/1000)|0', 'i32') }}}; - {{{ makeSetValue('p', C_STRUCTS.timeb.millitm, 'millis % 1000', 'i16') }}}; - {{{ makeSetValue('p', C_STRUCTS.timeb.timezone, '0', 'i16') }}}; // Obsolete field - {{{ makeSetValue('p', C_STRUCTS.timeb.dstflag, '0', 'i16') }}}; // Obsolete field - return 0; - }, - - // ========================================================================== - // sys/times.h - // ========================================================================== - - times__deps: ['memset'], - times: function(buffer) { - // clock_t times(struct tms *buffer); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/times.html - // NOTE: This is fake, since we can't calculate real CPU time usage in JS. - if (buffer !== 0) { - _memset(buffer, 0, {{{ C_STRUCTS.tms.__size__ }}}); - } - return 0; - }, - - // ========================================================================== - // sys/types.h - // ========================================================================== - // http://www.kernel.org/doc/man-pages/online/pages/man3/minor.3.html - makedev: function(maj, min) { - return ((maj) << 8 | (min)); - }, - gnu_dev_makedev: 'makedev', - major: function(dev) { - return ((dev) >> 8); - }, - gnu_dev_major: 'major', - minor: function(dev) { - return ((dev) & 0xff); - }, - gnu_dev_minor: 'minor', - - // ========================================================================== - // setjmp.h - // ========================================================================== - - saveSetjmp__asm: true, - saveSetjmp__sig: 'iii', - saveSetjmp__deps: ['realloc'], - saveSetjmp: function(env, label, table, size) { - // Not particularly fast: slow table lookup of setjmpId to label. But setjmp - // prevents relooping anyhow, so slowness is to be expected. And typical case - // is 1 setjmp per invocation, or less. - env = env|0; - label = label|0; - table = table|0; - size = size|0; - var i = 0; - setjmpId = (setjmpId+1)|0; - {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; - while ((i|0) < (size|0)) { - if ({{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}} == 0) { - {{{ makeSetValueAsm('table', '(i<<3)', 'setjmpId', 'i32') }}}; - {{{ makeSetValueAsm('table', '(i<<3)+4', 'label', 'i32') }}}; - // prepare next slot - {{{ makeSetValueAsm('table', '(i<<3)+8', '0', 'i32') }}}; - {{{ makeSetTempRet0('size') }}}; - return table | 0; - } - i = i+1|0; - } - // grow the table - size = (size*2)|0; - table = _realloc(table|0, 8*(size+1|0)|0) | 0; - table = _saveSetjmp(env|0, label|0, table|0, size|0) | 0; - {{{ makeSetTempRet0('size') }}}; - return table | 0; - }, - - testSetjmp__asm: true, - testSetjmp__sig: 'iii', - testSetjmp: function(id, table, size) { - id = id|0; - table = table|0; - size = size|0; - var i = 0, curr = 0; - while ((i|0) < (size|0)) { - curr = {{{ makeGetValueAsm('table', '(i<<3)', 'i32') }}}; - if ((curr|0) == 0) break; - if ((curr|0) == (id|0)) { - return {{{ makeGetValueAsm('table', '(i<<3)+4', 'i32') }}}; - } - i = i+1|0; - } - return 0; - }, - - setjmp__deps: ['saveSetjmp', 'testSetjmp'], - setjmp__inline: function(env) { - // Save the label - return '_saveSetjmp(' + env + ', label, setjmpTable)|0'; - }, - - longjmp__deps: ['saveSetjmp', 'testSetjmp'], - longjmp: function(env, value) { - asm['setThrew'](env, value || 1); - throw 'longjmp'; - }, - emscripten_longjmp__deps: ['longjmp'], - emscripten_longjmp: function(env, value) { - _longjmp(env, value); - }, - - // ========================================================================== - // sys/wait.h - // ========================================================================== - - wait__deps: ['$ERRNO_CODES', '__setErrNo'], - wait: function(stat_loc) { - // pid_t wait(int *stat_loc); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/wait.html - // Makes no sense in a single-process environment. - ___setErrNo(ERRNO_CODES.ECHILD); - return -1; - }, - // NOTE: These aren't really the same, but we use the same stub for them all. - waitid: 'wait', - waitpid: 'wait', - wait3: 'wait', - wait4: 'wait', - - // ========================================================================== - // locale.h - // ========================================================================== - - $LOCALE: { - curr: 0, - check: function(locale) { - if (locale) locale = Pointer_stringify(locale); - return locale === 'C' || locale === 'POSIX' || !locale; - }, - }, - - newlocale__deps: ['$LOCALE', 'calloc'], - newlocale: function(mask, locale, base) { - if (!LOCALE.check(locale)) { - ___setErrNo(ERRNO_CODES.ENOENT); - return 0; - } - if (!base) base = _calloc(1, 4); - return base; - }, - - freelocale__deps: ['$LOCALE', 'free'], - freelocale: function(locale) { - _free(locale); - }, - - uselocale__deps: ['$LOCALE'], - uselocale: function(locale) { - var old = LOCALE.curr; - if (locale) LOCALE.curr = locale; - return old; - }, - - setlocale__deps: ['$LOCALE'], - setlocale: function(category, locale) { - if (LOCALE.check(locale)) { - if (!_setlocale.ret) _setlocale.ret = allocate(intArrayFromString('C'), 'i8', ALLOC_NORMAL); - return _setlocale.ret; - } - return 0; - }, - - localeconv: function() { - // %struct.timeval = type { char* decimal point, other stuff... } - // var indexes = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }); - var me = _localeconv; - if (!me.ret) { - // These are defaults from the "C" locale - me.ret = allocate([ - allocate(intArrayFromString('.'), 'i8', ALLOC_NORMAL),0,0,0, // decimal_point - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // thousands_sep - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // grouping - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // int_curr_symbol - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // currency_symbol - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // mon_decimal_point - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // mon_thousands_sep - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // mon_grouping - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0, // positive_sign - allocate(intArrayFromString(''), 'i8', ALLOC_NORMAL),0,0,0 // negative_sign - ], 'i8*', ALLOC_NORMAL); // Allocate strings in lconv, still don't allocate chars - } - return me.ret; - }, - - __locale_mb_cur_max: function() { throw '__locale_mb_cur_max not implemented' }, - - // ========================================================================== - // langinfo.h - // ========================================================================== - - nl_langinfo: function(item) { - // char *nl_langinfo(nl_item item); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/nl_langinfo.html - var result; - switch (item) { - case {{{ cDefine('CODESET') }}}: - result = 'ANSI_X3.4-1968'; - break; - case {{{ cDefine('D_T_FMT') }}}: - result = '%a %b %e %H:%M:%S %Y'; - break; - case {{{ cDefine('D_FMT') }}}: - result = '%m/%d/%y'; - break; - case {{{ cDefine('T_FMT') }}}: - result = '%H:%M:%S'; - break; - case {{{ cDefine('T_FMT_AMPM') }}}: - result = '%I:%M:%S %p'; - break; - case {{{ cDefine('AM_STR') }}}: - result = 'AM'; - break; - case {{{ cDefine('PM_STR') }}}: - result = 'PM'; - break; - case {{{ cDefine('DAY_1') }}}: - result = 'Sunday'; - break; - case {{{ cDefine('DAY_2') }}}: - result = 'Monday'; - break; - case {{{ cDefine('DAY_3') }}}: - result = 'Tuesday'; - break; - case {{{ cDefine('DAY_4') }}}: - result = 'Wednesday'; - break; - case {{{ cDefine('DAY_5') }}}: - result = 'Thursday'; - break; - case {{{ cDefine('DAY_6') }}}: - result = 'Friday'; - break; - case {{{ cDefine('DAY_7') }}}: - result = 'Saturday'; - break; - case {{{ cDefine('ABDAY_1') }}}: - result = 'Sun'; - break; - case {{{ cDefine('ABDAY_2') }}}: - result = 'Mon'; - break; - case {{{ cDefine('ABDAY_3') }}}: - result = 'Tue'; - break; - case {{{ cDefine('ABDAY_4') }}}: - result = 'Wed'; - break; - case {{{ cDefine('ABDAY_5') }}}: - result = 'Thu'; - break; - case {{{ cDefine('ABDAY_6') }}}: - result = 'Fri'; - break; - case {{{ cDefine('ABDAY_7') }}}: - result = 'Sat'; - break; - case {{{ cDefine('MON_1') }}}: - result = 'January'; - break; - case {{{ cDefine('MON_2') }}}: - result = 'February'; - break; - case {{{ cDefine('MON_3') }}}: - result = 'March'; - break; - case {{{ cDefine('MON_4') }}}: - result = 'April'; - break; - case {{{ cDefine('MON_5') }}}: - result = 'May'; - break; - case {{{ cDefine('MON_6') }}}: - result = 'June'; - break; - case {{{ cDefine('MON_7') }}}: - result = 'July'; - break; - case {{{ cDefine('MON_8') }}}: - result = 'August'; - break; - case {{{ cDefine('MON_9') }}}: - result = 'September'; - break; - case {{{ cDefine('MON_10') }}}: - result = 'October'; - break; - case {{{ cDefine('MON_11') }}}: - result = 'November'; - break; - case {{{ cDefine('MON_12') }}}: - result = 'December'; - break; - case {{{ cDefine('ABMON_1') }}}: - result = 'Jan'; - break; - case {{{ cDefine('ABMON_2') }}}: - result = 'Feb'; - break; - case {{{ cDefine('ABMON_3') }}}: - result = 'Mar'; - break; - case {{{ cDefine('ABMON_4') }}}: - result = 'Apr'; - break; - case {{{ cDefine('ABMON_5') }}}: - result = 'May'; - break; - case {{{ cDefine('ABMON_6') }}}: - result = 'Jun'; - break; - case {{{ cDefine('ABMON_7') }}}: - result = 'Jul'; - break; - case {{{ cDefine('ABMON_8') }}}: - result = 'Aug'; - break; - case {{{ cDefine('ABMON_9') }}}: - result = 'Sep'; - break; - case {{{ cDefine('ABMON_10') }}}: - result = 'Oct'; - break; - case {{{ cDefine('ABMON_11') }}}: - result = 'Nov'; - break; - case {{{ cDefine('ABMON_12') }}}: - result = 'Dec'; - break; - case {{{ cDefine('ALT_DIGITS') }}}: - result = ''; - break; - case {{{ cDefine('RADIXCHAR') }}}: - result = '.'; - break; - case {{{ cDefine('THOUSEP') }}}: - result = ''; - break; - case {{{ cDefine('YESEXPR') }}}: - result = '^[yY]'; - break; - case {{{ cDefine('NOEXPR') }}}: - result = '^[nN]'; - break; - case {{{ cDefine('CRNCYSTR') }}}: - result = '-'; - break; - case {{{ cDefine('ERA') }}}: - case {{{ cDefine('ERA_D_FMT') }}}: - case {{{ cDefine('ERA_D_T_FMT') }}}: - case {{{ cDefine('ERA_T_FMT') }}}: - default: - result = ''; - break; - } - - var me = _nl_langinfo; - if (!me.ret) me.ret = _malloc(32); - writeAsciiToMemory(result, me.ret); - return me.ret; - }, - - // ========================================================================== - // errno.h - // ========================================================================== - - $ERRNO_CODES: { - EPERM: {{{ cDefine('EPERM') }}}, - ENOENT: {{{ cDefine('ENOENT') }}}, - ESRCH: {{{ cDefine('ESRCH') }}}, - EINTR: {{{ cDefine('EINTR') }}}, - EIO: {{{ cDefine('EIO') }}}, - ENXIO: {{{ cDefine('ENXIO') }}}, - E2BIG: {{{ cDefine('E2BIG') }}}, - ENOEXEC: {{{ cDefine('ENOEXEC') }}}, - EBADF: {{{ cDefine('EBADF') }}}, - ECHILD: {{{ cDefine('ECHILD') }}}, - EAGAIN: {{{ cDefine('EAGAIN') }}}, - EWOULDBLOCK: {{{ cDefine('EWOULDBLOCK') }}}, - ENOMEM: {{{ cDefine('ENOMEM') }}}, - EACCES: {{{ cDefine('EACCES') }}}, - EFAULT: {{{ cDefine('EFAULT') }}}, - ENOTBLK: {{{ cDefine('ENOTBLK') }}}, - EBUSY: {{{ cDefine('EBUSY') }}}, - EEXIST: {{{ cDefine('EEXIST') }}}, - EXDEV: {{{ cDefine('EXDEV') }}}, - ENODEV: {{{ cDefine('ENODEV') }}}, - ENOTDIR: {{{ cDefine('ENOTDIR') }}}, - EISDIR: {{{ cDefine('EISDIR') }}}, - EINVAL: {{{ cDefine('EINVAL') }}}, - ENFILE: {{{ cDefine('ENFILE') }}}, - EMFILE: {{{ cDefine('EMFILE') }}}, - ENOTTY: {{{ cDefine('ENOTTY') }}}, - ETXTBSY: {{{ cDefine('ETXTBSY') }}}, - EFBIG: {{{ cDefine('EFBIG') }}}, - ENOSPC: {{{ cDefine('ENOSPC') }}}, - ESPIPE: {{{ cDefine('ESPIPE') }}}, - EROFS: {{{ cDefine('EROFS') }}}, - EMLINK: {{{ cDefine('EMLINK') }}}, - EPIPE: {{{ cDefine('EPIPE') }}}, - EDOM: {{{ cDefine('EDOM') }}}, - ERANGE: {{{ cDefine('ERANGE') }}}, - ENOMSG: {{{ cDefine('ENOMSG') }}}, - EIDRM: {{{ cDefine('EIDRM') }}}, - ECHRNG: {{{ cDefine('ECHRNG') }}}, - EL2NSYNC: {{{ cDefine('EL2NSYNC') }}}, - EL3HLT: {{{ cDefine('EL3HLT') }}}, - EL3RST: {{{ cDefine('EL3RST') }}}, - ELNRNG: {{{ cDefine('ELNRNG') }}}, - EUNATCH: {{{ cDefine('EUNATCH') }}}, - ENOCSI: {{{ cDefine('ENOCSI') }}}, - EL2HLT: {{{ cDefine('EL2HLT') }}}, - EDEADLK: {{{ cDefine('EDEADLK') }}}, - ENOLCK: {{{ cDefine('ENOLCK') }}}, - EBADE: {{{ cDefine('EBADE') }}}, - EBADR: {{{ cDefine('EBADR') }}}, - EXFULL: {{{ cDefine('EXFULL') }}}, - ENOANO: {{{ cDefine('ENOANO') }}}, - EBADRQC: {{{ cDefine('EBADRQC') }}}, - EBADSLT: {{{ cDefine('EBADSLT') }}}, - EDEADLOCK: {{{ cDefine('EDEADLOCK') }}}, - EBFONT: {{{ cDefine('EBFONT') }}}, - ENOSTR: {{{ cDefine('ENOSTR') }}}, - ENODATA: {{{ cDefine('ENODATA') }}}, - ETIME: {{{ cDefine('ETIME') }}}, - ENOSR: {{{ cDefine('ENOSR') }}}, - ENONET: {{{ cDefine('ENONET') }}}, - ENOPKG: {{{ cDefine('ENOPKG') }}}, - EREMOTE: {{{ cDefine('EREMOTE') }}}, - ENOLINK: {{{ cDefine('ENOLINK') }}}, - EADV: {{{ cDefine('EADV') }}}, - ESRMNT: {{{ cDefine('ESRMNT') }}}, - ECOMM: {{{ cDefine('ECOMM') }}}, - EPROTO: {{{ cDefine('EPROTO') }}}, - EMULTIHOP: {{{ cDefine('EMULTIHOP') }}}, - EDOTDOT: {{{ cDefine('EDOTDOT') }}}, - EBADMSG: {{{ cDefine('EBADMSG') }}}, - ENOTUNIQ: {{{ cDefine('ENOTUNIQ') }}}, - EBADFD: {{{ cDefine('EBADFD') }}}, - EREMCHG: {{{ cDefine('EREMCHG') }}}, - ELIBACC: {{{ cDefine('ELIBACC') }}}, - ELIBBAD: {{{ cDefine('ELIBBAD') }}}, - ELIBSCN: {{{ cDefine('ELIBSCN') }}}, - ELIBMAX: {{{ cDefine('ELIBMAX') }}}, - ELIBEXEC: {{{ cDefine('ELIBEXEC') }}}, - ENOSYS: {{{ cDefine('ENOSYS') }}}, - ENOTEMPTY: {{{ cDefine('ENOTEMPTY') }}}, - ENAMETOOLONG: {{{ cDefine('ENAMETOOLONG') }}}, - ELOOP: {{{ cDefine('ELOOP') }}}, - EOPNOTSUPP: {{{ cDefine('EOPNOTSUPP') }}}, - EPFNOSUPPORT: {{{ cDefine('EPFNOSUPPORT') }}}, - ECONNRESET: {{{ cDefine('ECONNRESET') }}}, - ENOBUFS: {{{ cDefine('ENOBUFS') }}}, - EAFNOSUPPORT: {{{ cDefine('EAFNOSUPPORT') }}}, - EPROTOTYPE: {{{ cDefine('EPROTOTYPE') }}}, - ENOTSOCK: {{{ cDefine('ENOTSOCK') }}}, - ENOPROTOOPT: {{{ cDefine('ENOPROTOOPT') }}}, - ESHUTDOWN: {{{ cDefine('ESHUTDOWN') }}}, - ECONNREFUSED: {{{ cDefine('ECONNREFUSED') }}}, - EADDRINUSE: {{{ cDefine('EADDRINUSE') }}}, - ECONNABORTED: {{{ cDefine('ECONNABORTED') }}}, - ENETUNREACH: {{{ cDefine('ENETUNREACH') }}}, - ENETDOWN: {{{ cDefine('ENETDOWN') }}}, - ETIMEDOUT: {{{ cDefine('ETIMEDOUT') }}}, - EHOSTDOWN: {{{ cDefine('EHOSTDOWN') }}}, - EHOSTUNREACH: {{{ cDefine('EHOSTUNREACH') }}}, - EINPROGRESS: {{{ cDefine('EINPROGRESS') }}}, - EALREADY: {{{ cDefine('EALREADY') }}}, - EDESTADDRREQ: {{{ cDefine('EDESTADDRREQ') }}}, - EMSGSIZE: {{{ cDefine('EMSGSIZE') }}}, - EPROTONOSUPPORT: {{{ cDefine('EPROTONOSUPPORT') }}}, - ESOCKTNOSUPPORT: {{{ cDefine('ESOCKTNOSUPPORT') }}}, - EADDRNOTAVAIL: {{{ cDefine('EADDRNOTAVAIL') }}}, - ENETRESET: {{{ cDefine('ENETRESET') }}}, - EISCONN: {{{ cDefine('EISCONN') }}}, - ENOTCONN: {{{ cDefine('ENOTCONN') }}}, - ETOOMANYREFS: {{{ cDefine('ETOOMANYREFS') }}}, - EUSERS: {{{ cDefine('EUSERS') }}}, - EDQUOT: {{{ cDefine('EDQUOT') }}}, - ESTALE: {{{ cDefine('ESTALE') }}}, - ENOTSUP: {{{ cDefine('ENOTSUP') }}}, - ENOMEDIUM: {{{ cDefine('ENOMEDIUM') }}}, - EILSEQ: {{{ cDefine('EILSEQ') }}}, - EOVERFLOW: {{{ cDefine('EOVERFLOW') }}}, - ECANCELED: {{{ cDefine('ECANCELED') }}}, - ENOTRECOVERABLE: {{{ cDefine('ENOTRECOVERABLE') }}}, - EOWNERDEAD: {{{ cDefine('EOWNERDEAD') }}}, - ESTRPIPE: {{{ cDefine('ESTRPIPE') }}}, - }, - $ERRNO_MESSAGES: { - 0: 'Success', - {{{ cDefine('EPERM') }}}: 'Not super-user', - {{{ cDefine('ENOENT') }}}: 'No such file or directory', - {{{ cDefine('ESRCH') }}}: 'No such process', - {{{ cDefine('EINTR') }}}: 'Interrupted system call', - {{{ cDefine('EIO') }}}: 'I/O error', - {{{ cDefine('ENXIO') }}}: 'No such device or address', - {{{ cDefine('E2BIG') }}}: 'Arg list too long', - {{{ cDefine('ENOEXEC') }}}: 'Exec format error', - {{{ cDefine('EBADF') }}}: 'Bad file number', - {{{ cDefine('ECHILD') }}}: 'No children', - {{{ cDefine('EWOULDBLOCK') }}}: 'No more processes', - {{{ cDefine('ENOMEM') }}}: 'Not enough core', - {{{ cDefine('EACCES') }}}: 'Permission denied', - {{{ cDefine('EFAULT') }}}: 'Bad address', - {{{ cDefine('ENOTBLK') }}}: 'Block device required', - {{{ cDefine('EBUSY') }}}: 'Mount device busy', - {{{ cDefine('EEXIST') }}}: 'File exists', - {{{ cDefine('EXDEV') }}}: 'Cross-device link', - {{{ cDefine('ENODEV') }}}: 'No such device', - {{{ cDefine('ENOTDIR') }}}: 'Not a directory', - {{{ cDefine('EISDIR') }}}: 'Is a directory', - {{{ cDefine('EINVAL') }}}: 'Invalid argument', - {{{ cDefine('ENFILE') }}}: 'Too many open files in system', - {{{ cDefine('EMFILE') }}}: 'Too many open files', - {{{ cDefine('ENOTTY') }}}: 'Not a typewriter', - {{{ cDefine('ETXTBSY') }}}: 'Text file busy', - {{{ cDefine('EFBIG') }}}: 'File too large', - {{{ cDefine('ENOSPC') }}}: 'No space left on device', - {{{ cDefine('ESPIPE') }}}: 'Illegal seek', - {{{ cDefine('EROFS') }}}: 'Read only file system', - {{{ cDefine('EMLINK') }}}: 'Too many links', - {{{ cDefine('EPIPE') }}}: 'Broken pipe', - {{{ cDefine('EDOM') }}}: 'Math arg out of domain of func', - {{{ cDefine('ERANGE') }}}: 'Math result not representable', - {{{ cDefine('ENOMSG') }}}: 'No message of desired type', - {{{ cDefine('EIDRM') }}}: 'Identifier removed', - {{{ cDefine('ECHRNG') }}}: 'Channel number out of range', - {{{ cDefine('EL2NSYNC') }}}: 'Level 2 not synchronized', - {{{ cDefine('EL3HLT') }}}: 'Level 3 halted', - {{{ cDefine('EL3RST') }}}: 'Level 3 reset', - {{{ cDefine('ELNRNG') }}}: 'Link number out of range', - {{{ cDefine('EUNATCH') }}}: 'Protocol driver not attached', - {{{ cDefine('ENOCSI') }}}: 'No CSI structure available', - {{{ cDefine('EL2HLT') }}}: 'Level 2 halted', - {{{ cDefine('EDEADLK') }}}: 'Deadlock condition', - {{{ cDefine('ENOLCK') }}}: 'No record locks available', - {{{ cDefine('EBADE') }}}: 'Invalid exchange', - {{{ cDefine('EBADR') }}}: 'Invalid request descriptor', - {{{ cDefine('EXFULL') }}}: 'Exchange full', - {{{ cDefine('ENOANO') }}}: 'No anode', - {{{ cDefine('EBADRQC') }}}: 'Invalid request code', - {{{ cDefine('EBADSLT') }}}: 'Invalid slot', - {{{ cDefine('EDEADLOCK') }}}: 'File locking deadlock error', - {{{ cDefine('EBFONT') }}}: 'Bad font file fmt', - {{{ cDefine('ENOSTR') }}}: 'Device not a stream', - {{{ cDefine('ENODATA') }}}: 'No data (for no delay io)', - {{{ cDefine('ETIME') }}}: 'Timer expired', - {{{ cDefine('ENOSR') }}}: 'Out of streams resources', - {{{ cDefine('ENONET') }}}: 'Machine is not on the network', - {{{ cDefine('ENOPKG') }}}: 'Package not installed', - {{{ cDefine('EREMOTE') }}}: 'The object is remote', - {{{ cDefine('ENOLINK') }}}: 'The link has been severed', - {{{ cDefine('EADV') }}}: 'Advertise error', - {{{ cDefine('ESRMNT') }}}: 'Srmount error', - {{{ cDefine('ECOMM') }}}: 'Communication error on send', - {{{ cDefine('EPROTO') }}}: 'Protocol error', - {{{ cDefine('EMULTIHOP') }}}: 'Multihop attempted', - {{{ cDefine('EDOTDOT') }}}: 'Cross mount point (not really error)', - {{{ cDefine('EBADMSG') }}}: 'Trying to read unreadable message', - {{{ cDefine('ENOTUNIQ') }}}: 'Given log. name not unique', - {{{ cDefine('EBADFD') }}}: 'f.d. invalid for this operation', - {{{ cDefine('EREMCHG') }}}: 'Remote address changed', - {{{ cDefine('ELIBACC') }}}: 'Can access a needed shared lib', - {{{ cDefine('ELIBBAD') }}}: 'Accessing a corrupted shared lib', - {{{ cDefine('ELIBSCN') }}}: '.lib section in a.out corrupted', - {{{ cDefine('ELIBMAX') }}}: 'Attempting to link in too many libs', - {{{ cDefine('ELIBEXEC') }}}: 'Attempting to exec a shared library', - {{{ cDefine('ENOSYS') }}}: 'Function not implemented', - {{{ cDefine('ENOTEMPTY') }}}: 'Directory not empty', - {{{ cDefine('ENAMETOOLONG') }}}: 'File or path name too long', - {{{ cDefine('ELOOP') }}}: 'Too many symbolic links', - {{{ cDefine('EOPNOTSUPP') }}}: 'Operation not supported on transport endpoint', - {{{ cDefine('EPFNOSUPPORT') }}}: 'Protocol family not supported', - {{{ cDefine('ECONNRESET') }}}: 'Connection reset by peer', - {{{ cDefine('ENOBUFS') }}}: 'No buffer space available', - {{{ cDefine('EAFNOSUPPORT') }}}: 'Address family not supported by protocol family', - {{{ cDefine('EPROTOTYPE') }}}: 'Protocol wrong type for socket', - {{{ cDefine('ENOTSOCK') }}}: 'Socket operation on non-socket', - {{{ cDefine('ENOPROTOOPT') }}}: 'Protocol not available', - {{{ cDefine('ESHUTDOWN') }}}: 'Can\'t send after socket shutdown', - {{{ cDefine('ECONNREFUSED') }}}: 'Connection refused', - {{{ cDefine('EADDRINUSE') }}}: 'Address already in use', - {{{ cDefine('ECONNABORTED') }}}: 'Connection aborted', - {{{ cDefine('ENETUNREACH') }}}: 'Network is unreachable', - {{{ cDefine('ENETDOWN') }}}: 'Network interface is not configured', - {{{ cDefine('ETIMEDOUT') }}}: 'Connection timed out', - {{{ cDefine('EHOSTDOWN') }}}: 'Host is down', - {{{ cDefine('EHOSTUNREACH') }}}: 'Host is unreachable', - {{{ cDefine('EINPROGRESS') }}}: 'Connection already in progress', - {{{ cDefine('EALREADY') }}}: 'Socket already connected', - {{{ cDefine('EDESTADDRREQ') }}}: 'Destination address required', - {{{ cDefine('EMSGSIZE') }}}: 'Message too long', - {{{ cDefine('EPROTONOSUPPORT') }}}: 'Unknown protocol', - {{{ cDefine('ESOCKTNOSUPPORT') }}}: 'Socket type not supported', - {{{ cDefine('EADDRNOTAVAIL') }}}: 'Address not available', - {{{ cDefine('ENETRESET') }}}: 'Connection reset by network', - {{{ cDefine('EISCONN') }}}: 'Socket is already connected', - {{{ cDefine('ENOTCONN') }}}: 'Socket is not connected', - {{{ cDefine('ETOOMANYREFS') }}}: 'Too many references', - {{{ cDefine('EUSERS') }}}: 'Too many users', - {{{ cDefine('EDQUOT') }}}: 'Quota exceeded', - {{{ cDefine('ESTALE') }}}: 'Stale file handle', - {{{ cDefine('ENOTSUP') }}}: 'Not supported', - {{{ cDefine('ENOMEDIUM') }}}: 'No medium (in tape drive)', - {{{ cDefine('EILSEQ') }}}: 'Illegal byte sequence', - {{{ cDefine('EOVERFLOW') }}}: 'Value too large for defined data type', - {{{ cDefine('ECANCELED') }}}: 'Operation canceled', - {{{ cDefine('ENOTRECOVERABLE') }}}: 'State not recoverable', - {{{ cDefine('EOWNERDEAD') }}}: 'Previous owner died', - {{{ cDefine('ESTRPIPE') }}}: 'Streams pipe error', - }, - __setErrNo: function(value) { - if (Module['___errno_location']) {{{ makeSetValue("Module['___errno_location']()", 0, 'value', 'i32') }}}; -#if ASSERTIONS - else Module.printErr('failed to set errno from JS'); -#endif - return value; - }, - - // ========================================================================== - // sys/resource.h - // ========================================================================== - - // TODO: Implement for real. - getrlimit: function(resource, rlp) { - // int getrlimit(int resource, struct rlimit *rlp); - {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_cur, '-1', 'i32') }}}; // RLIM_INFINITY - {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_cur + 4, '-1', 'i32') }}}; // RLIM_INFINITY - {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_max, '-1', 'i32') }}}; // RLIM_INFINITY - {{{ makeSetValue('rlp', C_STRUCTS.rlimit.rlim_max + 4, '-1', 'i32') }}}; // RLIM_INFINITY - return 0; - }, - setrlimit: function(resource, rlp) { - // int setrlimit(int resource, const struct rlimit *rlp) - return 0; - }, - - // TODO: Implement for real. We just do time used, and no useful data - getrusage: function(resource, rlp) { - // int getrusage(int resource, struct rusage *rlp); - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_sec, '1', 'i32') }}}; - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_utime.tv_usec, '2', 'i32') }}}; - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_sec, '3', 'i32') }}}; - {{{ makeSetValue('rlp', C_STRUCTS.rusage.ru_stime.tv_usec, '4', 'i32') }}}; - return 0; - }, - - // ========================================================================== - // sched.h (stubs only - no thread support yet!) - // ========================================================================== - sched_yield: function() { - return 0; - }, - - // ========================================================================== - // malloc.h - // ========================================================================== - - memalign: function(boundary, size) { - // leaks, and even returns an invalid pointer. Horrible hack... but then, this is a deprecated function... - var ret = Runtime.staticAlloc(size + boundary); - return ret + boundary - (ret % boundary); - }, - - posix_memalign__deps: ['memalign'], - posix_memalign: function(memptr, alignment, size) { - var ptr = _memalign(alignment, size); - {{{ makeSetValue('memptr', '0', 'ptr', 'i8*') }}}; - return 0; - }, - - // ========================================================================== - // arpa/inet.h - // ========================================================================== - htonl: function(value) { - return ((value & 0xff) << 24) + ((value & 0xff00) << 8) + - ((value & 0xff0000) >>> 8) + ((value & 0xff000000) >>> 24); - }, - htons: function(value) { - return ((value & 0xff) << 8) + ((value & 0xff00) >> 8); - }, - ntohl: 'htonl', - ntohs: 'htons', - - // old ipv4 only functions - inet_addr__deps: ['_inet_pton4_raw'], - inet_addr: function(ptr) { - var addr = __inet_pton4_raw(Pointer_stringify(ptr)); - if (addr === null) { - return -1; - } - return addr; - }, - inet_ntoa__deps: ['_inet_ntop4_raw'], - inet_ntoa: function(in_addr) { - if (!_inet_ntoa.buffer) { - _inet_ntoa.buffer = _malloc(1024); - } - var addr = {{{ makeGetValue('in_addr', '0', 'i32') }}}; - var str = __inet_ntop4_raw(addr); - writeStringToMemory(str.substr(0, 1024), _inet_ntoa.buffer); - return _inet_ntoa.buffer; - }, - inet_aton__deps: ['_inet_pton4_raw'], - inet_aton: function(cp, inp) { - var addr = __inet_pton4_raw(Pointer_stringify(cp)); - if (addr === null) { - return 0; - } - {{{ makeSetValue('inp', '0', 'addr', 'i32') }}}; - return 1; - }, - - // new ipv4 / ipv6 functions - _inet_ntop4_raw: function(addr) { - return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) - }, - _inet_ntop4__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop4_raw'], - _inet_ntop4: function(src, dst, size) { - var addr = {{{ makeGetValue('src', '0', 'i32') }}}; - var str = __inet_ntop4_raw(addr); - if (str.length+1 > size) { - ___setErrNo(ERRNO_CODES.ENOSPC); - return 0; - } - writeStringToMemory(str, dst); - return dst; - }, - _inet_ntop6_raw__deps: ['ntohs', '_inet_ntop4_raw'], - _inet_ntop6_raw: function(ints) { - // ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4 - // Format for IPv4 compatible and mapped 128-bit IPv6 Addresses - // 128-bits are split into eight 16-bit words - // stored in network byte order (big-endian) - // | 80 bits | 16 | 32 bits | - // +-----------------------------------------------------------------+ - // | 10 bytes | 2 | 4 bytes | - // +--------------------------------------+--------------------------+ - // + 5 words | 1 | 2 words | - // +--------------------------------------+--------------------------+ - // |0000..............................0000|0000| IPv4 ADDRESS | (compatible) - // +--------------------------------------+----+---------------------+ - // |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped) - // +--------------------------------------+----+---------------------+ - var str = ""; - var word = 0; - var longest = 0; - var lastzero = 0; - var zstart = 0; - var len = 0; - var i = 0; - var parts = [ - ints[0] & 0xffff, - (ints[0] >> 16), - ints[1] & 0xffff, - (ints[1] >> 16), - ints[2] & 0xffff, - (ints[2] >> 16), - ints[3] & 0xffff, - (ints[3] >> 16) - ]; - - // Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses - - var hasipv4 = true; - var v4part = ""; - // check if the 10 high-order bytes are all zeros (first 5 words) - for (i = 0; i < 5; i++) { - if (parts[i] !== 0) { hasipv4 = false; break; } - } - - if (hasipv4) { - // low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words) - v4part = __inet_ntop4_raw(parts[6] | (parts[7] << 16)); - // IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word) - if (parts[5] === -1) { - str = "::ffff:"; - str += v4part; - return str; - } - // IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word) - if (parts[5] === 0) { - str = "::"; - //special case IPv6 addresses - if(v4part === "0.0.0.0") v4part = ""; // any/unspecified address - if(v4part === "0.0.0.1") v4part = "1";// loopback address - str += v4part; - return str; - } - } - - // Handle all other IPv6 addresses - - // first run to find the longest contiguous zero words - for (word = 0; word < 8; word++) { - if (parts[word] === 0) { - if (word - lastzero > 1) { - len = 0; - } - lastzero = word; - len++; - } - if (len > longest) { - longest = len; - zstart = word - longest + 1; - } - } - - for (word = 0; word < 8; word++) { - if (longest > 1) { - // compress contiguous zeros - to produce "::" - if (parts[word] === 0 && word >= zstart && word < (zstart + longest) ) { - if (word === zstart) { - str += ":"; - if (zstart === 0) str += ":"; //leading zeros case - } - continue; - } - } - // converts 16-bit words from big-endian to little-endian before converting to hex string - str += Number(_ntohs(parts[word] & 0xffff)).toString(16); - str += word < 7 ? ":" : ""; - } - return str; - }, - _inet_ntop6__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop6_raw'], - _inet_ntop6: function(src, dst, size) { - var addr = [ - {{{ makeGetValue('src', '0', 'i32') }}}, {{{ makeGetValue('src', '4', 'i32') }}}, - {{{ makeGetValue('src', '8', 'i32') }}}, {{{ makeGetValue('src', '12', 'i32') }}} - ]; - var str = __inet_ntop6_raw(addr); - if (str.length+1 > size) { - ___setErrNo(ERRNO_CODES.ENOSPC); - return 0; - } - writeStringToMemory(str, dst); - return dst; - }, - inet_ntop__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntop4', '_inet_ntop6'], - inet_ntop: function(af, src, dst, size) { - // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html - switch (af) { - case {{{ cDefine('AF_INET') }}}: - return __inet_ntop4(src, dst, size); - case {{{ cDefine('AF_INET6') }}}: - return __inet_ntop6(src, dst, size); - default: - ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); - return 0; - } - }, - - _inet_pton4_raw: function(str) { - var b = str.split('.'); - for (var i = 0; i < 4; i++) { - var tmp = Number(b[i]); - if (isNaN(tmp)) return null; - b[i] = tmp; - } - return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0; - }, - _inet_pton4__deps: ['_inet_pton4_raw'], - _inet_pton4: function(src, dst) { - var ret = __inet_pton4_raw(Pointer_stringify(src)); - if (ret === null) { - return 0; - } - {{{ makeSetValue('dst', '0', 'ret', 'i32') }}}; - return 1; - }, - _inet_pton6_raw__deps: ['htons'], - _inet_pton6_raw: function(str) { - var words; - var w, offset, z, i; - /* http://home.deds.nl/~aeron/regex/ */ - var valid6regx = /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i - var parts = []; - if (!valid6regx.test(str)) { - return null; - } - if (str === "::") { - return [0, 0, 0, 0, 0, 0, 0, 0]; - } - // Z placeholder to keep track of zeros when splitting the string on ":" - if (str.indexOf("::") === 0) { - str = str.replace("::", "Z:"); // leading zeros case - } else { - str = str.replace("::", ":Z:"); - } - - if (str.indexOf(".") > 0) { - // parse IPv4 embedded stress - str = str.replace(new RegExp('[.]', 'g'), ":"); - words = str.split(":"); - words[words.length-4] = parseInt(words[words.length-4]) + parseInt(words[words.length-3])*256; - words[words.length-3] = parseInt(words[words.length-2]) + parseInt(words[words.length-1])*256; - words = words.slice(0, words.length-2); - } else { - words = str.split(":"); - } - - offset = 0; z = 0; - for (w=0; w < words.length; w++) { - if (typeof words[w] === 'string') { - if (words[w] === 'Z') { - // compressed zeros - write appropriate number of zero words - for (z = 0; z < (8 - words.length+1); z++) { - parts[w+z] = 0; - } - offset = z-1; - } else { - // parse hex to field to 16-bit value and write it in network byte-order - parts[w+offset] = _htons(parseInt(words[w],16)); - } - } else { - // parsed IPv4 words - parts[w+offset] = words[w]; - } - } - return [ - (parts[1] << 16) | parts[0], - (parts[3] << 16) | parts[2], - (parts[5] << 16) | parts[4], - (parts[7] << 16) | parts[6] - ]; - }, - _inet_pton6__deps: ['_inet_pton6_raw'], - _inet_pton6: function(src, dst) { - var ints = __inet_pton6_raw(Pointer_stringify(src)); - if (ints === null) { - return 0; - } - for (var i = 0; i < 4; i++) { - {{{ makeSetValue('dst', 'i*4', 'ints[i]', 'i32') }}}; - } - return 1; - }, - inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_pton4', '_inet_pton6'], - inet_pton: function(af, src, dst) { - // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_pton.html - switch (af) { - case {{{ cDefine('AF_INET') }}}: - return __inet_pton4(src, dst); - case {{{ cDefine('AF_INET6') }}}: - return __inet_pton6(src, dst); - default: - ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); - return -1; - } - }, - - // ========================================================================== - // net/if.h - // ========================================================================== - - if_nametoindex: function(a) { - return 0; - }, - if_indextoname: function(a, b) { - return 0; - }, - if_nameindex: function() { - return 0; - }, - if_freenameindex: function(a) { - }, - - // ========================================================================== - // netinet/in.h - // ========================================================================== - -#if USE_PTHREADS - in6addr_any: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_any = PthreadWorkerInit._in6addr_any; else PthreadWorkerInit._in6addr_any = _in6addr_any = allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', - in6addr_loopback: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_loopback = PthreadWorkerInit._in6addr_loopback; else PthreadWorkerInit._in6addr_loopback = _in6addr_loopback = allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', -#else - in6addr_any: - 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', - in6addr_loopback: - 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', -#endif - - // ========================================================================== - // netdb.h - // ========================================================================== - -#if USE_PTHREADS - __h_errno_state: '; if (ENVIRONMENT_IS_PTHREAD) ___h_errno_state = PthreadWorkerInit.___h_errno_state; else PthreadWorkerInit.___h_errno_state = ___h_errno_state = allocate(1, "i32", ALLOC_STATIC)', -#else - __h_errno_state: 'allocate(1, "i32", ALLOC_STATIC)', -#endif - __h_errno_location__deps: ['__h_errno_state'], - __h_errno_location: function() { - return ___h_errno_state; - }, - - // We can't actually resolve hostnames in the browser, so instead - // we're generating fake IP addresses with lookup_name that we can - // resolve later on with lookup_addr. - // We do the aliasing in 172.29.*.*, giving us 65536 possibilities. - $DNS__deps: ['_inet_pton4_raw', '_inet_pton6_raw'], - $DNS: { - address_map: { - id: 1, - addrs: {}, - names: {} - }, - - lookup_name: function (name) { - // If the name is already a valid ipv4 / ipv6 address, don't generate a fake one. - var res = __inet_pton4_raw(name); - if (res) { - return name; - } - res = __inet_pton6_raw(name); - if (res) { - return name; - } - - // See if this name is already mapped. - var addr; - - if (DNS.address_map.addrs[name]) { - addr = DNS.address_map.addrs[name]; - } else { - var id = DNS.address_map.id++; - assert(id < 65535, 'exceeded max address mappings of 65535'); - - addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00); - - DNS.address_map.names[addr] = name; - DNS.address_map.addrs[name] = addr; - } - - return addr; - }, - - lookup_addr: function (addr) { - if (DNS.address_map.names[addr]) { - return DNS.address_map.names[addr]; - } - - return null; - } - }, - - // note: lots of leaking here! - gethostbyaddr__deps: ['$DNS', 'gethostbyname', '_inet_ntop4_raw'], - gethostbyaddr: function (addr, addrlen, type) { - if (type !== {{{ cDefine('AF_INET') }}}) { - ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); - // TODO: set h_errno - return null; - } - addr = {{{ makeGetValue('addr', '0', 'i32') }}}; // addr is in_addr - var host = __inet_ntop4_raw(addr); - var lookup = DNS.lookup_addr(host); - if (lookup) { - host = lookup; - } - var hostp = allocate(intArrayFromString(host), 'i8', ALLOC_STACK); - return _gethostbyname(hostp); - }, - - gethostbyname__deps: ['$DNS', '_inet_pton4_raw'], - gethostbyname: function(name) { - name = Pointer_stringify(name); - - // generate hostent - var ret = _malloc({{{ C_STRUCTS.hostent.__size__ }}}); // XXX possibly leaked, as are others here - var nameBuf = _malloc(name.length+1); - writeStringToMemory(name, nameBuf); - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_name, 'nameBuf', 'i8*') }}}; - var aliasesBuf = _malloc(4); - {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}}; - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_aliases, 'aliasesBuf', 'i8**') }}}; - var afinet = {{{ cDefine('AF_INET') }}}; - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addrtype, 'afinet', 'i32') }}}; - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_length, '4', 'i32') }}}; - var addrListBuf = _malloc(12); - {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}}; - {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}}; - {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}}; - {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addr_list, 'addrListBuf', 'i8**') }}}; - return ret; - }, - - gethostbyname_r__deps: ['gethostbyname'], - gethostbyname_r: function(name, ret, buf, buflen, out, err) { - var data = _gethostbyname(name); - _memcpy(ret, data, {{{ C_STRUCTS.hostent.__size__ }}}); - _free(data); - {{{ makeSetValue('err', '0', '0', 'i32') }}}; - {{{ makeSetValue('out', '0', 'ret', '*') }}}; - return 0; - }, - - getaddrinfo__deps: ['$Sockets', '$DNS', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl'], - getaddrinfo: function(node, service, hint, out) { - // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL - // hints are specified or ai_family set to AF_UNSPEC or ai_socktype or ai_protocol set to 0 then we - // really should provide a linked list of suitable addrinfo values. - var addrs = []; - var canon = null; - var addr = 0; - var port = 0; - var flags = 0; - var family = {{{ cDefine('AF_UNSPEC') }}}; - var type = 0; - var proto = 0; - var ai, last; - - function allocaddrinfo(family, type, proto, canon, addr, port) { - var sa, salen, ai; - var res; - - salen = family === {{{ cDefine('AF_INET6') }}} ? - {{{ C_STRUCTS.sockaddr_in6.__size__ }}} : - {{{ C_STRUCTS.sockaddr_in.__size__ }}}; - addr = family === {{{ cDefine('AF_INET6') }}} ? - __inet_ntop6_raw(addr) : - __inet_ntop4_raw(addr); - sa = _malloc(salen); - res = __write_sockaddr(sa, family, addr, port); - assert(!res.errno); - - ai = _malloc({{{ C_STRUCTS.addrinfo.__size__ }}}); - {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_family, 'family', 'i32') }}}; - {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_socktype, 'type', 'i32') }}}; - {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_protocol, 'proto', 'i32') }}}; - if (canon) { - {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_canonname, 'canon', 'i32') }}}; - } - {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addr, 'sa', '*') }}}; - if (family === {{{ cDefine('AF_INET6') }}}) { - {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}}; - } else { - {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; - } - {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_next, '0', 'i32') }}}; - - return ai; - } - - if (hint) { - flags = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}}; - family = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_family, 'i32') }}}; - type = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_socktype, 'i32') }}}; - proto = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_protocol, 'i32') }}}; - } - if (type && !proto) { - proto = type === {{{ cDefine('SOCK_DGRAM') }}} ? {{{ cDefine('IPPROTO_UDP') }}} : {{{ cDefine('IPPROTO_TCP') }}}; - } - if (!type && proto) { - type = proto === {{{ cDefine('IPPROTO_UDP') }}} ? {{{ cDefine('SOCK_DGRAM') }}} : {{{ cDefine('SOCK_STREAM') }}}; - } - - // If type or proto are set to zero in hints we should really be returning multiple addrinfo values, but for - // now default to a TCP STREAM socket so we can at least return a sensible addrinfo given NULL hints. - if (proto === 0) { - proto = {{{ cDefine('IPPROTO_TCP') }}}; - } - if (type === 0) { - type = {{{ cDefine('SOCK_STREAM') }}}; - } - - if (!node && !service) { - return {{{ cDefine('EAI_NONAME') }}}; - } - if (flags & ~({{{ cDefine('AI_PASSIVE') }}}|{{{ cDefine('AI_CANONNAME') }}}|{{{ cDefine('AI_NUMERICHOST') }}}| - {{{ cDefine('AI_NUMERICSERV') }}}|{{{ cDefine('AI_V4MAPPED') }}}|{{{ cDefine('AI_ALL') }}}|{{{ cDefine('AI_ADDRCONFIG') }}})) { - return {{{ cDefine('EAI_BADFLAGS') }}}; - } - if (hint !== 0 && ({{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) { - return {{{ cDefine('EAI_BADFLAGS') }}}; - } - if (flags & {{{ cDefine('AI_ADDRCONFIG') }}}) { - // TODO - return {{{ cDefine('EAI_NONAME') }}}; - } - if (type !== 0 && type !== {{{ cDefine('SOCK_STREAM') }}} && type !== {{{ cDefine('SOCK_DGRAM') }}}) { - return {{{ cDefine('EAI_SOCKTYPE') }}}; - } - if (family !== {{{ cDefine('AF_UNSPEC') }}} && family !== {{{ cDefine('AF_INET') }}} && family !== {{{ cDefine('AF_INET6') }}}) { - return {{{ cDefine('EAI_FAMILY') }}}; - } - - if (service) { - service = Pointer_stringify(service); - port = parseInt(service, 10); - - if (isNaN(port)) { - if (flags & {{{ cDefine('AI_NUMERICSERV') }}}) { - return {{{ cDefine('EAI_NONAME') }}}; - } - // TODO support resolving well-known service names from: - // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt - return {{{ cDefine('EAI_SERVICE') }}}; - } - } - - if (!node) { - if (family === {{{ cDefine('AF_UNSPEC') }}}) { - family = {{{ cDefine('AF_INET') }}}; - } - if ((flags & {{{ cDefine('AI_PASSIVE') }}}) === 0) { - if (family === {{{ cDefine('AF_INET') }}}) { - addr = _htonl({{{ cDefine('INADDR_LOOPBACK') }}}); - } else { - addr = [0, 0, 0, 1]; - } - } - ai = allocaddrinfo(family, type, proto, null, addr, port); - {{{ makeSetValue('out', '0', 'ai', '*') }}}; - return 0; - } - - // - // try as a numeric address - // - node = Pointer_stringify(node); - addr = __inet_pton4_raw(node); - if (addr !== null) { - // incoming node is a valid ipv4 address - if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET') }}}) { - family = {{{ cDefine('AF_INET') }}}; - } - else if (family === {{{ cDefine('AF_INET6') }}} && (flags & {{{ cDefine('AI_V4MAPPED') }}})) { - addr = [0, 0, _htonl(0xffff), addr]; - family = {{{ cDefine('AF_INET6') }}}; - } else { - return {{{ cDefine('EAI_NONAME') }}}; - } - } else { - addr = __inet_pton6_raw(node); - if (addr !== null) { - // incoming node is a valid ipv6 address - if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET6') }}}) { - family = {{{ cDefine('AF_INET6') }}}; - } else { - return {{{ cDefine('EAI_NONAME') }}}; - } - } - } - if (addr != null) { - ai = allocaddrinfo(family, type, proto, node, addr, port); - {{{ makeSetValue('out', '0', 'ai', '*') }}}; - return 0; - } - if (flags & {{{ cDefine('AI_NUMERICHOST') }}}) { - return {{{ cDefine('EAI_NONAME') }}}; - } - - // - // try as a hostname - // - // resolve the hostname to a temporary fake address - node = DNS.lookup_name(node); - addr = __inet_pton4_raw(node); - if (family === {{{ cDefine('AF_UNSPEC') }}}) { - family = {{{ cDefine('AF_INET') }}}; - } else if (family === {{{ cDefine('AF_INET6') }}}) { - addr = [0, 0, _htonl(0xffff), addr]; - } - ai = allocaddrinfo(family, type, proto, null, addr, port); - {{{ makeSetValue('out', '0', 'ai', '*') }}}; - return 0; - }, - - freeaddrinfo__deps: ['$Sockets'], - freeaddrinfo: function(ai) { - var sa = {{{ makeGetValue('ai', C_STRUCTS.addrinfo.ai_addr, '*') }}}; - _free(sa); - _free(ai); - }, - - getnameinfo__deps: ['$Sockets', '$DNS', '_read_sockaddr'], - getnameinfo: function (sa, salen, node, nodelen, serv, servlen, flags) { - var info = __read_sockaddr(sa, salen); - if (info.errno) { - return {{{ cDefine('EAI_FAMILY') }}}; - } - var port = info.port; - var addr = info.addr; - - if (node && nodelen) { - var lookup; - if ((flags & {{{ cDefine('NI_NUMERICHOST') }}}) || !(lookup = DNS.lookup_addr(addr))) { - if (flags & {{{ cDefine('NI_NAMEREQD') }}}) { - return {{{ cDefine('EAI_NONAME') }}}; - } - } else { - addr = lookup; - } - if (addr.length >= nodelen) { - return {{{ cDefine('EAI_OVERFLOW') }}}; - } - writeStringToMemory(addr, node); - } - - if (serv && servlen) { - port = '' + port; - if (port.length > servlen) { - return {{{ cDefine('EAI_OVERFLOW') }}}; - } - writeStringToMemory(port, serv); - } - - return 0; - }, - // Can't use a literal for $GAI_ERRNO_MESSAGES as was done for $ERRNO_MESSAGES as the keys (e.g. EAI_BADFLAGS) - // are actually negative numbers and you can't have expressions as keys in JavaScript literals. - $GAI_ERRNO_MESSAGES: {}, - - gai_strerror__deps: ['$GAI_ERRNO_MESSAGES'], - gai_strerror: function(val) { - var buflen = 256; - - // On first call to gai_strerror we initialise the buffer and populate the error messages. - if (!_gai_strerror.buffer) { - _gai_strerror.buffer = _malloc(buflen); - - GAI_ERRNO_MESSAGES['0'] = 'Success'; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_BADFLAGS') }}}] = 'Invalid value for \'ai_flags\' field'; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_NONAME') }}}] = 'NAME or SERVICE is unknown'; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_AGAIN') }}}] = 'Temporary failure in name resolution'; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_FAIL') }}}] = 'Non-recoverable failure in name res'; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_FAMILY') }}}] = '\'ai_family\' not supported'; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SOCKTYPE') }}}] = '\'ai_socktype\' not supported'; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SERVICE') }}}] = 'SERVICE not supported for \'ai_socktype\''; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_MEMORY') }}}] = 'Memory allocation failure'; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SYSTEM') }}}] = 'System error returned in \'errno\''; - GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_OVERFLOW') }}}] = 'Argument buffer overflow'; - } - - var msg = 'Unknown error'; - - if (val in GAI_ERRNO_MESSAGES) { - if (GAI_ERRNO_MESSAGES[val].length > buflen - 1) { - msg = 'Message too long'; // EMSGSIZE message. This should never occur given the GAI_ERRNO_MESSAGES above. - } else { - msg = GAI_ERRNO_MESSAGES[val]; - } - } - - writeAsciiToMemory(msg, _gai_strerror.buffer); - return _gai_strerror.buffer; + $ERRNO_MESSAGES: { + 0: 'Success', + {{{ cDefine('EPERM') }}}: 'Not super-user', + {{{ cDefine('ENOENT') }}}: 'No such file or directory', + {{{ cDefine('ESRCH') }}}: 'No such process', + {{{ cDefine('EINTR') }}}: 'Interrupted system call', + {{{ cDefine('EIO') }}}: 'I/O error', + {{{ cDefine('ENXIO') }}}: 'No such device or address', + {{{ cDefine('E2BIG') }}}: 'Arg list too long', + {{{ cDefine('ENOEXEC') }}}: 'Exec format error', + {{{ cDefine('EBADF') }}}: 'Bad file number', + {{{ cDefine('ECHILD') }}}: 'No children', + {{{ cDefine('EWOULDBLOCK') }}}: 'No more processes', + {{{ cDefine('ENOMEM') }}}: 'Not enough core', + {{{ cDefine('EACCES') }}}: 'Permission denied', + {{{ cDefine('EFAULT') }}}: 'Bad address', + {{{ cDefine('ENOTBLK') }}}: 'Block device required', + {{{ cDefine('EBUSY') }}}: 'Mount device busy', + {{{ cDefine('EEXIST') }}}: 'File exists', + {{{ cDefine('EXDEV') }}}: 'Cross-device link', + {{{ cDefine('ENODEV') }}}: 'No such device', + {{{ cDefine('ENOTDIR') }}}: 'Not a directory', + {{{ cDefine('EISDIR') }}}: 'Is a directory', + {{{ cDefine('EINVAL') }}}: 'Invalid argument', + {{{ cDefine('ENFILE') }}}: 'Too many open files in system', + {{{ cDefine('EMFILE') }}}: 'Too many open files', + {{{ cDefine('ENOTTY') }}}: 'Not a typewriter', + {{{ cDefine('ETXTBSY') }}}: 'Text file busy', + {{{ cDefine('EFBIG') }}}: 'File too large', + {{{ cDefine('ENOSPC') }}}: 'No space left on device', + {{{ cDefine('ESPIPE') }}}: 'Illegal seek', + {{{ cDefine('EROFS') }}}: 'Read only file system', + {{{ cDefine('EMLINK') }}}: 'Too many links', + {{{ cDefine('EPIPE') }}}: 'Broken pipe', + {{{ cDefine('EDOM') }}}: 'Math arg out of domain of func', + {{{ cDefine('ERANGE') }}}: 'Math result not representable', + {{{ cDefine('ENOMSG') }}}: 'No message of desired type', + {{{ cDefine('EIDRM') }}}: 'Identifier removed', + {{{ cDefine('ECHRNG') }}}: 'Channel number out of range', + {{{ cDefine('EL2NSYNC') }}}: 'Level 2 not synchronized', + {{{ cDefine('EL3HLT') }}}: 'Level 3 halted', + {{{ cDefine('EL3RST') }}}: 'Level 3 reset', + {{{ cDefine('ELNRNG') }}}: 'Link number out of range', + {{{ cDefine('EUNATCH') }}}: 'Protocol driver not attached', + {{{ cDefine('ENOCSI') }}}: 'No CSI structure available', + {{{ cDefine('EL2HLT') }}}: 'Level 2 halted', + {{{ cDefine('EDEADLK') }}}: 'Deadlock condition', + {{{ cDefine('ENOLCK') }}}: 'No record locks available', + {{{ cDefine('EBADE') }}}: 'Invalid exchange', + {{{ cDefine('EBADR') }}}: 'Invalid request descriptor', + {{{ cDefine('EXFULL') }}}: 'Exchange full', + {{{ cDefine('ENOANO') }}}: 'No anode', + {{{ cDefine('EBADRQC') }}}: 'Invalid request code', + {{{ cDefine('EBADSLT') }}}: 'Invalid slot', + {{{ cDefine('EDEADLOCK') }}}: 'File locking deadlock error', + {{{ cDefine('EBFONT') }}}: 'Bad font file fmt', + {{{ cDefine('ENOSTR') }}}: 'Device not a stream', + {{{ cDefine('ENODATA') }}}: 'No data (for no delay io)', + {{{ cDefine('ETIME') }}}: 'Timer expired', + {{{ cDefine('ENOSR') }}}: 'Out of streams resources', + {{{ cDefine('ENONET') }}}: 'Machine is not on the network', + {{{ cDefine('ENOPKG') }}}: 'Package not installed', + {{{ cDefine('EREMOTE') }}}: 'The object is remote', + {{{ cDefine('ENOLINK') }}}: 'The link has been severed', + {{{ cDefine('EADV') }}}: 'Advertise error', + {{{ cDefine('ESRMNT') }}}: 'Srmount error', + {{{ cDefine('ECOMM') }}}: 'Communication error on send', + {{{ cDefine('EPROTO') }}}: 'Protocol error', + {{{ cDefine('EMULTIHOP') }}}: 'Multihop attempted', + {{{ cDefine('EDOTDOT') }}}: 'Cross mount point (not really error)', + {{{ cDefine('EBADMSG') }}}: 'Trying to read unreadable message', + {{{ cDefine('ENOTUNIQ') }}}: 'Given log. name not unique', + {{{ cDefine('EBADFD') }}}: 'f.d. invalid for this operation', + {{{ cDefine('EREMCHG') }}}: 'Remote address changed', + {{{ cDefine('ELIBACC') }}}: 'Can access a needed shared lib', + {{{ cDefine('ELIBBAD') }}}: 'Accessing a corrupted shared lib', + {{{ cDefine('ELIBSCN') }}}: '.lib section in a.out corrupted', + {{{ cDefine('ELIBMAX') }}}: 'Attempting to link in too many libs', + {{{ cDefine('ELIBEXEC') }}}: 'Attempting to exec a shared library', + {{{ cDefine('ENOSYS') }}}: 'Function not implemented', + {{{ cDefine('ENOTEMPTY') }}}: 'Directory not empty', + {{{ cDefine('ENAMETOOLONG') }}}: 'File or path name too long', + {{{ cDefine('ELOOP') }}}: 'Too many symbolic links', + {{{ cDefine('EOPNOTSUPP') }}}: 'Operation not supported on transport endpoint', + {{{ cDefine('EPFNOSUPPORT') }}}: 'Protocol family not supported', + {{{ cDefine('ECONNRESET') }}}: 'Connection reset by peer', + {{{ cDefine('ENOBUFS') }}}: 'No buffer space available', + {{{ cDefine('EAFNOSUPPORT') }}}: 'Address family not supported by protocol family', + {{{ cDefine('EPROTOTYPE') }}}: 'Protocol wrong type for socket', + {{{ cDefine('ENOTSOCK') }}}: 'Socket operation on non-socket', + {{{ cDefine('ENOPROTOOPT') }}}: 'Protocol not available', + {{{ cDefine('ESHUTDOWN') }}}: 'Can\'t send after socket shutdown', + {{{ cDefine('ECONNREFUSED') }}}: 'Connection refused', + {{{ cDefine('EADDRINUSE') }}}: 'Address already in use', + {{{ cDefine('ECONNABORTED') }}}: 'Connection aborted', + {{{ cDefine('ENETUNREACH') }}}: 'Network is unreachable', + {{{ cDefine('ENETDOWN') }}}: 'Network interface is not configured', + {{{ cDefine('ETIMEDOUT') }}}: 'Connection timed out', + {{{ cDefine('EHOSTDOWN') }}}: 'Host is down', + {{{ cDefine('EHOSTUNREACH') }}}: 'Host is unreachable', + {{{ cDefine('EINPROGRESS') }}}: 'Connection already in progress', + {{{ cDefine('EALREADY') }}}: 'Socket already connected', + {{{ cDefine('EDESTADDRREQ') }}}: 'Destination address required', + {{{ cDefine('EMSGSIZE') }}}: 'Message too long', + {{{ cDefine('EPROTONOSUPPORT') }}}: 'Unknown protocol', + {{{ cDefine('ESOCKTNOSUPPORT') }}}: 'Socket type not supported', + {{{ cDefine('EADDRNOTAVAIL') }}}: 'Address not available', + {{{ cDefine('ENETRESET') }}}: 'Connection reset by network', + {{{ cDefine('EISCONN') }}}: 'Socket is already connected', + {{{ cDefine('ENOTCONN') }}}: 'Socket is not connected', + {{{ cDefine('ETOOMANYREFS') }}}: 'Too many references', + {{{ cDefine('EUSERS') }}}: 'Too many users', + {{{ cDefine('EDQUOT') }}}: 'Quota exceeded', + {{{ cDefine('ESTALE') }}}: 'Stale file handle', + {{{ cDefine('ENOTSUP') }}}: 'Not supported', + {{{ cDefine('ENOMEDIUM') }}}: 'No medium (in tape drive)', + {{{ cDefine('EILSEQ') }}}: 'Illegal byte sequence', + {{{ cDefine('EOVERFLOW') }}}: 'Value too large for defined data type', + {{{ cDefine('ECANCELED') }}}: 'Operation canceled', + {{{ cDefine('ENOTRECOVERABLE') }}}: 'State not recoverable', + {{{ cDefine('EOWNERDEAD') }}}: 'Previous owner died', + {{{ cDefine('ESTRPIPE') }}}: 'Streams pipe error', }, - - // Implement netdb.h protocol entry (getprotoent, getprotobyname, getprotobynumber, setprotoent, endprotoent) - // http://pubs.opengroup.org/onlinepubs/9699919799/functions/getprotobyname.html - // The Protocols object holds our 'fake' protocols 'database'. - $Protocols: { - list: [], - map: {} + __setErrNo: function(value) { + if (Module['___errno_location']) {{{ makeSetValue("Module['___errno_location']()", 0, 'value', 'i32') }}}; +#if ASSERTIONS + else Module.printErr('failed to set errno from JS'); +#endif + return value; }, - setprotoent__deps: ['$Protocols'], - setprotoent: function(stayopen) { - // void setprotoent(int stayopen); - - // Allocate and populate a protoent structure given a name, protocol number and array of aliases - function allocprotoent(name, proto, aliases) { - // write name into buffer - var nameBuf = _malloc(name.length + 1); - writeAsciiToMemory(name, nameBuf); - - // write aliases into buffer - var j = 0; - var length = aliases.length; - var aliasListBuf = _malloc((length + 1) * 4); // Use length + 1 so we have space for the terminating NULL ptr. - - for (var i = 0; i < length; i++, j += 4) { - var alias = aliases[i]; - var aliasBuf = _malloc(alias.length + 1); - writeAsciiToMemory(alias, aliasBuf); - {{{ makeSetValue('aliasListBuf', 'j', 'aliasBuf', 'i8*') }}}; - } - {{{ makeSetValue('aliasListBuf', 'j', '0', 'i8*') }}}; // Terminating NULL pointer. - - // generate protoent - var pe = _malloc({{{ C_STRUCTS.protoent.__size__ }}}); - {{{ makeSetValue('pe', C_STRUCTS.protoent.p_name, 'nameBuf', 'i8*') }}}; - {{{ makeSetValue('pe', C_STRUCTS.protoent.p_aliases, 'aliasListBuf', 'i8**') }}}; - {{{ makeSetValue('pe', C_STRUCTS.protoent.p_proto, 'proto', 'i32') }}}; - return pe; - }; - - // Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial - // to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful. - var list = Protocols.list; - var map = Protocols.map; - if (list.length === 0) { - var entry = allocprotoent('tcp', 6, ['TCP']); - list.push(entry); - map['tcp'] = map['6'] = entry; - entry = allocprotoent('udp', 17, ['UDP']); - list.push(entry); - map['udp'] = map['17'] = entry; - } - _setprotoent.index = 0; + // ========================================================================== + // sched.h (stubs only - no thread support yet!) + // ========================================================================== + sched_yield: function() { + return 0; }, - endprotoent: function() { - // void endprotoent(void); - // We're not using a real protocol database so we don't do a real close. - }, + // ========================================================================== + // arpa/inet.h + // ========================================================================== - getprotoent__deps: ['setprotoent', '$Protocols'], - getprotoent: function(number) { - // struct protoent *getprotoent(void); - // reads the next entry from the protocols 'database' or return NULL if 'eof' - if (_setprotoent.index === Protocols.list.length) { - return 0; - } else { - var result = Protocols.list[_setprotoent.index++]; - return result; + // old ipv4 only functions + inet_addr__deps: ['_inet_pton4_raw'], + inet_addr: function(ptr) { + var addr = __inet_pton4_raw(Pointer_stringify(ptr)); + if (addr === null) { + return -1; } - }, - - getprotobyname__deps: ['setprotoent', '$Protocols'], - getprotobyname: function(name) { - // struct protoent *getprotobyname(const char *); - name = Pointer_stringify(name); - _setprotoent(true); - var result = Protocols.map[name]; - return result; - }, - - getprotobynumber__deps: ['setprotoent', '$Protocols'], - getprotobynumber: function(number) { - // struct protoent *getprotobynumber(int proto); - _setprotoent(true); - var result = Protocols.map[number]; - return result; + return addr; }, // ========================================================================== - // sockets. Note that the implementation assumes all sockets are always - // nonblocking + // netinet/in.h // ========================================================================== -#if SOCKET_WEBRTC - $Sockets__deps: ['__setErrNo', '$ERRNO_CODES', - function() { return 'var SocketIO = ' + read('socket.io.js') + ';\n' }, - function() { return 'var Peer = ' + read('wrtcp.js') + ';\n' }], -#else - $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'], -#endif - $Sockets: { - BUFFER_SIZE: 10*1024, // initial size - MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer - - nextFd: 1, - fds: {}, - nextport: 1, - maxport: 65535, - peer: null, - connections: {}, - portmap: {}, - localAddr: 0xfe00000a, // Local address is always 10.0.0.254 - addrPool: [ 0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a, - 0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a, - 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a] /* 0x0100000a is reserved */ - }, -#if SOCKET_WEBRTC - /* WebRTC sockets supports several options on the Module object. - - * Module['host']: true if this peer is hosting, false otherwise - * Module['webrtc']['broker']: hostname for the p2p broker that this peer should use - * Module['webrtc']['session']: p2p session for that this peer will join, or undefined if this peer is hosting - * Module['webrtc']['hostOptions']: options to pass into p2p library if this peer is hosting - * Module['webrtc']['onpeer']: function(peer, route), invoked when this peer is ready to connect - * Module['webrtc']['onconnect']: function(peer), invoked when a new peer connection is ready - * Module['webrtc']['ondisconnect']: function(peer), invoked when an existing connection is closed - * Module['webrtc']['onerror']: function(error), invoked when an error occurs - */ - socket__deps: ['$FS', '$Sockets'], - socket: function(family, type, protocol) { #if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_SOCKET') }}}, family, type, protocol); -#endif - var INCOMING_QUEUE_LENGTH = 64; - var info = FS.createStream({ - addr: null, - port: null, - inQueue: new CircularBuffer(INCOMING_QUEUE_LENGTH), - header: new Uint16Array(2), - bound: false, - socket: true, - stream_ops: {} - }); - assert(info.fd < 64); // select() assumes socket fd values are in 0..63 - var stream = type == {{{ cDefine('SOCK_STREAM') }}}; - if (protocol) { - assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if stream, must be tcp - } - - // Open the peer connection if we don't have it already - if (null == Sockets.peer) { - var host = Module['host']; - var broker = Module['webrtc']['broker']; - var session = Module['webrtc']['session']; - var peer = new Peer(broker); - var listenOptions = Module['webrtc']['hostOptions'] || {}; - peer.onconnection = function peer_onconnection(connection) { - console.log('connected'); - var addr; - /* If this peer is connecting to the host, assign 10.0.0.1 to the host so it can be - reached at a known address. - */ - // Assign 10.0.0.1 to the host - if (session && session === connection['route']) { - addr = 0x0100000a; // 10.0.0.1 - } else { - addr = Sockets.addrPool.shift(); - } - connection['addr'] = addr; - Sockets.connections[addr] = connection; - connection.ondisconnect = function connection_ondisconnect() { - console.log('disconnect'); - // Don't return the host address (10.0.0.1) to the pool - if (!(session && session === Sockets.connections[addr]['route'])) { - Sockets.addrPool.push(addr); - } - delete Sockets.connections[addr]; - - if (Module['webrtc']['ondisconnect'] && 'function' === typeof Module['webrtc']['ondisconnect']) { - Module['webrtc']['ondisconnect'](peer); - } - }; - connection.onerror = function connection_onerror(error) { - if (Module['webrtc']['onerror'] && 'function' === typeof Module['webrtc']['onerror']) { - Module['webrtc']['onerror'](error); - } - }; - connection.onmessage = function connection_onmessage(label, message) { - if ('unreliable' === label) { - handleMessage(addr, message.data); - } - } - - if (Module['webrtc']['onconnect'] && 'function' === typeof Module['webrtc']['onconnect']) { - Module['webrtc']['onconnect'](peer); - } - }; - peer.onpending = function peer_onpending(pending) { - console.log('pending from: ', pending['route'], '; initiated by: ', (pending['incoming']) ? 'remote' : 'local'); - }; - peer.onerror = function peer_onerror(error) { - console.error(error); - }; - peer.onroute = function peer_onroute(route) { - if (Module['webrtc']['onpeer'] && 'function' === typeof Module['webrtc']['onpeer']) { - Module['webrtc']['onpeer'](peer, route); - } - }; - function handleMessage(addr, message) { -#if SOCKET_DEBUG - Module.print("received " + message.byteLength + " raw bytes"); + in6addr_any: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_any = PthreadWorkerInit._in6addr_any; else PthreadWorkerInit._in6addr_any = _in6addr_any = allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', + in6addr_loopback: '; if (ENVIRONMENT_IS_PTHREAD) _in6addr_loopback = PthreadWorkerInit._in6addr_loopback; else PthreadWorkerInit._in6addr_loopback = _in6addr_loopback = allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', +#else + in6addr_any: + 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', + in6addr_loopback: + 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', #endif - var header = new Uint16Array(message, 0, 2); - if (Sockets.portmap[header[1]]) { - Sockets.portmap[header[1]].inQueue.push([addr, message]); - } else { - console.log("unable to deliver message: ", addr, header[1], message); - } - } - window.onbeforeunload = function window_onbeforeunload() { - var ids = Object.keys(Sockets.connections); - ids.forEach(function(id) { - Sockets.connections[id].close(); - }); - } - Sockets.peer = peer; - } - - function CircularBuffer(max_length) { - var buffer = new Array(++ max_length); - var head = 0; - var tail = 0; - var length = 0; - return { - push: function(element) { - buffer[tail ++] = element; - length = Math.min(++ length, max_length - 1); - tail = tail % max_length; - if (tail === head) { - head = (head + 1) % max_length; - } - }, - shift: function(element) { - if (length < 1) return undefined; - - var element = buffer[head]; - -- length; - head = (head + 1) % max_length; - return element; - }, - length: function() { - return length; - } - }; - }; - return info.fd; - }, + // ========================================================================== + // netdb.h + // ========================================================================== - mkport__deps: ['$Sockets'], - mkport: function() { - for(var i = 0; i < Sockets.maxport; ++ i) { - var port = Sockets.nextport ++; - Sockets.nextport = (Sockets.nextport > Sockets.maxport) ? 1 : Sockets.nextport; - if (!Sockets.portmap[port]) { - return port; - } + _inet_pton4_raw: function(str) { + var b = str.split('.'); + for (var i = 0; i < 4; i++) { + var tmp = Number(b[i]); + if (isNaN(tmp)) return null; + b[i] = tmp; } - assert(false, 'all available ports are in use!'); + return (b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)) >>> 0; }, - - connect: function() { - // Stub: connection-oriented sockets are not supported yet. + _inet_ntop4_raw: function(addr) { + return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) }, - - bind__deps: ['$FS', '$Sockets', '_inet_ntop4_raw', 'ntohs', 'mkport'], - bind: function(fd, addr, addrlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_BIND') }}}, fd, addr, addrlen); -#endif - var info = FS.getStream(fd); - if (!info) return -1; - if (addr) { - info.port = _ntohs(getValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_port }}}, 'i16')); - // info.addr = getValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_addr.s_addr }}}, 'i32'); - } - if (!info.port) { - info.port = _mkport(); + _inet_pton6_raw__deps: ['htons'], + _inet_pton6_raw: function(str) { + var words; + var w, offset, z, i; + /* http://home.deds.nl/~aeron/regex/ */ + var valid6regx = /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i + var parts = []; + if (!valid6regx.test(str)) { + return null; } - info.addr = Sockets.localAddr; // 10.0.0.254 - info.host = __inet_ntop4_raw(info.addr); - info.close = function info_close() { - Sockets.portmap[info.port] = undefined; + if (str === "::") { + return [0, 0, 0, 0, 0, 0, 0, 0]; } - Sockets.portmap[info.port] = info; - console.log("bind: ", info.host, info.port); - info.bound = true; - }, - - sendmsg__deps: ['$FS', '$Sockets', 'bind', '_inet_ntop4_raw', 'ntohs'], - sendmsg: function(fd, msg, flags) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_SENDMSG') }}}, fd, msg, flags); -#endif - var info = FS.getStream(fd); - if (!info) return -1; - // if we are not connected, use the address info in the message - if (!info.bound) { - _bind(fd); + // Z placeholder to keep track of zeros when splitting the string on ":" + if (str.indexOf("::") === 0) { + str = str.replace("::", "Z:"); // leading zeros case + } else { + str = str.replace("::", ":Z:"); } - var name = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_name, '*') }}}; - assert(name, 'sendmsg on non-connected socket, and no name/address in the message'); - var port = _ntohs(getValue(name + {{{ C_STRUCTS.sockaddr_in.sin_port }}}, 'i16')); - var addr = getValue(name + {{{ C_STRUCTS.sockaddr_in.sin_addr.s_addr }}}, 'i32'); - var connection = Sockets.connections[addr]; - // var host = __inet_ntop4_raw(addr); - - if (!(connection && connection.connected)) { - ___setErrNo(ERRNO_CODES.EWOULDBLOCK); - return -1; + if (str.indexOf(".") > 0) { + // parse IPv4 embedded stress + str = str.replace(new RegExp('[.]', 'g'), ":"); + words = str.split(":"); + words[words.length-4] = parseInt(words[words.length-4]) + parseInt(words[words.length-3])*256; + words[words.length-3] = parseInt(words[words.length-2]) + parseInt(words[words.length-1])*256; + words = words.slice(0, words.length-2); + } else { + words = str.split(":"); } - var iov = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iov, 'i8*') }}}; - var num = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; -#if SOCKET_DEBUG - Module.print('sendmsg vecs: ' + num); -#endif - var totalSize = 0; - for (var i = 0; i < num; i++) { - totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; - } - var data = new Uint8Array(totalSize); - var ret = 0; - for (var i = 0; i < num; i++) { - var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; -#if SOCKET_DEBUG - Module.print('sendmsg curr size: ' + currNum); -#endif - if (!currNum) continue; - var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; - data.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret); - ret += currNum; + offset = 0; z = 0; + for (w=0; w < words.length; w++) { + if (typeof words[w] === 'string') { + if (words[w] === 'Z') { + // compressed zeros - write appropriate number of zero words + for (z = 0; z < (8 - words.length+1); z++) { + parts[w+z] = 0; + } + offset = z-1; + } else { + // parse hex to field to 16-bit value and write it in network byte-order + parts[w+offset] = _htons(parseInt(words[w],16)); + } + } else { + // parsed IPv4 words + parts[w+offset] = words[w]; + } } - - info.header[0] = info.port; // src port - info.header[1] = port; // dst port -#if SOCKET_DEBUG - Module.print('sendmsg port: ' + info.header[0] + ' -> ' + info.header[1]); - Module.print('sendmsg bytes: ' + data.length + ' | ' + Array.prototype.slice.call(data)); -#endif - var buffer = new Uint8Array(info.header.byteLength + data.byteLength); - buffer.set(new Uint8Array(info.header.buffer)); - buffer.set(data, info.header.byteLength); - - connection.send('unreliable', buffer.buffer); - return ret; + return [ + (parts[1] << 16) | parts[0], + (parts[3] << 16) | parts[2], + (parts[5] << 16) | parts[4], + (parts[7] << 16) | parts[6] + ]; }, - - recvmsg__deps: ['$FS', '$Sockets', 'bind', '__setErrNo', '$ERRNO_CODES', 'htons'], - recvmsg: function(fd, msg, flags) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_RECVMSG') }}}, fd, msg, flags); -#endif - var info = FS.getStream(fd); - if (!info) return -1; - // if we are not connected, use the address info in the message - if (!info.port) { - console.log('recvmsg on unbound socket'); - assert(false, 'cannot receive on unbound socket'); - } - if (info.inQueue.length() == 0) { - ___setErrNo(ERRNO_CODES.EWOULDBLOCK); - return -1; + _inet_pton6__deps: ['_inet_pton6_raw'], + _inet_pton6: function(src, dst) { + var ints = __inet_pton6_raw(Pointer_stringify(src)); + if (ints === null) { + return 0; } - - var entry = info.inQueue.shift(); - var addr = entry[0]; - var message = entry[1]; - var header = new Uint16Array(message, 0, info.header.length); - var buffer = new Uint8Array(message, info.header.byteLength); - - var bytes = buffer.length; -#if SOCKET_DEBUG - Module.print('recvmsg port: ' + header[1] + ' <- ' + header[0]); - Module.print('recvmsg bytes: ' + bytes + ' | ' + Array.prototype.slice.call(buffer)); -#endif - // write source - var name = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_name, '*') }}}; - {{{ makeSetValue('name', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'addr', 'i32') }}}; - {{{ makeSetValue('name', C_STRUCTS.sockaddr_in.sin_port, '_htons(header[0])', 'i16') }}}; - // write data - var ret = bytes; - var iov = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iov, 'i8*') }}}; - var num = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; - var bufferPos = 0; - for (var i = 0; i < num && bytes > 0; i++) { - var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; -#if SOCKET_DEBUG - Module.print('recvmsg loop ' + [i, num, bytes, currNum]); -#endif - if (!currNum) continue; - currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..? - bytes -= currNum; - var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; -#if SOCKET_DEBUG - Module.print('recvmsg call recv ' + currNum); -#endif - HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf); - bufferPos += currNum; + for (var i = 0; i < 4; i++) { + {{{ makeSetValue('dst', 'i*4', 'ints[i]', 'i32') }}}; } - return ret; + return 1; }, + _inet_ntop6_raw__deps: ['ntohs', '_inet_ntop4_raw'], + _inet_ntop6_raw: function(ints) { + // ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4 + // Format for IPv4 compatible and mapped 128-bit IPv6 Addresses + // 128-bits are split into eight 16-bit words + // stored in network byte order (big-endian) + // | 80 bits | 16 | 32 bits | + // +-----------------------------------------------------------------+ + // | 10 bytes | 2 | 4 bytes | + // +--------------------------------------+--------------------------+ + // + 5 words | 1 | 2 words | + // +--------------------------------------+--------------------------+ + // |0000..............................0000|0000| IPv4 ADDRESS | (compatible) + // +--------------------------------------+----+---------------------+ + // |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped) + // +--------------------------------------+----+---------------------+ + var str = ""; + var word = 0; + var longest = 0; + var lastzero = 0; + var zstart = 0; + var len = 0; + var i = 0; + var parts = [ + ints[0] & 0xffff, + (ints[0] >> 16), + ints[1] & 0xffff, + (ints[1] >> 16), + ints[2] & 0xffff, + (ints[2] >> 16), + ints[3] & 0xffff, + (ints[3] >> 16) + ]; - shutdown__deps: ['$FS'], - shutdown: function(fd, how) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_SHUTDOWN') }}}, fd, how); -#endif - var stream = FS.getStream(fd); - if (!stream) return -1; - stream.close(); - FS.closeStream(stream); - }, + // Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses - ioctl__deps: ['$FS'], - ioctl: function(fd, request, varargs) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_IOCTL') }}}, fd, request, varargs); -#endif - var info = FS.getStream(fd); - if (!info) return -1; - var bytes = 0; - if (info.hasData()) { - bytes = info.inQueue[0].length; + var hasipv4 = true; + var v4part = ""; + // check if the 10 high-order bytes are all zeros (first 5 words) + for (i = 0; i < 5; i++) { + if (parts[i] !== 0) { hasipv4 = false; break; } } - var dest = {{{ makeGetValue('varargs', '0', 'i32') }}}; - {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}}; - return 0; - }, - - setsockopt: function(d, level, optname, optval, optlen) { -#if SOCKET_DEBUG - console.log('ignoring setsockopt command'); -#endif - return 0; - }, - accept__deps: ['$FS'], - accept: function(fd, addr, addrlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_ACCEPT') }}}, fd, addr, addrlen); -#endif - // TODO: webrtc queued incoming connections, etc. - // For now, the model is that bind does a connect, and we "accept" that one connection, - // which has host:port the same as ours. We also return the same socket fd. - var info = FS.getStream(fd); - if (!info) return -1; - if (addr) { - setValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_addr.s_addr }}}, info.addr, 'i32'); - setValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_port }}}, info.port, 'i32'); - setValue(addrlen, {{{ C_STRUCTS.sockaddr_in.__size__ }}}, 'i32'); + if (hasipv4) { + // low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words) + v4part = __inet_ntop4_raw(parts[6] | (parts[7] << 16)); + // IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word) + if (parts[5] === -1) { + str = "::ffff:"; + str += v4part; + return str; + } + // IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word) + if (parts[5] === 0) { + str = "::"; + //special case IPv6 addresses + if(v4part === "0.0.0.0") v4part = ""; // any/unspecified address + if(v4part === "0.0.0.1") v4part = "1";// loopback address + str += v4part; + return str; + } } - return fd; - }, - - select__deps: ['$FS'], - select: function(nfds, readfds, writefds, exceptfds, timeout) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_5({{{ cDefine('EM_PROXIED_SELECT') }}}, nfds, readfds, writefds, exceptfds, timeout); -#endif - // readfds are supported, - // writefds checks socket open status - // exceptfds not supported - // timeout is always 0 - fully async - assert(!exceptfds); - var errorCondition = 0; - - function canRead(info) { - return info.inQueue.length() > 0; - } + // Handle all other IPv6 addresses - function canWrite(info) { - return true; + // first run to find the longest contiguous zero words + for (word = 0; word < 8; word++) { + if (parts[word] === 0) { + if (word - lastzero > 1) { + len = 0; + } + lastzero = word; + len++; + } + if (len > longest) { + longest = len; + zstart = word - longest + 1; + } } - function checkfds(nfds, fds, can) { - if (!fds) return 0; - - var bitsSet = 0; - var dstLow = 0; - var dstHigh = 0; - var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}}; - var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}}; - nfds = Math.min(64, nfds); // fd sets have 64 bits - - for (var fd = 0; fd < nfds; fd++) { - var mask = 1 << (fd % 32), int_ = fd < 32 ? srcLow : srcHigh; - if (int_ & mask) { - // index is in the set, check if it is ready for read - var info = FS.getStream(fd); - if (info && can(info)) { - // set bit - fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); - bitsSet++; + for (word = 0; word < 8; word++) { + if (longest > 1) { + // compress contiguous zeros - to produce "::" + if (parts[word] === 0 && word >= zstart && word < (zstart + longest) ) { + if (word === zstart) { + str += ":"; + if (zstart === 0) str += ":"; //leading zeros case } + continue; } } - - {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}}; - {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}}; - return bitsSet; - } - - var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite); - if (errorCondition) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } else { - return totalHandles; + // converts 16-bit words from big-endian to little-endian before converting to hex string + str += Number(_ntohs(parts[word] & 0xffff)).toString(16); + str += word < 7 ? ":" : ""; } + return str; }, -#else - // ========================================================================== - // socket.h - // ========================================================================== + _read_sockaddr__deps: ['$Sockets', '_inet_ntop4_raw', '_inet_ntop6_raw'], _read_sockaddr: function (sa, salen) { // family / port offsets are common to both sockaddr_in and sockaddr_in6 @@ -7434,575 +3230,471 @@ LibraryManager.library = { default: return { errno: ERRNO_CODES.EAFNOSUPPORT }; } - // kind of lame, but let's match _read_sockaddr's interface - return {}; + // kind of lame, but let's match _read_sockaddr's interface + return {}; + }, + + // We can't actually resolve hostnames in the browser, so instead + // we're generating fake IP addresses with lookup_name that we can + // resolve later on with lookup_addr. + // We do the aliasing in 172.29.*.*, giving us 65536 possibilities. + $DNS__deps: ['_inet_pton4_raw', '_inet_pton6_raw'], + $DNS: { + address_map: { + id: 1, + addrs: {}, + names: {} + }, + + lookup_name: function (name) { + // If the name is already a valid ipv4 / ipv6 address, don't generate a fake one. + var res = __inet_pton4_raw(name); + if (res) { + return name; + } + res = __inet_pton6_raw(name); + if (res) { + return name; + } + + // See if this name is already mapped. + var addr; + + if (DNS.address_map.addrs[name]) { + addr = DNS.address_map.addrs[name]; + } else { + var id = DNS.address_map.id++; + assert(id < 65535, 'exceeded max address mappings of 65535'); + + addr = '172.29.' + (id & 0xff) + '.' + (id & 0xff00); + + DNS.address_map.names[addr] = name; + DNS.address_map.addrs[name] = addr; + } + + return addr; + }, + + lookup_addr: function (addr) { + if (DNS.address_map.names[addr]) { + return DNS.address_map.names[addr]; + } + + return null; + } + }, + + // note: lots of leaking here! + gethostbyaddr__deps: ['$DNS', 'gethostbyname', '_inet_ntop4_raw'], + gethostbyaddr: function (addr, addrlen, type) { + if (type !== {{{ cDefine('AF_INET') }}}) { + ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + // TODO: set h_errno + return null; + } + addr = {{{ makeGetValue('addr', '0', 'i32') }}}; // addr is in_addr + var host = __inet_ntop4_raw(addr); + var lookup = DNS.lookup_addr(host); + if (lookup) { + host = lookup; + } + var hostp = allocate(intArrayFromString(host), 'i8', ALLOC_STACK); + return _gethostbyname(hostp); }, - socket__deps: ['$FS', '$SOCKFS'], - socket: function(family, type, protocol) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_SOCKET') }}}, family, type, protocol); -#endif - var sock = SOCKFS.createSocket(family, type, protocol); - assert(sock.stream.fd < 64); // select() assumes socket fd values are in 0..63 - return sock.stream.fd; - }, + gethostbyname__deps: ['$DNS', '_inet_pton4_raw'], + gethostbyname: function(name) { + name = Pointer_stringify(name); - socketpair__deps: ['$ERRNO_CODES', '__setErrNo'], - socketpair: function(domain, type, protocol, sv) { - // int socketpair(int domain, int type, int protocol, int sv[2]); - // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html - ___setErrNo(ERRNO_CODES.EOPNOTSUPP); - return -1; + // generate hostent + var ret = _malloc({{{ C_STRUCTS.hostent.__size__ }}}); // XXX possibly leaked, as are others here + var nameBuf = _malloc(name.length+1); + writeStringToMemory(name, nameBuf); + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_name, 'nameBuf', 'i8*') }}}; + var aliasesBuf = _malloc(4); + {{{ makeSetValue('aliasesBuf', '0', '0', 'i8*') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_aliases, 'aliasesBuf', 'i8**') }}}; + var afinet = {{{ cDefine('AF_INET') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addrtype, 'afinet', 'i32') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_length, '4', 'i32') }}}; + var addrListBuf = _malloc(12); + {{{ makeSetValue('addrListBuf', '0', 'addrListBuf+8', 'i32*') }}}; + {{{ makeSetValue('addrListBuf', '4', '0', 'i32*') }}}; + {{{ makeSetValue('addrListBuf', '8', '__inet_pton4_raw(DNS.lookup_name(name))', 'i32') }}}; + {{{ makeSetValue('ret', C_STRUCTS.hostent.h_addr_list, 'addrListBuf', 'i8**') }}}; + return ret; }, - shutdown__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo'], - shutdown: function(fd, how) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_SHUTDOWN') }}}, fd, how); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - _close(fd); + gethostbyname_r__deps: ['gethostbyname'], + gethostbyname_r: function(name, ret, buf, buflen, out, err) { + var data = _gethostbyname(name); + _memcpy(ret, data, {{{ C_STRUCTS.hostent.__size__ }}}); + _free(data); + {{{ makeSetValue('err', '0', '0', 'i32') }}}; + {{{ makeSetValue('out', '0', 'ret', '*') }}}; + return 0; }, - bind__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'], - bind: function(fd, addrp, addrlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_BIND') }}}, fd, addrp, addrlen); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } + getaddrinfo__deps: ['$Sockets', '$DNS', '_inet_pton4_raw', '_inet_ntop4_raw', '_inet_pton6_raw', '_inet_ntop6_raw', '_write_sockaddr', 'htonl'], + getaddrinfo: function(node, service, hint, out) { + // Note getaddrinfo currently only returns a single addrinfo with ai_next defaulting to NULL. When NULL + // hints are specified or ai_family set to AF_UNSPEC or ai_socktype or ai_protocol set to 0 then we + // really should provide a linked list of suitable addrinfo values. + var addrs = []; + var canon = null; + var addr = 0; + var port = 0; + var flags = 0; + var family = {{{ cDefine('AF_UNSPEC') }}}; + var type = 0; + var proto = 0; + var ai, last; - var info = __read_sockaddr(addrp, addrlen); - if (info.errno) { - ___setErrNo(info.errno); - return -1; - } - var port = info.port; - var addr = DNS.lookup_addr(info.addr) || info.addr; + function allocaddrinfo(family, type, proto, canon, addr, port) { + var sa, salen, ai; + var res; - try { - sock.sock_ops.bind(sock, addr, port); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; - } - }, + salen = family === {{{ cDefine('AF_INET6') }}} ? + {{{ C_STRUCTS.sockaddr_in6.__size__ }}} : + {{{ C_STRUCTS.sockaddr_in.__size__ }}}; + addr = family === {{{ cDefine('AF_INET6') }}} ? + __inet_ntop6_raw(addr) : + __inet_ntop4_raw(addr); + sa = _malloc(salen); + res = __write_sockaddr(sa, family, addr, port); + assert(!res.errno); - connect__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'], - connect: function(fd, addrp, addrlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_CONNECT') }}}, fd, addrp, addrlen); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } + ai = _malloc({{{ C_STRUCTS.addrinfo.__size__ }}}); + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_family, 'family', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_socktype, 'type', 'i32') }}}; + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_protocol, 'proto', 'i32') }}}; + if (canon) { + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_canonname, 'canon', 'i32') }}}; + } + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addr, 'sa', '*') }}}; + if (family === {{{ cDefine('AF_INET6') }}}) { + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in6.__size__, 'i32') }}}; + } else { + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_addrlen, C_STRUCTS.sockaddr_in.__size__, 'i32') }}}; + } + {{{ makeSetValue('ai', C_STRUCTS.addrinfo.ai_next, '0', 'i32') }}}; - var info = __read_sockaddr(addrp, addrlen); - if (info.errno) { - ___setErrNo(info.errno); - return -1; + return ai; } - var port = info.port; - var addr = DNS.lookup_addr(info.addr) || info.addr; - try { - sock.sock_ops.connect(sock, addr, port); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; + if (hint) { + flags = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}}; + family = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_family, 'i32') }}}; + type = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_socktype, 'i32') }}}; + proto = {{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_protocol, 'i32') }}}; } - }, - - listen__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo'], - listen: function(fd, backlog) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_LISTEN') }}}, fd, backlog); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + if (type && !proto) { + proto = type === {{{ cDefine('SOCK_DGRAM') }}} ? {{{ cDefine('IPPROTO_UDP') }}} : {{{ cDefine('IPPROTO_TCP') }}}; } - try { - sock.sock_ops.listen(sock, backlog); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; + if (!type && proto) { + type = proto === {{{ cDefine('IPPROTO_UDP') }}} ? {{{ cDefine('SOCK_DGRAM') }}} : {{{ cDefine('SOCK_STREAM') }}}; } - }, - accept__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], - accept: function(fd, addr, addrlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_ACCEPT') }}}, fd, addr, addrlen); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + // If type or proto are set to zero in hints we should really be returning multiple addrinfo values, but for + // now default to a TCP STREAM socket so we can at least return a sensible addrinfo given NULL hints. + if (proto === 0) { + proto = {{{ cDefine('IPPROTO_TCP') }}}; } - try { - var newsock = sock.sock_ops.accept(sock); - if (addr) { - var res = __write_sockaddr(addr, newsock.family, DNS.lookup_name(newsock.daddr), newsock.dport); - assert(!res.errno); - } - return newsock.stream.fd; - } catch (e) { - FS.handleFSError(e); - return -1; + if (type === 0) { + type = {{{ cDefine('SOCK_STREAM') }}}; } - }, - getsockname__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], - getsockname: function (fd, addr, addrlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_GETSOCKNAME') }}}, fd, addr, addrlen); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - try { - var info = sock.sock_ops.getname(sock); - var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(info.addr), info.port); - assert(!res.errno); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; + if (!node && !service) { + return {{{ cDefine('EAI_NONAME') }}}; } - }, - - getpeername__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], - getpeername: function (fd, addr, addrlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_GETPEERNAME') }}}, fd, addr, addrlen); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + if (flags & ~({{{ cDefine('AI_PASSIVE') }}}|{{{ cDefine('AI_CANONNAME') }}}|{{{ cDefine('AI_NUMERICHOST') }}}| + {{{ cDefine('AI_NUMERICSERV') }}}|{{{ cDefine('AI_V4MAPPED') }}}|{{{ cDefine('AI_ALL') }}}|{{{ cDefine('AI_ADDRCONFIG') }}})) { + return {{{ cDefine('EAI_BADFLAGS') }}}; } - try { - var info = sock.sock_ops.getname(sock, true); - var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(info.addr), info.port); - assert(!res.errno); - return 0; - } catch (e) { - FS.handleFSError(e); - return -1; + if (hint !== 0 && ({{{ makeGetValue('hint', C_STRUCTS.addrinfo.ai_flags, 'i32') }}} & {{{ cDefine('AI_CANONNAME') }}}) && !node) { + return {{{ cDefine('EAI_BADFLAGS') }}}; } - }, - - send__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo', 'write'], - send: function(fd, buf, len, flags) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_SEND') }}}, fd, buf, len, flags); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + if (flags & {{{ cDefine('AI_ADDRCONFIG') }}}) { + // TODO + return {{{ cDefine('EAI_NONAME') }}}; } - // TODO honor flags - return _write(fd, buf, len); - }, - - recv__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo', 'read'], - recv: function(fd, buf, len, flags) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_4({{{ cDefine('EM_PROXIED_RECV') }}}, fd, buf, len, flags); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + if (type !== 0 && type !== {{{ cDefine('SOCK_STREAM') }}} && type !== {{{ cDefine('SOCK_DGRAM') }}}) { + return {{{ cDefine('EAI_SOCKTYPE') }}}; } - // TODO honor flags - return _read(fd, buf, len); - }, - - sendto__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'], - sendto: function(fd, message, length, flags, dest_addr, dest_len) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_6({{{ cDefine('EM_PROXIED_SENDTO') }}}, fd, message, length, flags, dest_addr, dest_len); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + if (family !== {{{ cDefine('AF_UNSPEC') }}} && family !== {{{ cDefine('AF_INET') }}} && family !== {{{ cDefine('AF_INET6') }}}) { + return {{{ cDefine('EAI_FAMILY') }}}; } - // read the address and port to send to - var info = __read_sockaddr(dest_addr, dest_len); - if (info.errno) { - ___setErrNo(info.errno); - return -1; - } - var port = info.port; - var addr = DNS.lookup_addr(info.addr) || info.addr; + if (service) { + service = Pointer_stringify(service); + port = parseInt(service, 10); - // send the message - try { - var slab = {{{ makeGetSlabs('message', 'i8', true) }}}; - return sock.sock_ops.sendmsg(sock, slab, message, length, addr, port); - } catch (e) { - FS.handleFSError(e); - return -1; + if (isNaN(port)) { + if (flags & {{{ cDefine('AI_NUMERICSERV') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; + } + // TODO support resolving well-known service names from: + // http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt + return {{{ cDefine('EAI_SERVICE') }}}; + } } - }, - recvfrom__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], - recvfrom: function(fd, buf, len, flags, addr, addrlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_6({{{ cDefine('EM_PROXIED_RECVFROM') }}}, fd, buf, len, flags, addr, addrlen); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + if (!node) { + if (family === {{{ cDefine('AF_UNSPEC') }}}) { + family = {{{ cDefine('AF_INET') }}}; + } + if ((flags & {{{ cDefine('AI_PASSIVE') }}}) === 0) { + if (family === {{{ cDefine('AF_INET') }}}) { + addr = _htonl({{{ cDefine('INADDR_LOOPBACK') }}}); + } else { + addr = [0, 0, 0, 1]; + } + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; } - // read from the socket - var msg; - try { - msg = sock.sock_ops.recvmsg(sock, len); - } catch (e) { - FS.handleFSError(e); - return -1; + // + // try as a numeric address + // + node = Pointer_stringify(node); + addr = __inet_pton4_raw(node); + if (addr !== null) { + // incoming node is a valid ipv4 address + if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET') }}}) { + family = {{{ cDefine('AF_INET') }}}; + } + else if (family === {{{ cDefine('AF_INET6') }}} && (flags & {{{ cDefine('AI_V4MAPPED') }}})) { + addr = [0, 0, _htonl(0xffff), addr]; + family = {{{ cDefine('AF_INET6') }}}; + } else { + return {{{ cDefine('EAI_NONAME') }}}; + } + } else { + addr = __inet_pton6_raw(node); + if (addr !== null) { + // incoming node is a valid ipv6 address + if (family === {{{ cDefine('AF_UNSPEC') }}} || family === {{{ cDefine('AF_INET6') }}}) { + family = {{{ cDefine('AF_INET6') }}}; + } else { + return {{{ cDefine('EAI_NONAME') }}}; + } + } } - - if (!msg) { - // socket is closed + if (addr != null) { + ai = allocaddrinfo(family, type, proto, node, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; return 0; } - - // write the source address out - if (addr) { - var res = __write_sockaddr(addr, sock.family, DNS.lookup_name(msg.addr), msg.port); - assert(!res.errno); + if (flags & {{{ cDefine('AI_NUMERICHOST') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; } - // write the buffer out - HEAPU8.set(msg.buffer, buf); - return msg.buffer.byteLength; + // + // try as a hostname + // + // resolve the hostname to a temporary fake address + node = DNS.lookup_name(node); + addr = __inet_pton4_raw(node); + if (family === {{{ cDefine('AF_UNSPEC') }}}) { + family = {{{ cDefine('AF_INET') }}}; + } else if (family === {{{ cDefine('AF_INET6') }}}) { + addr = [0, 0, _htonl(0xffff), addr]; + } + ai = allocaddrinfo(family, type, proto, null, addr, port); + {{{ makeSetValue('out', '0', 'ai', '*') }}}; + return 0; }, - sendmsg__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_read_sockaddr'], - sendmsg: function(fd, message, flags) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_SENDMSG') }}}, fd, message, flags); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; + getnameinfo__deps: ['$Sockets', '$DNS', '_read_sockaddr'], + getnameinfo: function (sa, salen, node, nodelen, serv, servlen, flags) { + var info = __read_sockaddr(sa, salen); + if (info.errno) { + return {{{ cDefine('EAI_FAMILY') }}}; } + var port = info.port; + var addr = info.addr; - var iov = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iov, '*') }}}; - var num = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; - - // read the address and port to send to - var addr; - var port; - var name = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_name, '*') }}}; - var namelen = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_namelen, 'i32') }}}; - if (name) { - var info = __read_sockaddr(name, namelen); - if (info.errno) { - ___setErrNo(info.errno); - return -1; + if (node && nodelen) { + var lookup; + if ((flags & {{{ cDefine('NI_NUMERICHOST') }}}) || !(lookup = DNS.lookup_addr(addr))) { + if (flags & {{{ cDefine('NI_NAMEREQD') }}}) { + return {{{ cDefine('EAI_NONAME') }}}; + } + } else { + addr = lookup; + } + if (addr.length >= nodelen) { + return {{{ cDefine('EAI_OVERFLOW') }}}; } - port = info.port; - addr = DNS.lookup_addr(info.addr) || info.addr; + writeStringToMemory(addr, node); } - // concatenate scatter-gather arrays into one message buffer - var total = 0; - for (var i = 0; i < num; i++) { - total += {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_len, 'i32') }}}; - } - var view = new Uint8Array(total); - var offset = 0; - for (var i = 0; i < num; i++) { - var iovbase = {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_base, 'i8*') }}}; - var iovlen = {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_len, 'i32') }}}; - for (var j = 0; j < iovlen; j++) { - view[offset++] = {{{ makeGetValue('iovbase', 'j', 'i8') }}}; + if (serv && servlen) { + port = '' + port; + if (port.length > servlen) { + return {{{ cDefine('EAI_OVERFLOW') }}}; } + writeStringToMemory(port, serv); } - // write the buffer - try { - return sock.sock_ops.sendmsg(sock, view, 0, total, addr, port); - } catch (e) { - FS.handleFSError(e); - return -1; - } + return 0; }, + // Can't use a literal for $GAI_ERRNO_MESSAGES as was done for $ERRNO_MESSAGES as the keys (e.g. EAI_BADFLAGS) + // are actually negative numbers and you can't have expressions as keys in JavaScript literals. + $GAI_ERRNO_MESSAGES: {}, - recvmsg__deps: ['$FS', '$SOCKFS', '$DNS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr'], - recvmsg: function(fd, message, flags) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_RECVMSG') }}}, fd, message, flags); -#endif - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - - var iov = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iov, 'i8*') }}}; - var num = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; + gai_strerror__deps: ['$GAI_ERRNO_MESSAGES'], + gai_strerror: function(val) { + var buflen = 256; - // get the total amount of data we can read across all arrays - var total = 0; - for (var i = 0; i < num; i++) { - total += {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_len, 'i32') }}}; - } + // On first call to gai_strerror we initialise the buffer and populate the error messages. + if (!_gai_strerror.buffer) { + _gai_strerror.buffer = _malloc(buflen); - // try to read total data - var msg; - try { - msg = sock.sock_ops.recvmsg(sock, total); - } catch (e) { - FS.handleFSError(e); - return -1; + GAI_ERRNO_MESSAGES['0'] = 'Success'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_BADFLAGS') }}}] = 'Invalid value for \'ai_flags\' field'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_NONAME') }}}] = 'NAME or SERVICE is unknown'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_AGAIN') }}}] = 'Temporary failure in name resolution'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_FAIL') }}}] = 'Non-recoverable failure in name res'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_FAMILY') }}}] = '\'ai_family\' not supported'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SOCKTYPE') }}}] = '\'ai_socktype\' not supported'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SERVICE') }}}] = 'SERVICE not supported for \'ai_socktype\''; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_MEMORY') }}}] = 'Memory allocation failure'; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_SYSTEM') }}}] = 'System error returned in \'errno\''; + GAI_ERRNO_MESSAGES['' + {{{ cDefine('EAI_OVERFLOW') }}}] = 'Argument buffer overflow'; } - if (!msg) { - // socket is closed - return 0; - } + var msg = 'Unknown error'; - // TODO honor flags: - // MSG_OOB - // Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific. - // MSG_PEEK - // Peeks at the incoming message. - // MSG_WAITALL - // Requests that the function block until the full amount of data requested can be returned. The function may return a smaller amount of data if a signal is caught, if the connection is terminated, if MSG_PEEK was specified, or if an error is pending for the socket. - - // write the source address out - var name = {{{ makeGetValue('message', C_STRUCTS.msghdr.msg_name, '*') }}}; - if (name) { - var res = __write_sockaddr(name, sock.family, DNS.lookup_name(msg.addr), msg.port); - assert(!res.errno); - } - // write the buffer out to the scatter-gather arrays - var bytesRead = 0; - var bytesRemaining = msg.buffer.byteLength; - - for (var i = 0; bytesRemaining > 0 && i < num; i++) { - var iovbase = {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_base, 'i8*') }}}; - var iovlen = {{{ makeGetValue('iov', '(' + C_STRUCTS.iovec.__size__ + ' * i) + ' + C_STRUCTS.iovec.iov_len, 'i32') }}}; - if (!iovlen) { - continue; + if (val in GAI_ERRNO_MESSAGES) { + if (GAI_ERRNO_MESSAGES[val].length > buflen - 1) { + msg = 'Message too long'; // EMSGSIZE message. This should never occur given the GAI_ERRNO_MESSAGES above. + } else { + msg = GAI_ERRNO_MESSAGES[val]; } - var length = Math.min(iovlen, bytesRemaining); - var buf = msg.buffer.subarray(bytesRead, bytesRead + length); - HEAPU8.set(buf, iovbase + bytesRead); - bytesRead += length; - bytesRemaining -= length; } - // TODO set msghdr.msg_flags - // MSG_EOR - // End of record was received (if supported by the protocol). - // MSG_OOB - // Out-of-band data was received. - // MSG_TRUNC - // Normal data was truncated. - // MSG_CTRUNC - - return bytesRead; + writeAsciiToMemory(msg, _gai_strerror.buffer); + return _gai_strerror.buffer; }, - setsockopt: function(fd, level, optname, optval, optlen) { -#if SOCKET_DEBUG - console.log('ignoring setsockopt command'); -#endif - return 0; + // Implement netdb.h protocol entry (getprotoent, getprotobyname, getprotobynumber, setprotoent, endprotoent) + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/getprotobyname.html + // The Protocols object holds our 'fake' protocols 'database'. + $Protocols: { + list: [], + map: {} }, + setprotoent__deps: ['$Protocols'], + setprotoent: function(stayopen) { + // void setprotoent(int stayopen); - getsockopt__deps: ['$SOCKFS', '__setErrNo', '$ERRNO_CODES'], - getsockopt: function(fd, level, optname, optval, optlen) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_5({{{ cDefine('EM_PROXIED_GETSOCKOPT') }}}, fd, level, optname, optval, optlen); -#endif - // int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/getsockopt.html - // Minimal getsockopt aimed at resolving https://github.com/kripken/emscripten/issues/2211 - // so only supports SOL_SOCKET with SO_ERROR. - var sock = SOCKFS.getSocket(fd); - if (!sock) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } + // Allocate and populate a protoent structure given a name, protocol number and array of aliases + function allocprotoent(name, proto, aliases) { + // write name into buffer + var nameBuf = _malloc(name.length + 1); + writeAsciiToMemory(name, nameBuf); - if (level === {{{ cDefine('SOL_SOCKET') }}}) { - if (optname === {{{ cDefine('SO_ERROR') }}}) { - {{{ makeSetValue('optval', 0, 'sock.error', 'i32') }}}; - {{{ makeSetValue('optlen', 0, 4, 'i32') }}}; - sock.error = null; // Clear the error (The SO_ERROR option obtains and then clears this field). - return 0; - } else { - ___setErrNo(ERRNO_CODES.ENOPROTOOPT); // The option is unknown at the level indicated. -#if ASSERTIONS - Runtime.warnOnce('getsockopt() returning an error as we currently only support optname SO_ERROR'); -#endif - return -1; - } - } else { - ___setErrNo(ERRNO_CODES.ENOPROTOOPT); //The option is unknown at the level indicated. -#if ASSERTIONS - Runtime.warnOnce('getsockopt() returning an error as we only support level SOL_SOCKET'); -#endif - return -1; - } - }, + // write aliases into buffer + var j = 0; + var length = aliases.length; + var aliasListBuf = _malloc((length + 1) * 4); // Use length + 1 so we have space for the terminating NULL ptr. - mkport: function() { throw 'TODO' }, + for (var i = 0; i < length; i++, j += 4) { + var alias = aliases[i]; + var aliasBuf = _malloc(alias.length + 1); + writeAsciiToMemory(alias, aliasBuf); + {{{ makeSetValue('aliasListBuf', 'j', 'aliasBuf', 'i8*') }}}; + } + {{{ makeSetValue('aliasListBuf', 'j', '0', 'i8*') }}}; // Terminating NULL pointer. - // ========================================================================== - // select.h - // ========================================================================== + // generate protoent + var pe = _malloc({{{ C_STRUCTS.protoent.__size__ }}}); + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_name, 'nameBuf', 'i8*') }}}; + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_aliases, 'aliasListBuf', 'i8**') }}}; + {{{ makeSetValue('pe', C_STRUCTS.protoent.p_proto, 'proto', 'i32') }}}; + return pe; + }; - select__deps: ['$FS', '__DEFAULT_POLLMASK'], - select: function(nfds, readfds, writefds, exceptfds, timeout) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_5({{{ cDefine('EM_PROXIED_SELECT') }}}, nfds, readfds, writefds, exceptfds, timeout); -#endif - // readfds are supported, - // writefds checks socket open status - // exceptfds not supported - // timeout is always 0 - fully async - assert(nfds <= 64, 'nfds must be less than or equal to 64'); // fd sets have 64 bits - assert(!exceptfds, 'exceptfds not supported'); - - var total = 0; - - var srcReadLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0), - srcReadHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0); - var srcWriteLow = (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0), - srcWriteHigh = (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0); - var srcExceptLow = (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0), - srcExceptHigh = (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0); - - var dstReadLow = 0, - dstReadHigh = 0; - var dstWriteLow = 0, - dstWriteHigh = 0; - var dstExceptLow = 0, - dstExceptHigh = 0; - - var allLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0) | - (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0) | - (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0); - var allHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0) | - (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0) | - (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0); - - function get(fd, low, high, val) { - return (fd < 32 ? (low & val) : (high & val)); + // Populate the protocol 'database'. The entries are limited to tcp and udp, though it is fairly trivial + // to add extra entries from /etc/protocols if desired - though not sure if that'd actually be useful. + var list = Protocols.list; + var map = Protocols.map; + if (list.length === 0) { + var entry = allocprotoent('tcp', 6, ['TCP']); + list.push(entry); + map['tcp'] = map['6'] = entry; + entry = allocprotoent('udp', 17, ['UDP']); + list.push(entry); + map['udp'] = map['17'] = entry; } - for (var fd = 0; fd < nfds; fd++) { - var mask = 1 << (fd % 32); - if (!(get(fd, allLow, allHigh, mask))) { - continue; // index isn't in the set - } - - var stream = FS.getStream(fd); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - - var flags = ___DEFAULT_POLLMASK; + _setprotoent.index = 0; + }, - if (stream.stream_ops.poll) { - flags = stream.stream_ops.poll(stream); - } + endprotoent: function() { + // void endprotoent(void); + // We're not using a real protocol database so we don't do a real close. + }, - if ((flags & {{{ cDefine('POLLIN') }}}) && get(fd, srcReadLow, srcReadHigh, mask)) { - fd < 32 ? (dstReadLow = dstReadLow | mask) : (dstReadHigh = dstReadHigh | mask); - total++; - } - if ((flags & {{{ cDefine('POLLOUT') }}}) && get(fd, srcWriteLow, srcWriteHigh, mask)) { - fd < 32 ? (dstWriteLow = dstWriteLow | mask) : (dstWriteHigh = dstWriteHigh | mask); - total++; - } - if ((flags & {{{ cDefine('POLLPRI') }}}) && get(fd, srcExceptLow, srcExceptHigh, mask)) { - fd < 32 ? (dstExceptLow = dstExceptLow | mask) : (dstExceptHigh = dstExceptHigh | mask); - total++; - } + getprotoent__deps: ['setprotoent', '$Protocols'], + getprotoent: function(number) { + // struct protoent *getprotoent(void); + // reads the next entry from the protocols 'database' or return NULL if 'eof' + if (_setprotoent.index === Protocols.list.length) { + return 0; + } else { + var result = Protocols.list[_setprotoent.index++]; + return result; } + }, - if (readfds) { - {{{ makeSetValue('readfds', '0', 'dstReadLow', 'i32') }}}; - {{{ makeSetValue('readfds', '4', 'dstReadHigh', 'i32') }}}; - } - if (writefds) { - {{{ makeSetValue('writefds', '0', 'dstWriteLow', 'i32') }}}; - {{{ makeSetValue('writefds', '4', 'dstWriteHigh', 'i32') }}}; - } - if (exceptfds) { - {{{ makeSetValue('exceptfds', '0', 'dstExceptLow', 'i32') }}}; - {{{ makeSetValue('exceptfds', '4', 'dstExceptHigh', 'i32') }}}; - } - - return total; + getprotobyname__deps: ['setprotoent', '$Protocols'], + getprotobyname: function(name) { + // struct protoent *getprotobyname(const char *); + name = Pointer_stringify(name); + _setprotoent(true); + var result = Protocols.map[name]; + return result; + }, + + getprotobynumber__deps: ['setprotoent', '$Protocols'], + getprotobynumber: function(number) { + // struct protoent *getprotobynumber(int proto); + _setprotoent(true); + var result = Protocols.map[number]; + return result; }, // ========================================================================== - // sys/ioctl.h + // sockets. Note that the implementation assumes all sockets are always + // nonblocking // ========================================================================== - - ioctl__deps: ['$FS'], - ioctl: function(fd, request, varargs) { -#if USE_PTHREADS - if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_IOCTL') }}}, fd, request, varargs); +#if SOCKET_WEBRTC + $Sockets__deps: ['__setErrNo', '$ERRNO_CODES', + function() { return 'var SocketIO = ' + read('socket.io.js') + ';\n' }, + function() { return 'var Peer = ' + read('wrtcp.js') + ';\n' }], +#else + $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'], #endif - var stream = FS.getStream(fd); - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } - var arg = {{{ makeGetValue('varargs', '0', 'i32') }}}; + $Sockets: { + BUFFER_SIZE: 10*1024, // initial size + MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer - try { - return FS.ioctl(stream, request, arg); - } catch (e) { - FS.handleFSError(e); - return -1; - } + nextFd: 1, + fds: {}, + nextport: 1, + maxport: 65535, + peer: null, + connections: {}, + portmap: {}, + localAddr: 0xfe00000a, // Local address is always 10.0.0.254 + addrPool: [ 0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a, + 0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a, + 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a] /* 0x0100000a is reserved */ }, -#endif - - // pty.h - - openpty: function() { throw 'openpty: TODO' }, - forkpty: function() { throw 'forkpty: TODO' }, - - // grp.h - - initgroups: function() { throw 'initgroups: TODO' }, // pwd.h @@ -8376,6 +4068,9 @@ LibraryManager.library = { abort('Undefined behavior! ubsan_handle_float_cast_overflow: ' + [id, post]); }, + // USE_FULL_LIBRARY hacks + realloc: function() { throw 'bad' }, + // internal musl requirements that we do, for now _pthread_cleanup_push: function(){}, _pthread_cleanup_pop: function(){}, diff --git a/src/library_bootstrap_structInfo.js b/src/library_bootstrap_structInfo.js index 88deeaf039d76..a0808a7bebf5f 100644 --- a/src/library_bootstrap_structInfo.js +++ b/src/library_bootstrap_structInfo.js @@ -30,25 +30,6 @@ LibraryManager.library = { if (bytes != 0) self.alloc(bytes); return ret; // Previous break location. }, - printf__deps: ['_formatString'], - printf: function(format, varargs) { - // int printf(const char *restrict format, ...); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html - // extra effort to support printf, even without a filesystem. very partial, very hackish - var result = __formatString(format, varargs); - var string = intArrayToString(result); - if (string[string.length-1] === '\n') string = string.substr(0, string.length-1); // remove a final \n, as Module.print will do that - Module.print(string); - return result.length; - }, - puts: function(s) { - // extra effort to support puts, even without a filesystem. very partial, very hackish - var result = Pointer_stringify(s); - var string = result.substr(0); - if (string[string.length-1] === '\n') string = string.substr(0, string.length-1); // remove a final \n, as Module.print will do that - Module.print(string); - return result.length; - }, malloc: function(x) { return Runtime.dynamicAlloc(x); }, diff --git a/src/library_formatString.js b/src/library_formatString.js index 4dda7b1282bd5..f8de643d1143f 100644 --- a/src/library_formatString.js +++ b/src/library_formatString.js @@ -417,6 +417,27 @@ mergeInto(LibraryManager.library, { } } return ret; - } + }, + + // printf/puts implementations for when musl is not pulled in - very partial. useful for tests, and when bootstrapping structInfo + printf__deps: ['_formatString'], + printf: function(format, varargs) { + // int printf(const char *restrict format, ...); + // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html + // extra effort to support printf, even without a filesystem. very partial, very hackish + var result = __formatString(format, varargs); + var string = intArrayToString(result); + if (string[string.length-1] === '\n') string = string.substr(0, string.length-1); // remove a final \n, as Module.print will do that + Module.print(string); + return result.length; + }, + puts: function(s) { + // extra effort to support puts, even without a filesystem. very partial, very hackish + var result = Pointer_stringify(s); + var string = result.substr(0); + if (string[string.length-1] === '\n') string = string.substr(0, string.length-1); // remove a final \n, as Module.print will do that + Module.print(string); + return result.length; + }, }); diff --git a/src/library_fs.js b/src/library_fs.js index 86bd8d30f2807..c97829304cf8d 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1,5 +1,5 @@ mergeInto(LibraryManager.library, { - $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr', 'fflush'], + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$PATH', '$TTY', '$MEMFS', '$IDBFS', '$NODEFS', 'stdin', 'stdout', 'stderr'], $FS__postset: 'FS.staticInit();' + '__ATINIT__.unshift(function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() });' + '__ATMAIN__.push(function() { FS.ignorePermissions = false });' + @@ -283,8 +283,7 @@ mergeInto(LibraryManager.library, { }, // convert O_* bitmask to a string for nodePermissions flagsToPermissionString: function(flag) { - var accmode = flag & {{{ cDefine('O_ACCMODE') }}}; - var perms = ['r', 'w', 'rw'][accmode]; + var perms = ['r', 'w', 'rw'][flag & 3]; if ((flag & {{{ cDefine('O_TRUNC') }}})) { perms += 'w'; } diff --git a/src/library_gl.js b/src/library_gl.js index 4bed304827897..8678503ae4f78 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -5525,6 +5525,7 @@ var LibraryGL = { // Generate index data in a format suitable for GLES 2.0/WebGL var numVertexes = 4 * GLImmediate.vertexCounter / GLImmediate.stride; + if (!numVertexes) return; #if ASSERTIONS assert(numVertexes % 1 == 0, "`numVertexes` must be an integer."); #endif diff --git a/src/library_openal.js b/src/library_openal.js index 8d93a1985e9b2..b7422adc7f9c0 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -973,6 +973,9 @@ var LibraryOpenAL = { case 0x1002 /* AL_CONE_OUTER_ANGLE */: {{{ makeSetValue('value', '0', 'src.coneOuterAngle', 'i32') }}}; break; + case 0x1007 /* AL_LOOPING */: + {{{ makeSetValue('value', '0', 'src.loop', 'i32') }}}; + break; case 0x1009 /* AL_BUFFER */: if (!src.queue.length) { {{{ makeSetValue('value', '0', '0', 'i32') }}}; @@ -1008,6 +1011,42 @@ var LibraryOpenAL = { } }, + alGetSourceiv__deps: ['alGetSourcei'], + alGetSourceiv: function(source, param, values) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetSourceiv called without a valid context"); +#endif + return; + } + var src = AL.currentContext.src[source]; + if (!src) { +#if OPENAL_DEBUG + console.error("alGetSourceiv called with an invalid source"); +#endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; + return; + } + switch (param) { + case 0x202 /* AL_SOURCE_RELATIVE */: + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1007 /* AL_LOOPING */: + case 0x1009 /* AL_BUFFER */: + case 0x1010 /* AL_SOURCE_STATE */: + case 0x1015 /* AL_BUFFERS_QUEUED */: + case 0x1016 /* AL_BUFFERS_PROCESSED */: + _alGetSourcei(source, param, values); + break; + default: +#if OPENAL_DEBUG + console.error("alGetSourceiv with param " + param + " not implemented yet"); +#endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + alGetSourcef: function(source, param, value) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -1063,6 +1102,7 @@ var LibraryOpenAL = { } }, + alGetSourcefv__deps: ['alGetSourcef'], alGetSourcefv: function(source, param, values) { if (!AL.currentContext) { #if OPENAL_DEBUG @@ -1079,6 +1119,21 @@ var LibraryOpenAL = { return; } switch (param) { + case 0x1003 /* AL_PITCH */: + case 0x100A /* AL_GAIN */: + case 0x100D /* AL_MIN_GAIN */: + case 0x100E /* AL_MAX_GAIN */: + case 0x1023 /* AL_MAX_DISTANCE */: + case 0x1021 /* AL_ROLLOFF_FACTOR */: + case 0x1022 /* AL_CONE_OUTER_GAIN */: + case 0x1001 /* AL_CONE_INNER_ANGLE */: + case 0x1002 /* AL_CONE_OUTER_ANGLE */: + case 0x1020 /* AL_REFERENCE_DISTANCE */: + case 0x1024 /* AL_SEC_OFFSET */: + case 0x1025 /* AL_SAMPLE_OFFSET */: + case 0x1026 /* AL_BYTE_OFFSET */: + _alGetSourcef(source, param, values); + break; case 0x1004 /* AL_POSITION */: var position = src.position; {{{ makeSetValue('values', '0', 'position[0]', 'float') }}} diff --git a/src/preamble.js b/src/preamble.js index 69884e2f17ee9..176824cf9cb99 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -19,7 +19,7 @@ Runtime.GLOBAL_BASE = Runtime.alignMemory(Runtime.GLOBAL_BASE, {{{ MAX_GLOBAL_AL #endif {{{ maybeExport('Runtime') }}} -#if CLOSURE_COMPILER +#if USE_CLOSURE_COMPILER Runtime['addFunction'] = Runtime.addFunction; Runtime['removeFunction'] = Runtime.removeFunction; #endif diff --git a/src/settings.js b/src/settings.js index 3be9e81e72aea..17192918e5109 100644 --- a/src/settings.js +++ b/src/settings.js @@ -120,7 +120,7 @@ var SIMD = 0; // Whether to allow autovectorized SIMD code ( https://github.com/ // (In older versions of emscripten, in particular pre-fastcomp, SIMD=1 was needed to get // any SIMD output at all.) -var CLOSURE_COMPILER = 0; // Whether closure compiling is being run on this output +var USE_CLOSURE_COMPILER = 0; // Whether closure compiling is being run on this output var SKIP_STACK_IN_SMALL = 1; // When enabled, does not push/pop the stack at all in // functions that have no basic stack usage. But, they @@ -554,6 +554,10 @@ var USE_SDL = 1; // Specify the SDL version that is being linked against. var USE_SDL_IMAGE = 1; // Specify the SDL_image version that is being linked against. Must match USE_SDL var USE_ZLIB = 0; // 1 = use zlib from emscripten-ports var USE_LIBPNG = 0; // 1 = use libpng from emscripten-ports +var USE_BULLET = 0; // 1 = use bullet from emscripten-ports +var USE_VORBIS = 0; // 1 = use vorbis from emscripten-ports +var USE_OGG = 0; // 1 = use ogg from emscripten-ports +var USE_FREETYPE = 0; // 1 = use freetype from emscripten-ports // Compiler debugging options diff --git a/src/shell.js b/src/shell.js index cf3a5c58bf29b..b0a9af7c0914c 100644 --- a/src/shell.js +++ b/src/shell.js @@ -14,7 +14,7 @@ // before the code. Then that object will be used in the code, and you // can continue to use Module afterwards as well. var Module; -#if CLOSURE_COMPILER +#if USE_CLOSURE_COMPILER if (!Module) Module = eval('(function() { try { return {{{ EXPORT_NAME }}} || {} } catch(e) { return {} } })()'); #else if (!Module) Module = (typeof {{{ EXPORT_NAME }}} !== 'undefined' ? {{{ EXPORT_NAME }}} : null) || {}; @@ -129,7 +129,7 @@ else if (ENVIRONMENT_IS_SHELL) { Module['arguments'] = arguments; } -#if CLOSURE_COMPILER +#if USE_CLOSURE_COMPILER eval("if (typeof gc === 'function' && gc.toString().indexOf('[native code]') > 0) var gc = undefined"); // wipe out the SpiderMonkey shell 'gc' function, which can confuse closure (uses it as a minified name, and it is then initted to a non-falsey value unexpectedly) #endif } diff --git a/tests/bullet_hello_world.cpp b/tests/bullet_hello_world.cpp new file mode 100644 index 0000000000000..1e995a5449f1a --- /dev/null +++ b/tests/bullet_hello_world.cpp @@ -0,0 +1,66 @@ +#include + +#include + +int main(void) { + + btBroadphaseInterface *broadphase = new btDbvtBroadphase(); + + btDefaultCollisionConfiguration *collisionConfiguration = new btDefaultCollisionConfiguration(); + btCollisionDispatcher *dispatcher = new btCollisionDispatcher(collisionConfiguration); + + btSequentialImpulseConstraintSolver *solver = new btSequentialImpulseConstraintSolver; + + btDiscreteDynamicsWorld *dynamicsWorld = + new btDiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); + + dynamicsWorld->setGravity(btVector3(0, -10, 0)); + + btCollisionShape *groundShape = new btStaticPlaneShape(btVector3(0, 1, 0), 1); + + btCollisionShape *fallShape = new btSphereShape(1); + + btDefaultMotionState *groundMotionState = + new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, -1, 0))); + btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(0, groundMotionState, groundShape, + btVector3(0, 0, 0)); + btRigidBody *groundRigidBody = new btRigidBody(groundRigidBodyCI); + dynamicsWorld->addRigidBody(groundRigidBody); + + btDefaultMotionState *fallMotionState = + new btDefaultMotionState(btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 50, 0))); + btScalar mass = 1; + btVector3 fallInertia(0, 0, 0); + fallShape->calculateLocalInertia(mass, fallInertia); + btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(mass, fallMotionState, fallShape, + fallInertia); + btRigidBody *fallRigidBody = new btRigidBody(fallRigidBodyCI); + dynamicsWorld->addRigidBody(fallRigidBody); + + for(int i = 0; i < 300; i++) { + dynamicsWorld->stepSimulation(1 / 60.f, 10); + btTransform trans; + fallRigidBody->getMotionState()->getWorldTransform(trans); + } + printf("BULLET RUNNING\n"); + + dynamicsWorld->removeRigidBody(fallRigidBody); + delete fallRigidBody->getMotionState(); + delete fallRigidBody; + + dynamicsWorld->removeRigidBody(groundRigidBody); + delete groundRigidBody->getMotionState(); + delete groundRigidBody; + + delete fallShape; + + delete groundShape; + + delete dynamicsWorld; + delete solver; + delete collisionConfiguration; + delete dispatcher; + delete broadphase; + + return 0; +} diff --git a/tests/cases/entry2.ll b/tests/cases/entry2.ll index 9ef869980577c..231b1f54c77ef 100644 --- a/tests/cases/entry2.ll +++ b/tests/cases/entry2.ll @@ -2,16 +2,16 @@ target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:128-n32-S128" target triple = "asmjs-unknown-emscripten" -@.str = private unnamed_addr constant [11 x i8] c"getgid=%d\0A\00", align 1 +@.str = private unnamed_addr constant [11 x i8] c"sched_=%d\0A\00", align 1 @.str1 = private unnamed_addr constant [6 x i8] c"f=%d\0A\00", align 1 define internal i32 @_Z1fii(i32, i32) noinline { - %3 = tail call i32 @getgid() + %3 = tail call i32 @sched_yield() %4 = icmp eq i32 %3, 0 br i1 %4, label %7, label %5 ;