~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to src/library.js

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-06-11 15:45:24 UTC
  • mfrom: (1.2.1) (2.1.1 experimental)
  • Revision ID: package-import@ubuntu.com-20130611154524-rppb3w6tixlegv4n
Tags: 1.4.7~20130611~a1eb425-1
* New snapshot release
* Upload to unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
// object. For convenience, the short name appears here. Note that if you add a
16
16
// new function with an '_', it will not be found.
17
17
 
 
18
// Memory allocated during startup, in postsets, should only be ALLOC_STATIC
 
19
 
18
20
LibraryManager.library = {
19
21
  // ==========================================================================
20
22
  // File system base.
21
23
  // ==========================================================================
22
24
 
23
25
  // keep this low in memory, because we flatten arrays with them in them
24
 
  stdin: 'allocate(1, "i32*", ALLOC_STACK)',
25
 
  stdout: 'allocate(1, "i32*", ALLOC_STACK)',
26
 
  stderr: 'allocate(1, "i32*", ALLOC_STACK)',
27
 
  _impure_ptr: 'allocate(1, "i32*", ALLOC_STACK)',
 
26
  stdin: 'allocate(1, "i32*", ALLOC_STATIC)',
 
27
  stdout: 'allocate(1, "i32*", ALLOC_STATIC)',
 
28
  stderr: 'allocate(1, "i32*", ALLOC_STATIC)',
 
29
  _impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)',
28
30
 
29
31
  $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'],
30
32
  $FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' +
61
63
    // This is set to false when the runtime is initialized, allowing you
62
64
    // to modify the filesystem freely before run() is called.
63
65
    ignorePermissions: true,
 
66
    createFileHandle: function(stream, fd) {
 
67
      if (typeof stream === 'undefined') {
 
68
        stream = null;
 
69
      }
 
70
      if (!fd) {
 
71
        if (stream && stream.socket) {
 
72
          for (var i = 1; i < 64; i++) {
 
73
            if (!FS.streams[i]) {
 
74
              fd = i;
 
75
              break;
 
76
            }
 
77
          }
 
78
          assert(fd, 'ran out of low fds for sockets');
 
79
        } else {
 
80
          fd = Math.max(FS.streams.length, 64);
 
81
          for (var i = FS.streams.length; i < fd; i++) {
 
82
            FS.streams[i] = null; // Keep dense
 
83
          }
 
84
        }
 
85
      }
 
86
      // Close WebSocket first if we are about to replace the fd (i.e. dup2)
 
87
      if (FS.streams[fd] && FS.streams[fd].socket && FS.streams[fd].socket.close) {
 
88
        FS.streams[fd].socket.close();
 
89
      }
 
90
      FS.streams[fd] = stream;
 
91
      return fd;
 
92
    },
 
93
    removeFileHandle: function(fd) {
 
94
      FS.streams[fd] = null;
 
95
    },
64
96
    joinPath: function(parts, forceRelative) {
65
97
      var ret = parts[0];
66
98
      for (var i = 1; i < parts.length; i++) {
294
326
      if (typeof XMLHttpRequest !== 'undefined') {
295
327
        if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';
296
328
        // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.
297
 
        var LazyUint8Array = function(chunkSize, length) {
298
 
          this.length = length;
299
 
          this.chunkSize = chunkSize;
 
329
        var LazyUint8Array = function() {
 
330
          this.lengthKnown = false;
300
331
          this.chunks = []; // Loaded chunks. Index is the chunk number
301
332
        }
302
333
        LazyUint8Array.prototype.get = function(idx) {
303
334
          if (idx > this.length-1 || idx < 0) {
304
335
            return undefined;
305
336
          }
306
 
          var chunkOffset = idx % chunkSize;
307
 
          var chunkNum = Math.floor(idx / chunkSize);
 
337
          var chunkOffset = idx % this.chunkSize;
 
338
          var chunkNum = Math.floor(idx / this.chunkSize);
308
339
          return this.getter(chunkNum)[chunkOffset];
309
340
        }
310
341
        LazyUint8Array.prototype.setDataGetter = function(getter) {
311
342
          this.getter = getter;
312
343
        }
313
 
  
314
 
        // Find length
315
 
        var xhr = new XMLHttpRequest();
316
 
        xhr.open('HEAD', url, false);
317
 
        xhr.send(null);
318
 
        if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
319
 
        var datalength = Number(xhr.getResponseHeader("Content-length"));
320
 
        var header;
321
 
        var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
 
344
 
 
345
        LazyUint8Array.prototype.cacheLength = function() {
 
346
            // Find length
 
347
            var xhr = new XMLHttpRequest();
 
348
            xhr.open('HEAD', url, false);
 
349
            xhr.send(null);
 
350
            if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
 
351
            var datalength = Number(xhr.getResponseHeader("Content-length"));
 
352
            var header;
 
353
            var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes";
322
354
#if SMALL_XHR_CHUNKS
323
 
        var chunkSize = 1024; // Chunk size in bytes
 
355
            var chunkSize = 1024; // Chunk size in bytes
324
356
#else
325
 
        var chunkSize = 1024*1024; // Chunk size in bytes
 
357
            var chunkSize = 1024*1024; // Chunk size in bytes
326
358
#endif
327
 
        if (!hasByteServing) chunkSize = datalength;
328
 
  
329
 
        // Function to get a range from the remote URL.
330
 
        var doXHR = (function(from, to) {
331
 
          if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
332
 
          if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
333
 
  
334
 
          // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
335
 
          var xhr = new XMLHttpRequest();
336
 
          xhr.open('GET', url, false);
337
 
          if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
338
 
  
339
 
          // Some hints to the browser that we want binary data.
340
 
          if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
341
 
          if (xhr.overrideMimeType) {
342
 
            xhr.overrideMimeType('text/plain; charset=x-user-defined');
343
 
          }
344
 
  
345
 
          xhr.send(null);
346
 
          if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
347
 
          if (xhr.response !== undefined) {
348
 
            return new Uint8Array(xhr.response || []);
349
 
          } else {
350
 
            return intArrayFromString(xhr.responseText || '', true);
351
 
          }
352
 
        });
353
 
  
354
 
        var lazyArray = new LazyUint8Array(chunkSize, datalength);
355
 
        lazyArray.setDataGetter(function(chunkNum) {
356
 
          var start = chunkNum * lazyArray.chunkSize;
357
 
          var end = (chunkNum+1) * lazyArray.chunkSize - 1; // including this byte
358
 
          end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
359
 
          if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
360
 
            lazyArray.chunks[chunkNum] = doXHR(start, end);
361
 
          }
362
 
          if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
363
 
          return lazyArray.chunks[chunkNum];
364
 
        });
 
359
 
 
360
            if (!hasByteServing) chunkSize = datalength;
 
361
 
 
362
            // Function to get a range from the remote URL.
 
363
            var doXHR = (function(from, to) {
 
364
              if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!");
 
365
              if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!");
 
366
 
 
367
              // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.
 
368
              var xhr = new XMLHttpRequest();
 
369
              xhr.open('GET', url, false);
 
370
              if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to);
 
371
 
 
372
              // Some hints to the browser that we want binary data.
 
373
              if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';
 
374
              if (xhr.overrideMimeType) {
 
375
                xhr.overrideMimeType('text/plain; charset=x-user-defined');
 
376
              }
 
377
 
 
378
              xhr.send(null);
 
379
              if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status);
 
380
              if (xhr.response !== undefined) {
 
381
                return new Uint8Array(xhr.response || []);
 
382
              } else {
 
383
                return intArrayFromString(xhr.responseText || '', true);
 
384
              }
 
385
            });
 
386
            var lazyArray = this;
 
387
            lazyArray.setDataGetter(function(chunkNum) {
 
388
              var start = chunkNum * chunkSize;
 
389
              var end = (chunkNum+1) * chunkSize - 1; // including this byte
 
390
              end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block
 
391
              if (typeof(lazyArray.chunks[chunkNum]) === "undefined") {
 
392
                lazyArray.chunks[chunkNum] = doXHR(start, end);
 
393
              }
 
394
              if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!");
 
395
              return lazyArray.chunks[chunkNum];
 
396
            });
 
397
 
 
398
            this._length = datalength;
 
399
            this._chunkSize = chunkSize;
 
400
            this.lengthKnown = true;
 
401
        }
 
402
 
 
403
        var lazyArray = new LazyUint8Array();
 
404
        Object.defineProperty(lazyArray, "length", {
 
405
            get: function() {
 
406
                if(!this.lengthKnown) {
 
407
                    this.cacheLength();
 
408
                }
 
409
                return this._length;
 
410
            }
 
411
        });
 
412
        Object.defineProperty(lazyArray, "chunkSize", {
 
413
            get: function() {
 
414
                if(!this.lengthKnown) {
 
415
                    this.cacheLength();
 
416
                }
 
417
                return this._chunkSize;
 
418
            }
 
419
        });
 
420
 
365
421
        var properties = { isDevice: false, contents: lazyArray };
366
422
      } else {
367
423
        var properties = { isDevice: false, url: url };
535
591
      var stdout = FS.createDevice(devFolder, 'stdout', null, output);
536
592
      var stderr = FS.createDevice(devFolder, 'stderr', null, error);
537
593
      FS.createDevice(devFolder, 'tty', input, output);
 
594
      FS.createDevice(devFolder, 'null', function(){}, function(){});
538
595
 
539
596
      // Create default streams.
540
597
      FS.streams[1] = {
573
630
        eof: false,
574
631
        ungotten: []
575
632
      };
576
 
      assert(Math.max(_stdin, _stdout, _stderr) < 1024); // make sure these are low, we flatten arrays with these
 
633
      // TODO: put these low in memory like we used to assert on: assert(Math.max(_stdin, _stdout, _stderr) < 15000); // make sure these are low, we flatten arrays with these
577
634
      {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 1, 'void*') }}};
578
635
      {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 2, 'void*') }}};
579
636
      {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 3, 'void*') }}};
590
647
      FS.streams[_stderr] = FS.streams[3];
591
648
#if ASSERTIONS
592
649
      FS.checkStreams();
593
 
      assert(FS.streams.length < 1024); // at this early stage, we should not have a large set of file descriptors - just a few
 
650
      // see previous TODO on stdin etc.: assert(FS.streams.length < 1024); // at this early stage, we should not have a large set of file descriptors - just a few
594
651
#endif
595
652
      allocate([ allocate(
596
653
        {{{ Runtime.QUANTUM_SIZE === 4 ? '[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0]' : '[0, _stdin, _stdout, _stderr]' }}},
597
 
        'void*', ALLOC_STATIC) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}});
 
654
        'void*', ALLOC_NORMAL) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}});
598
655
    },
599
656
 
600
657
    quit: function() {
650
707
      ___setErrNo(ERRNO_CODES.EACCES);
651
708
      return 0;
652
709
    }
653
 
    var id = FS.streams.length; // Keep dense
654
710
    var contents = [];
655
711
    for (var key in target.contents) contents.push(key);
656
 
    FS.streams[id] = {
 
712
    var id = FS.createFileHandle({
657
713
      path: path,
658
714
      object: target,
659
715
      // An index into contents. Special values: -2 is ".", -1 is "..".
670
726
      contents: contents,
671
727
      // Each stream has its own area for readdir() returns.
672
728
      currentEntry: _malloc(___dirent_struct_layout.__size__)
673
 
    };
 
729
    });
674
730
#if ASSERTIONS
675
731
    FS.checkStreams();
676
732
#endif
1188
1244
      finalPath = path.parentPath + '/' + path.name;
1189
1245
    }
1190
1246
    // Actually create an open stream.
1191
 
    var id = FS.streams.length; // Keep dense
 
1247
    var id;
1192
1248
    if (target.isFolder) {
1193
1249
      var entryBuffer = 0;
1194
1250
      if (___dirent_struct_layout) {
1196
1252
      }
1197
1253
      var contents = [];
1198
1254
      for (var key in target.contents) contents.push(key);
1199
 
      FS.streams[id] = {
 
1255
      id = FS.createFileHandle({
1200
1256
        path: finalPath,
1201
1257
        object: target,
1202
1258
        // An index into contents. Special values: -2 is ".", -1 is "..".
1213
1269
        contents: contents,
1214
1270
        // Each stream has its own area for readdir() returns.
1215
1271
        currentEntry: entryBuffer
1216
 
      };
 
1272
      });
1217
1273
    } else {
1218
 
      FS.streams[id] = {
 
1274
      id = FS.createFileHandle({
1219
1275
        path: finalPath,
1220
1276
        object: target,
1221
1277
        position: 0,
1225
1281
        error: false,
1226
1282
        eof: false,
1227
1283
        ungotten: []
1228
 
      };
 
1284
      });
1229
1285
    }
1230
1286
#if ASSERTIONS
1231
1287
    FS.checkStreams();
1268
1324
          newStream[member] = stream[member];
1269
1325
        }
1270
1326
        arg = dup2 ? arg : Math.max(arg, FS.streams.length); // dup2 wants exactly arg; fcntl wants a free descriptor >= arg
1271
 
        for (var i = FS.streams.length; i < arg; i++) {
1272
 
          FS.streams[i] = null; // Keep dense
1273
 
        }
1274
 
        FS.streams[arg] = newStream;
 
1327
        FS.createFileHandle(newStream, arg);
1275
1328
#if ASSERTIONS
1276
1329
        FS.checkStreams();
1277
1330
#endif
1748
1801
      return bytesRead;
1749
1802
    }
1750
1803
  },
1751
 
  read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'pread'],
 
1804
  read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'recv', 'pread'],
1752
1805
  read: function(fildes, buf, nbyte) {
1753
1806
    // ssize_t read(int fildes, void *buf, size_t nbyte);
1754
1807
    // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html
1755
1808
    var stream = FS.streams[fildes];
1756
 
    if (!stream) {
 
1809
    if (stream && ('socket' in stream)) {
 
1810
      return _recv(fildes, buf, nbyte, 0);
 
1811
    } else if (!stream) {
1757
1812
      ___setErrNo(ERRNO_CODES.EBADF);
1758
1813
      return -1;
1759
1814
    } else if (!stream.isRead) {
1779
1834
              ___setErrNo(ERRNO_CODES.EIO);
1780
1835
              return -1;
1781
1836
            }
 
1837
            if (result === undefined && bytesRead === 0) {
 
1838
              ___setErrNo(ERRNO_CODES.EAGAIN);
 
1839
              return -1;
 
1840
            }
1782
1841
            if (result === null || result === undefined) break;
1783
1842
            bytesRead++;
1784
1843
            {{{ makeSetValue('buf', 'i', 'result', 'i8') }}}
1944
2003
      return i;
1945
2004
    }
1946
2005
  },
1947
 
  write__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'pwrite'],
 
2006
  write__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'send', 'pwrite'],
1948
2007
  write: function(fildes, buf, nbyte) {
1949
2008
    // ssize_t write(int fildes, const void *buf, size_t nbyte);
1950
2009
    // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html
1951
2010
    var stream = FS.streams[fildes];
1952
 
    if (!stream) {
 
2011
    if (stream && ('socket' in stream)) {
 
2012
        return _send(fildes, buf, nbyte, 0);
 
2013
    } else if (!stream) {
1953
2014
      ___setErrNo(ERRNO_CODES.EBADF);
1954
2015
      return -1;
1955
2016
    } else if (!stream.isWrite) {
2425
2486
    // Implement a Linux-like 'memory area' for our 'process'.
2426
2487
    // Changes the size of the memory area by |bytes|; returns the
2427
2488
    // address of the previous top ('break') of the memory area
2428
 
 
2429
 
    // We need to make sure no one else allocates unfreeable memory!
2430
 
    // We must control this entirely. So we don't even need to do
2431
 
    // unfreeable allocations - the HEAP is ours, from STATICTOP up.
2432
 
    // TODO: We could in theory slice off the top of the HEAP when
2433
 
    //       sbrk gets a negative increment in |bytes|...
 
2489
    // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP
2434
2490
    var self = _sbrk;
2435
2491
    if (!self.called) {
2436
 
      STATICTOP = alignMemoryPage(STATICTOP); // make sure we start out aligned
 
2492
      DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned
2437
2493
      self.called = true;
2438
 
#if GC_SUPPORT
2439
 
      _sbrk.DYNAMIC_START = STATICTOP;
2440
 
#endif
 
2494
      assert(Runtime.dynamicAlloc);
 
2495
      self.alloc = Runtime.dynamicAlloc;
 
2496
      Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') };
2441
2497
    }
2442
 
    var ret = STATICTOP;
2443
 
    if (bytes != 0) Runtime.staticAlloc(bytes);
 
2498
    var ret = DYNAMICTOP;
 
2499
    if (bytes != 0) self.alloc(bytes);
2444
2500
    return ret;  // Previous break location.
2445
2501
  },
2446
2502
  open64: 'open',
2468
2524
      __scanString.whiteSpace[{{{ charCode(' ') }}}] = 1;
2469
2525
      __scanString.whiteSpace[{{{ charCode('\t') }}}] = 1;
2470
2526
      __scanString.whiteSpace[{{{ charCode('\n') }}}] = 1;
 
2527
      __scanString.whiteSpace[{{{ charCode('\v') }}}] = 1;
 
2528
      __scanString.whiteSpace[{{{ charCode('\f') }}}] = 1;
 
2529
      __scanString.whiteSpace[{{{ charCode('\r') }}}] = 1;
2471
2530
      __scanString.whiteSpace[' '] = 1;
2472
2531
      __scanString.whiteSpace['\t'] = 1;
2473
2532
      __scanString.whiteSpace['\n'] = 1;
 
2533
      __scanString.whiteSpace['\v'] = 1;
 
2534
      __scanString.whiteSpace['\f'] = 1;
 
2535
      __scanString.whiteSpace['\r'] = 1;
2474
2536
    }
2475
2537
    // Supports %x, %4x, %d.%d, %lld, %s, %f, %lf.
2476
2538
    // TODO: Support all format specifiers.
2546
2608
        if (format[formatIndex] == 'l') {
2547
2609
          long_ = true;
2548
2610
          formatIndex++;
2549
 
          if(format[formatIndex] == 'l') {
 
2611
          if (format[formatIndex] == 'l') {
2550
2612
            longLong = true;
2551
2613
            formatIndex++;
2552
2614
          }
2559
2621
        var curr = 0;
2560
2622
        var buffer = [];
2561
2623
        // 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
2562
 
        if (type == 'f' || type == 'e' || type == 'g' || type == 'E') {
 
2624
        if (type == 'f' || type == 'e' || type == 'g' || 
 
2625
            type == 'F' || type == 'E' || type == 'G') {
2563
2626
          var last = 0;
2564
2627
          next = get();
2565
2628
          while (next > 0) {
2581
2644
                (type == 's' ||
2582
2645
                 ((type === 'd' || type == 'u' || type == 'i') && ((next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}}) ||
2583
2646
                                                                   (first && next == {{{ charCode('-') }}}))) ||
2584
 
                 (type === 'x' && (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}} ||
 
2647
                 ((type === 'x' || type === 'X') && (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}} ||
2585
2648
                                   next >= {{{ charCode('a') }}} && next <= {{{ charCode('f') }}} ||
2586
2649
                                   next >= {{{ charCode('A') }}} && next <= {{{ charCode('F') }}}))) &&
2587
2650
                (formatIndex >= format.length || next !== format[formatIndex].charCodeAt(0))) { // Stop when we read something that is coming up
2605
2668
          case 'd': case 'u': case 'i':
2606
2669
            if (half) {
2607
2670
              {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i16') }}};
2608
 
            } else if(longLong) {
 
2671
            } else if (longLong) {
2609
2672
              {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i64') }}};
2610
2673
            } else {
2611
2674
              {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i32') }}};
2612
2675
            }
2613
2676
            break;
 
2677
          case 'X':
2614
2678
          case 'x':
2615
2679
            {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}}
2616
2680
            break;
 
2681
          case 'F':
2617
2682
          case 'f':
 
2683
          case 'E':
2618
2684
          case 'e':
 
2685
          case 'G':
2619
2686
          case 'g':
2620
2687
          case 'E':
2621
2688
            // fallthrough intended
2843
2910
            } else if (next == {{{ charCode('o') }}}) {
2844
2911
              argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8);
2845
2912
            } else if (next == {{{ charCode('x') }}} || next == {{{ charCode('X') }}}) {
2846
 
              prefix = flagAlternative ? '0x' : '';
 
2913
              prefix = (flagAlternative && currArg != 0) ? '0x' : '';
2847
2914
#if PRECISE_I64_MATH
2848
2915
              if (argSize == 8 && i64Math) {
2849
2916
                if (origArg[1]) {
3007
3074
          }
3008
3075
          case 's': {
3009
3076
            // String.
3010
 
            var arg = getNextArg('i8*') || nullString;
3011
 
            var argLength = _strlen(arg);
 
3077
            var arg = getNextArg('i8*');
 
3078
            var argLength = arg ? _strlen(arg) : '(null)'.length;
3012
3079
            if (precisionSet) argLength = Math.min(argLength, precision);
3013
3080
            if (!flagLeftAlign) {
3014
3081
              while (argLength < width--) {
3015
3082
                ret.push({{{ charCode(' ') }}});
3016
3083
              }
3017
3084
            }
3018
 
            for (var i = 0; i < argLength; i++) {
3019
 
              ret.push({{{ makeGetValue('arg++', 0, 'i8', null, true) }}});
 
3085
            if (arg) {
 
3086
              for (var i = 0; i < argLength; i++) {
 
3087
                ret.push({{{ makeGetValue('arg++', 0, 'i8', null, true) }}});
 
3088
              }
 
3089
            } else {
 
3090
              ret = ret.concat(intArrayFromString('(null)'.substr(0, argLength), true));
3020
3091
            }
3021
3092
            if (flagLeftAlign) {
3022
3093
              while (argLength < width--) {
3452
3523
    } else if (oldObj.isRoot || oldObj.path == FS.currentPath) {
3453
3524
      ___setErrNo(ERRNO_CODES.EBUSY);
3454
3525
      return -1;
3455
 
    } else if (newObj.path && newObj.path.indexOf(oldObj.path) == 0) {
 
3526
    } else if (newObj.parentPath &&
 
3527
               newObj.parentPath.indexOf(oldObj.path) == 0) {
3456
3528
      ___setErrNo(ERRNO_CODES.EINVAL);
3457
3529
      return -1;
3458
3530
    } else if (newObj.exists && newObj.object.isFolder) {
3545
3617
    return -1;
3546
3618
  },
3547
3619
  fscanf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES',
3548
 
                 '_scanString', 'getc', 'ungetc'],
 
3620
                 '_scanString', 'fgetc', 'fseek', 'ftell'],
3549
3621
  fscanf: function(stream, format, varargs) {
3550
3622
    // int fscanf(FILE *restrict stream, const char *restrict format, ... );
3551
3623
    // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html
3552
3624
    if (FS.streams[stream]) {
3553
 
      var stack = [];
3554
 
      var get = function() { var ret = _fgetc(stream); stack.push(ret); return ret };
3555
 
      var unget = function(c) { return _ungetc(stack.pop(), stream) };
 
3625
      var i = _ftell(stream), SEEK_SET = 0;
 
3626
      var get = function () { i++; return _fgetc(stream); };
 
3627
      var unget = function () { _fseek(stream, --i, SEEK_SET); };
3556
3628
      return __scanString(format, get, unget, varargs);
3557
3629
    } else {
3558
3630
      return -1;
3695
3767
     * this implementation simply uses malloc underneath the call to
3696
3768
     * mmap.
3697
3769
     */
 
3770
    var MAP_PRIVATE = 2;
 
3771
    var allocated = false;
 
3772
 
3698
3773
    if (!_mmap.mappings) _mmap.mappings = {};
 
3774
 
3699
3775
    if (stream == -1) {
3700
3776
      var ptr = _malloc(num);
 
3777
      if (!ptr) return -1;
 
3778
      _memset(ptr, 0, num);
 
3779
      allocated = true;
3701
3780
    } else {
3702
3781
      var info = FS.streams[stream];
3703
3782
      if (!info) return -1;
3704
3783
      var contents = info.object.contents;
3705
 
      contents = Array.prototype.slice.call(contents, offset, offset+num);
3706
 
      ptr = allocate(contents, 'i8', ALLOC_NORMAL);
3707
 
    }
3708
 
    // align to page size
3709
 
    var ret = ptr;
3710
 
    if (ptr % PAGE_SIZE != 0) {
3711
 
      var old = ptr;
3712
 
      ptr = _malloc(num + PAGE_SIZE);
3713
 
      ret = alignMemoryPage(ptr);
3714
 
      _memcpy(ret, old, num);
3715
 
      _free(old);
3716
 
    }
3717
 
    if (stream == -1) {
3718
 
      _memset(ret, 0, num);
3719
 
    }
3720
 
    _mmap.mappings[ret] = { malloc: ptr, num: num };
3721
 
    return ret;
 
3784
      // Only make a new copy when MAP_PRIVATE is specified.
 
3785
      if (flags & MAP_PRIVATE == 0) {
 
3786
        // We can't emulate MAP_SHARED when the file is not backed by HEAP.
 
3787
        assert(contents.buffer === HEAPU8.buffer);
 
3788
        ptr = contents.byteOffset;
 
3789
        allocated = false;
 
3790
      } else {
 
3791
        // Try to avoid unnecessary slices.
 
3792
        if (offset > 0 || offset + num < contents.length) {
 
3793
          if (contents.subarray) {
 
3794
            contents = contents.subarray(offset, offset+num);
 
3795
          } else {
 
3796
            contents = Array.prototype.slice.call(contents, offset, offset+num);
 
3797
          }
 
3798
        }
 
3799
        ptr = _malloc(num);
 
3800
        if (!ptr) return -1;
 
3801
        HEAPU8.set(contents, ptr);
 
3802
        allocated = true;
 
3803
      }
 
3804
    }
 
3805
 
 
3806
    _mmap.mappings[ptr] = { malloc: ptr, num: num, allocated: allocated };
 
3807
    return ptr;
3722
3808
  },
3723
3809
  __01mmap64_: 'mmap',
3724
3810
 
3729
3815
    if (!info) return 0;
3730
3816
    if (num == info.num) {
3731
3817
      _mmap.mappings[start] = null;
3732
 
      _free(info.malloc);
 
3818
      if (info.allocated) {
 
3819
        _free(info.malloc);
 
3820
      }
3733
3821
    }
3734
3822
    return 0;
3735
3823
  },
3750
3838
     * implementation (replaced by dlmalloc normally) so
3751
3839
     * not an issue.
3752
3840
     */
3753
 
    var ptr = Runtime.staticAlloc(bytes + 8);
 
3841
#if ASSERTIONS == 2
 
3842
    Runtime.warnOnce('using stub malloc (reference it from C to have the real one included)');
 
3843
#endif
 
3844
    var ptr = Runtime.dynamicAlloc(bytes + 8);
3754
3845
    return (ptr+8) & 0xFFFFFFF8;
3755
3846
  },
3756
 
  free: function(){},
 
3847
  free: function() {
 
3848
#if ASSERTIONS == 2
 
3849
    Runtime.warnOnce('using stub free (reference it from C to have the real one included)');
 
3850
#endif
 
3851
},
3757
3852
 
3758
3853
  calloc__deps: ['malloc'],
3759
3854
  calloc: function(n, s) {
3864
3959
          str++;
3865
3960
        }
3866
3961
      }
3867
 
    }
 
3962
    } else if (finalBase==16) {
 
3963
      if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) {
 
3964
        if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} ||
 
3965
            {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) {
 
3966
          str += 2;
 
3967
        }
 
3968
      }      
 
3969
    } 
3868
3970
    if (!finalBase) finalBase = 10;
3869
3971
 
3870
3972
    // Get digits.
3915
4017
#if USE_TYPED_ARRAYS == 2
3916
4018
  _parseInt64__deps: ['isspace', '__setErrNo', '$ERRNO_CODES', function() { Types.preciseI64MathUsed = 1 }],
3917
4019
  _parseInt64: function(str, endptr, base, min, max, unsign) {
3918
 
    var start = str;
 
4020
    var isNegative = false;
3919
4021
    // Skip space.
3920
4022
    while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++;
3921
 
 
 
4023
    
3922
4024
    // Check for a plus/minus sign.
3923
4025
    if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) {
3924
4026
      str++;
 
4027
      isNegative = true;
3925
4028
    } else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) {
3926
4029
      str++;
3927
4030
    }
3937
4040
          str += 2;
3938
4041
        } else {
3939
4042
          finalBase = 8;
3940
 
          str++;
3941
4043
          ok = true; // we saw an initial zero, perhaps the entire thing is just "0"
3942
4044
        }
3943
4045
      }
3944
 
    }
 
4046
    } else if (finalBase==16) {
 
4047
      if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) {
 
4048
        if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} ||
 
4049
            {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) {
 
4050
          str += 2;
 
4051
        }
 
4052
      }      
 
4053
    } 
3945
4054
    if (!finalBase) finalBase = 10;
 
4055
    start = str;
3946
4056
 
3947
4057
    // Get digits.
3948
4058
    var chr;
3955
4065
        ok = true;
3956
4066
      }
3957
4067
    }
 
4068
 
3958
4069
    if (!ok) {
3959
4070
      ___setErrNo(ERRNO_CODES.EINVAL);
3960
4071
      {{{ makeStructuralReturn(['0', '0']) }}};
3966
4077
    }
3967
4078
 
3968
4079
    try {
3969
 
      i64Math.fromString(Pointer_stringify(start, str - start), finalBase, min, max, unsign);
 
4080
      var numberString = isNegative ? '-'+Pointer_stringify(start, str - start) : Pointer_stringify(start, str - start);
 
4081
      i64Math.fromString(numberString, finalBase, min, max, unsign);
3970
4082
    } catch(e) {
3971
4083
      ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct
3972
4084
    }
4029
4141
    _free(temp);
4030
4142
  },
4031
4143
 
4032
 
  environ: 'allocate(1, "i32*", ALLOC_STACK)',
 
4144
  environ: 'allocate(1, "i32*", ALLOC_STATIC)',
4033
4145
  __environ__deps: ['environ'],
4034
4146
  __environ: '_environ',
4035
4147
  __buildEnvironment__deps: ['__environ'],
4413
4525
    }
4414
4526
    return pdest|0;
4415
4527
  },
4416
 
  
 
4528
 
4417
4529
  strlwr__deps:['tolower'],
4418
4530
  strlwr: function(pstr){
4419
4531
    var i = 0;
4420
4532
    while(1) {
4421
4533
      var x = {{{ makeGetValue('pstr', 'i', 'i8') }}};
4422
 
      if(x == 0) break;
 
4534
      if (x == 0) break;
4423
4535
      {{{ makeSetValue('pstr', 'i', '_tolower(x)', 'i8') }}};
4424
4536
      i++;
4425
4537
    }
4426
4538
  },
4427
 
  
 
4539
 
4428
4540
  strupr__deps:['toupper'],
4429
4541
  strupr: function(pstr){
4430
4542
    var i = 0;
4431
4543
    while(1) {
4432
4544
      var x = {{{ makeGetValue('pstr', 'i', 'i8') }}};
4433
 
      if(x == 0) break;
 
4545
      if (x == 0) break;
4434
4546
      {{{ makeSetValue('pstr', 'i', '_toupper(x)', 'i8') }}};
4435
4547
      i++;
4436
4548
    }
4442
4554
  strcat: function(pdest, psrc) {
4443
4555
    pdest = pdest|0; psrc = psrc|0;
4444
4556
    var i = 0;
4445
 
    pdest = (pdest + (_strlen(pdest)|0))|0;
 
4557
    var pdestEnd = 0;
 
4558
    pdestEnd = (pdest + (_strlen(pdest)|0))|0;
4446
4559
    do {
4447
 
      {{{ makeCopyValues('pdest+i', 'psrc+i', 1, 'i8', null, 1) }}};
 
4560
      {{{ makeCopyValues('pdestEnd+i', 'psrc+i', 1, 'i8', null, 1) }}};
4448
4561
      i = (i+1)|0;
4449
4562
    } while ({{{ makeGetValueAsm('psrc', 'i-1', 'i8') }}});
4450
4563
    return pdest|0;
4605
4718
    if (size < 0) {
4606
4719
      size = 0;
4607
4720
    }
4608
 
    
 
4721
 
4609
4722
    var newStr = _malloc(size + 1);
4610
4723
    {{{ makeCopyValues('newStr', 'ptr', 'size', 'null', null, 1) }}};
4611
4724
    {{{ makeSetValue('newStr', 'size', '0', 'i8') }}};
4924
5037
      }
4925
5038
      return 8;
4926
5039
    }
4927
 
    return 'var ctlz_i8 = allocate([' + range(256).map(function(x) { return ctlz(x) }).join(',') + '], "i8", ALLOC_STACK);';
 
5040
    return 'var ctlz_i8 = allocate([' + range(256).map(function(x) { return ctlz(x) }).join(',') + '], "i8", ALLOC_STATIC);';
4928
5041
  }],
4929
5042
  llvm_ctlz_i32__asm: true,
4930
5043
  llvm_ctlz_i32__sig: 'ii',
4960
5073
      }
4961
5074
      return 8;
4962
5075
    }
4963
 
    return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STACK);';
 
5076
    return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);';
4964
5077
  }],
4965
5078
  llvm_cttz_i32__asm: true,
4966
5079
  llvm_cttz_i32__sig: 'ii',
5033
5146
    return _malloc(size);
5034
5147
  },
5035
5148
  __cxa_free_exception: function(ptr) {
5036
 
    return _free(ptr);
 
5149
    try {
 
5150
      return _free(ptr);
 
5151
    } catch(e) { // XXX FIXME
 
5152
#if ASSERTIONS
 
5153
      Module.printErr('exception during cxa_free_exception: ' + e);
 
5154
#endif
 
5155
    }
5037
5156
  },
5038
5157
  __cxa_throw__sig: 'viii',
5039
5158
  __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'],
5096
5215
    }
5097
5216
    // Clear state flag.
5098
5217
#if ASM_JS
5099
 
    asm.setThrew(0);
 
5218
    asm['setThrew'](0);
5100
5219
#else
5101
5220
    __THREW__ = 0;
5102
5221
#endif
5521
5640
  frexp: function(x, exp_addr) {
5522
5641
    var sig = 0, exp_ = 0;
5523
5642
    if (x !== 0) {
 
5643
      var sign = 1;
 
5644
      if (x < 0) {
 
5645
        x = -x;
 
5646
        sign = -1;
 
5647
      }
5524
5648
      var raw_exp = Math.log(x)/Math.log(2);
5525
5649
      exp_ = Math.ceil(raw_exp);
5526
5650
      if (exp_ === raw_exp) exp_ += 1;
5527
 
      sig = x/Math.pow(2, exp_);
 
5651
      sig = sign*x/Math.pow(2, exp_);
5528
5652
    }
5529
5653
    {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}}
5530
5654
    return sig;
5918
6042
    ['i32', 'tm_gmtoff'],
5919
6043
    ['i32', 'tm_zone']]),
5920
6044
  // Statically allocated time struct.
5921
 
  __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)',
 
6045
  __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)',
5922
6046
  // Statically allocated timezone strings.
5923
6047
  __tm_timezones: {},
5924
6048
  // Statically allocated time strings.
5925
 
  __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STACK)',
 
6049
  __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)',
5926
6050
 
5927
6051
  mktime__deps: ['__tm_struct_layout', 'tzset'],
5928
6052
  mktime: function(tmPtr) {
6056
6180
 
6057
6181
  // TODO: Initialize these to defaults on startup from system settings.
6058
6182
  // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm)
6059
 
  _tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STACK)',
6060
 
  _daylight: 'allocate(1, "i32*", ALLOC_STACK)',
6061
 
  _timezone: 'allocate(1, "i32*", ALLOC_STACK)',
 
6183
  _tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)',
 
6184
  _daylight: 'allocate(1, "i32*", ALLOC_STATIC)',
 
6185
  _timezone: 'allocate(1, "i32*", ALLOC_STATIC)',
6062
6186
  tzset__deps: ['_tzname', '_daylight', '_timezone'],
6063
6187
  tzset: function() {
6064
6188
    // TODO: Use (malleable) environment variables instead of system settings.
6131
6255
  clock_gettime__deps: ['__timespec_struct_layout'],
6132
6256
  clock_gettime: function(clk_id, tp) {
6133
6257
    // int clock_gettime(clockid_t clk_id, struct timespec *tp);
6134
 
    {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}}
6135
 
    {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}
 
6258
    var now = Date.now();
 
6259
    {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', 'Math.floor(now/1000)', 'i32') }}}; // seconds
 
6260
    {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}; // nanoseconds - not supported
6136
6261
    return 0;
6137
6262
  },
6138
6263
  clock_settime: function(clk_id, tp) {
6296
6421
#endif
6297
6422
  longjmp: function(env, value) {
6298
6423
#if ASM_JS
6299
 
    asm.setThrew(env, value || 1);
 
6424
    asm['setThrew'](env, value || 1);
6300
6425
    throw 'longjmp';
6301
6426
#else
6302
6427
    throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 };
6592
6717
  // ==========================================================================
6593
6718
 
6594
6719
  $ERRNO_CODES: {
 
6720
    EPERM: 1,
 
6721
    ENOENT: 2,
 
6722
    ESRCH: 3,
 
6723
    EINTR: 4,
 
6724
    EIO: 5,
 
6725
    ENXIO: 6,
6595
6726
    E2BIG: 7,
 
6727
    ENOEXEC: 8,
 
6728
    EBADF: 9,
 
6729
    ECHILD: 10,
 
6730
    EAGAIN: 11,
 
6731
    EWOULDBLOCK: 11,
 
6732
    ENOMEM: 12,
6596
6733
    EACCES: 13,
6597
 
    EADDRINUSE: 98,
6598
 
    EADDRNOTAVAIL: 99,
6599
 
    EAFNOSUPPORT: 97,
6600
 
    EAGAIN: 11,
6601
 
    EALREADY: 114,
6602
 
    EBADF: 9,
6603
 
    EBADMSG: 74,
 
6734
    EFAULT: 14,
 
6735
    ENOTBLK: 15,
6604
6736
    EBUSY: 16,
6605
 
    ECANCELED: 125,
6606
 
    ECHILD: 10,
6607
 
    ECONNABORTED: 103,
6608
 
    ECONNREFUSED: 111,
6609
 
    ECONNRESET: 104,
6610
 
    EDEADLK: 35,
6611
 
    EDESTADDRREQ: 89,
6612
 
    EDOM: 33,
6613
 
    EDQUOT: 122,
6614
6737
    EEXIST: 17,
6615
 
    EFAULT: 14,
6616
 
    EFBIG: 27,
6617
 
    EHOSTUNREACH: 113,
6618
 
    EIDRM: 43,
6619
 
    EILSEQ: 84,
6620
 
    EINPROGRESS: 115,
6621
 
    EINTR: 4,
 
6738
    EXDEV: 18,
 
6739
    ENODEV: 19,
 
6740
    ENOTDIR: 20,
 
6741
    EISDIR: 21,
6622
6742
    EINVAL: 22,
6623
 
    EIO: 5,
6624
 
    EISCONN: 106,
6625
 
    EISDIR: 21,
6626
 
    ELOOP: 40,
 
6743
    ENFILE: 23,
6627
6744
    EMFILE: 24,
 
6745
    ENOTTY: 25,
 
6746
    ETXTBSY: 26,
 
6747
    EFBIG: 27,
 
6748
    ENOSPC: 28,
 
6749
    ESPIPE: 29,
 
6750
    EROFS: 30,
6628
6751
    EMLINK: 31,
6629
 
    EMSGSIZE: 90,
6630
 
    EMULTIHOP: 72,
6631
 
    ENAMETOOLONG: 36,
6632
 
    ENETDOWN: 100,
6633
 
    ENETRESET: 102,
6634
 
    ENETUNREACH: 101,
6635
 
    ENFILE: 23,
6636
 
    ENOBUFS: 105,
 
6752
    EPIPE: 32,
 
6753
    EDOM: 33,
 
6754
    ERANGE: 34,
 
6755
    ENOMSG: 35,
 
6756
    EIDRM: 36,
 
6757
    ECHRNG: 37,
 
6758
    EL2NSYNC: 38,
 
6759
    EL3HLT: 39,
 
6760
    EL3RST: 40,
 
6761
    ELNRNG: 41,
 
6762
    EUNATCH: 42,
 
6763
    ENOCSI: 43,
 
6764
    EL2HLT: 44,
 
6765
    EDEADLK: 45,
 
6766
    ENOLCK: 46,
 
6767
    EBADE: 50,
 
6768
    EBADR: 51,
 
6769
    EXFULL: 52,
 
6770
    ENOANO: 53,
 
6771
    EBADRQC: 54,
 
6772
    EBADSLT: 55,
 
6773
    EDEADLOCK: 56,
 
6774
    EBFONT: 57,
 
6775
    ENOSTR: 60,
6637
6776
    ENODATA: 61,
6638
 
    ENODEV: 19,
6639
 
    ENOENT: 2,
6640
 
    ENOEXEC: 8,
6641
 
    ENOLCK: 37,
 
6777
    ETIME: 62,
 
6778
    ENOSR: 63,
 
6779
    ENONET: 64,
 
6780
    ENOPKG: 65,
 
6781
    EREMOTE: 66,
6642
6782
    ENOLINK: 67,
6643
 
    ENOMEM: 12,
6644
 
    ENOMSG: 42,
6645
 
    ENOPROTOOPT: 92,
6646
 
    ENOSPC: 28,
6647
 
    ENOSR: 63,
6648
 
    ENOSTR: 60,
6649
 
    ENOSYS: 38,
6650
 
    ENOTCONN: 107,
6651
 
    ENOTDIR: 20,
6652
 
    ENOTEMPTY: 39,
6653
 
    ENOTRECOVERABLE: 131,
6654
 
    ENOTSOCK: 88,
6655
 
    ENOTSUP: 95,
6656
 
    ENOTTY: 25,
6657
 
    ENXIO: 6,
6658
 
    EOPNOTSUPP: 45,
6659
 
    EOVERFLOW: 75,
6660
 
    EOWNERDEAD: 130,
6661
 
    EPERM: 1,
6662
 
    EPIPE: 32,
 
6783
    EADV: 68,
 
6784
    ESRMNT: 69,
 
6785
    ECOMM: 70,
6663
6786
    EPROTO: 71,
6664
 
    EPROTONOSUPPORT: 93,
6665
 
    EPROTOTYPE: 91,
6666
 
    ERANGE: 34,
6667
 
    EROFS: 30,
6668
 
    ESPIPE: 29,
6669
 
    ESRCH: 3,
6670
 
    ESTALE: 116,
6671
 
    ETIME: 62,
6672
 
    ETIMEDOUT: 110,
6673
 
    ETXTBSY: 26,
6674
 
    EWOULDBLOCK: 11,
6675
 
    EXDEV: 18,
 
6787
    EMULTIHOP: 74,
 
6788
    ELBIN: 75,
 
6789
    EDOTDOT: 76,
 
6790
    EBADMSG: 77,
 
6791
    EFTYPE: 79,
 
6792
    ENOTUNIQ: 80,
 
6793
    EBADFD: 81,
 
6794
    EREMCHG: 82,
 
6795
    ELIBACC: 83,
 
6796
    ELIBBAD: 84,
 
6797
    ELIBSCN: 85,
 
6798
    ELIBMAX: 86,
 
6799
    ELIBEXEC: 87,
 
6800
    ENOSYS: 88,
 
6801
    ENMFILE: 89,
 
6802
    ENOTEMPTY: 90,
 
6803
    ENAMETOOLONG: 91,
 
6804
    ELOOP: 92,
 
6805
    EOPNOTSUPP: 95,
 
6806
    EPFNOSUPPORT: 96,
 
6807
    ECONNRESET: 104,
 
6808
    ENOBUFS: 105,
 
6809
    EAFNOSUPPORT: 106,
 
6810
    EPROTOTYPE: 107,
 
6811
    ENOTSOCK: 108,
 
6812
    ENOPROTOOPT: 109,
 
6813
    ESHUTDOWN: 110,
 
6814
    ECONNREFUSED: 111,
 
6815
    EADDRINUSE: 112,
 
6816
    ECONNABORTED: 113,
 
6817
    ENETUNREACH: 114,
 
6818
    ENETDOWN: 115,
 
6819
    ETIMEDOUT: 116,
 
6820
    EHOSTDOWN: 117,
 
6821
    EHOSTUNREACH: 118,
 
6822
    EINPROGRESS: 119,
 
6823
    EALREADY: 120,
 
6824
    EDESTADDRREQ: 121,
 
6825
    EMSGSIZE: 122,
 
6826
    EPROTONOSUPPORT: 123,
 
6827
    ESOCKTNOSUPPORT: 124,
 
6828
    EADDRNOTAVAIL: 125,
 
6829
    ENETRESET: 126,
 
6830
    EISCONN: 127,
 
6831
    ENOTCONN: 128,
 
6832
    ETOOMANYREFS: 129,
 
6833
    EPROCLIM: 130,
 
6834
    EUSERS: 131,
 
6835
    EDQUOT: 132,
 
6836
    ESTALE: 133,
 
6837
    ENOTSUP: 134,
 
6838
    ENOMEDIUM: 135,
 
6839
    ENOSHARE: 136,
 
6840
    ECASECLASH: 137,
 
6841
    EILSEQ: 138,
 
6842
    EOVERFLOW: 139,
 
6843
    ECANCELED: 140,
 
6844
    ENOTRECOVERABLE: 141,
 
6845
    EOWNERDEAD: 142,
 
6846
    ESTRPIPE: 143
6676
6847
  },
6677
6848
  $ERRNO_MESSAGES: {
 
6849
    0: 'Success',
 
6850
    1: 'Not super-user',
6678
6851
    2: 'No such file or directory',
 
6852
    3: 'No such process',
 
6853
    4: 'Interrupted system call',
 
6854
    5: 'I/O error',
 
6855
    6: 'No such device or address',
 
6856
    7: 'Arg list too long',
 
6857
    8: 'Exec format error',
 
6858
    9: 'Bad file number',
 
6859
    10: 'No children',
 
6860
    11: 'No more processes',
 
6861
    12: 'Not enough core',
6679
6862
    13: 'Permission denied',
6680
 
    98: 'Address already in use',
6681
 
    99: 'Cannot assign requested address',
6682
 
    97: 'Address family not supported by protocol',
6683
 
    11: 'Resource temporarily unavailable',
6684
 
    114: 'Operation already in progress',
6685
 
    9: 'Bad file descriptor',
6686
 
    74: 'Bad message',
6687
 
    16: 'Device or resource busy',
6688
 
    125: 'Operation canceled',
6689
 
    10: 'No child processes',
6690
 
    103: 'Software caused connection abort',
6691
 
    111: 'Connection refused',
6692
 
    104: 'Connection reset by peer',
6693
 
    35: 'Resource deadlock avoided',
6694
 
    89: 'Destination address required',
6695
 
    33: 'Numerical argument out of domain',
6696
 
    122: 'Disk quota exceeded',
 
6863
    14: 'Bad address',
 
6864
    15: 'Block device required',
 
6865
    16: 'Mount device busy',
6697
6866
    17: 'File exists',
6698
 
    14: 'Bad address',
6699
 
    27: 'File too large',
6700
 
    113: 'No route to host',
6701
 
    43: 'Identifier removed',
6702
 
    84: 'Invalid or incomplete multibyte or wide character',
6703
 
    115: 'Operation now in progress',
6704
 
    4: 'Interrupted system call',
 
6867
    18: 'Cross-device link',
 
6868
    19: 'No such device',
 
6869
    20: 'Not a directory',
 
6870
    21: 'Is a directory',
6705
6871
    22: 'Invalid argument',
6706
 
    5: 'Input/output error',
6707
 
    106: 'Transport endpoint is already connected',
6708
 
    21: 'Is a directory',
6709
 
    40: 'Too many levels of symbolic links',
 
6872
    23: 'Too many open files in system',
6710
6873
    24: 'Too many open files',
 
6874
    25: 'Not a typewriter',
 
6875
    26: 'Text file busy',
 
6876
    27: 'File too large',
 
6877
    28: 'No space left on device',
 
6878
    29: 'Illegal seek',
 
6879
    30: 'Read only file system',
6711
6880
    31: 'Too many links',
6712
 
    90: 'Message too long',
6713
 
    72: 'Multihop attempted',
6714
 
    36: 'File name too long',
6715
 
    100: 'Network is down',
6716
 
    102: 'Network dropped connection on reset',
6717
 
    101: 'Network is unreachable',
6718
 
    23: 'Too many open files in system',
6719
 
    105: 'No buffer space available',
6720
 
    61: 'No data available',
6721
 
    19: 'No such device',
6722
 
    8: 'Exec format error',
6723
 
    37: 'No locks available',
6724
 
    67: 'Link has been severed',
6725
 
    12: 'Cannot allocate memory',
6726
 
    42: 'No message of desired type',
6727
 
    92: 'Protocol not available',
6728
 
    28: 'No space left on device',
 
6881
    32: 'Broken pipe',
 
6882
    33: 'Math arg out of domain of func',
 
6883
    34: 'Math result not representable',
 
6884
    35: 'No message of desired type',
 
6885
    36: 'Identifier removed',
 
6886
    37: 'Channel number out of range',
 
6887
    38: 'Level 2 not synchronized',
 
6888
    39: 'Level 3 halted',
 
6889
    40: 'Level 3 reset',
 
6890
    41: 'Link number out of range',
 
6891
    42: 'Protocol driver not attached',
 
6892
    43: 'No CSI structure available',
 
6893
    44: 'Level 2 halted',
 
6894
    45: 'Deadlock condition',
 
6895
    46: 'No record locks available',
 
6896
    50: 'Invalid exchange',
 
6897
    51: 'Invalid request descriptor',
 
6898
    52: 'Exchange full',
 
6899
    53: 'No anode',
 
6900
    54: 'Invalid request code',
 
6901
    55: 'Invalid slot',
 
6902
    56: 'File locking deadlock error',
 
6903
    57: 'Bad font file fmt',
 
6904
    60: 'Device not a stream',
 
6905
    61: 'No data (for no delay io)',
 
6906
    62: 'Timer expired',
6729
6907
    63: 'Out of streams resources',
6730
 
    60: 'Device not a stream',
6731
 
    38: 'Function not implemented',
6732
 
    107: 'Transport endpoint is not connected',
6733
 
    20: 'Not a directory',
6734
 
    39: 'Directory not empty',
6735
 
    131: 'State not recoverable',
6736
 
    88: 'Socket operation on non-socket',
6737
 
    95: 'Operation not supported',
6738
 
    25: 'Inappropriate ioctl for device',
6739
 
    6: 'No such device or address',
6740
 
    45: 'Op not supported on transport endpoint',
6741
 
    75: 'Value too large for defined data type',
6742
 
    130: 'Owner died',
6743
 
    1: 'Operation not permitted',
6744
 
    32: 'Broken pipe',
 
6908
    64: 'Machine is not on the network',
 
6909
    65: 'Package not installed',
 
6910
    66: 'The object is remote',
 
6911
    67: 'The link has been severed',
 
6912
    68: 'Advertise error',
 
6913
    69: 'Srmount error',
 
6914
    70: 'Communication error on send',
6745
6915
    71: 'Protocol error',
6746
 
    93: 'Protocol not supported',
6747
 
    91: 'Protocol wrong type for socket',
6748
 
    34: 'Numerical result out of range',
6749
 
    30: 'Read-only file system',
6750
 
    29: 'Illegal seek',
6751
 
    3: 'No such process',
6752
 
    116: 'Stale NFS file handle',
6753
 
    62: 'Timer expired',
6754
 
    110: 'Connection timed out',
6755
 
    26: 'Text file busy',
6756
 
    18: 'Invalid cross-device link'
 
6916
    74: 'Multihop attempted',
 
6917
    75: 'Inode is remote (not really error)',
 
6918
    76: 'Cross mount point (not really error)',
 
6919
    77: 'Trying to read unreadable message',
 
6920
    79: 'Inappropriate file type or format',
 
6921
    80: 'Given log. name not unique',
 
6922
    81: 'f.d. invalid for this operation',
 
6923
    82: 'Remote address changed',
 
6924
    83: 'Can\t access a needed shared lib',
 
6925
    84: 'Accessing a corrupted shared lib',
 
6926
    85: '.lib section in a.out corrupted',
 
6927
    86: 'Attempting to link in too many libs',
 
6928
    87: 'Attempting to exec a shared library',
 
6929
    88: 'Function not implemented',
 
6930
    89: 'No more files',
 
6931
    90: 'Directory not empty',
 
6932
    91: 'File or path name too long',
 
6933
    92: 'Too many symbolic links',
 
6934
    95: 'Operation not supported on transport endpoint',
 
6935
    96: 'Protocol family not supported',
 
6936
    104: 'Connection reset by peer',
 
6937
    105: 'No buffer space available',
 
6938
    106: 'Address family not supported by protocol family',
 
6939
    107: 'Protocol wrong type for socket',
 
6940
    108: 'Socket operation on non-socket',
 
6941
    109: 'Protocol not available',
 
6942
    110: 'Can\'t send after socket shutdown',
 
6943
    111: 'Connection refused',
 
6944
    112: 'Address already in use',
 
6945
    113: 'Connection aborted',
 
6946
    114: 'Network is unreachable',
 
6947
    115: 'Network interface is not configured',
 
6948
    116: 'Connection timed out',
 
6949
    117: 'Host is down',
 
6950
    118: 'Host is unreachable',
 
6951
    119: 'Connection already in progress',
 
6952
    120: 'Socket already connected',
 
6953
    121: 'Destination address required',
 
6954
    122: 'Message too long',
 
6955
    123: 'Unknown protocol',
 
6956
    124: 'Socket type not supported',
 
6957
    125: 'Address not available',
 
6958
    126: 'ENETRESET',
 
6959
    127: 'Socket is already connected',
 
6960
    128: 'Socket is not connected',
 
6961
    129: 'TOOMANYREFS',
 
6962
    130: 'EPROCLIM',
 
6963
    131: 'EUSERS',
 
6964
    132: 'EDQUOT',
 
6965
    133: 'ESTALE',
 
6966
    134: 'Not supported',
 
6967
    135: 'No medium (in tape drive)',
 
6968
    136: 'No such host or network path',
 
6969
    137: 'Filename exists with different case',
 
6970
    138: 'EILSEQ',
 
6971
    139: 'Value too large for defined data type',
 
6972
    140: 'Operation canceled',
 
6973
    141: 'State not recoverable',
 
6974
    142: 'Previous owner died',
 
6975
    143: 'Streams pipe error',
6757
6976
  },
6758
 
  __setErrNo__postset: '___setErrNo(0);',
 
6977
  __errno_state: 0,
 
6978
  __setErrNo__deps: ['__errno_state'],
 
6979
  __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4); {{{ makeSetValue("___errno_state", 0, 0, "i32") }}};',
6759
6980
  __setErrNo: function(value) {
6760
6981
    // For convenient setting and returning of errno.
6761
 
    if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_STATIC);
6762
 
    {{{ makeSetValue('___setErrNo.ret', '0', 'value', 'i32') }}}
 
6982
    {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}}
6763
6983
    return value;
6764
6984
  },
6765
6985
  __errno_location__deps: ['__setErrNo'],
6766
6986
  __errno_location: function() {
6767
 
    return ___setErrNo.ret;
 
6987
    return ___errno_state;
6768
6988
  },
6769
6989
  __errno: '__errno_location',
6770
6990
 
6876
7096
       void **restrict stackaddr, size_t *restrict stacksize); */
6877
7097
    /*FIXME: assumes that there is only one thread, and that attr is the
6878
7098
      current thread*/
6879
 
    {{{ makeSetValue('stackaddr', '0', 'STACK_ROOT', 'i8*') }}}
 
7099
    {{{ makeSetValue('stackaddr', '0', 'STACK_BASE', 'i8*') }}}
6880
7100
    {{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}}
6881
7101
    return 0;
6882
7102
  },
7013
7233
    ['i32', 'h_length'],
7014
7234
    ['i8**', 'h_addr_list'],
7015
7235
  ]),
 
7236
 
7016
7237
  gethostbyname__deps: ['__hostent_struct_layout'],
7017
7238
  gethostbyname: function(name) {
7018
7239
    name = Pointer_stringify(name);
7055
7276
  // sockets. Note that the implementation assumes all sockets are always
7056
7277
  // nonblocking
7057
7278
  // ==========================================================================
7058
 
 
 
7279
#if SOCKET_WEBRTC
 
7280
  $Sockets__deps: ['__setErrNo', '$ERRNO_CODES',
 
7281
    function() { return 'var SocketIO = ' + read('socket.io.js') + ';\n' },
 
7282
    function() { return 'var Peer = ' + read('wrtcp.js') + ';\n' }],
 
7283
#else
7059
7284
  $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'],
 
7285
#endif
7060
7286
  $Sockets: {
7061
 
    BACKEND_WEBSOCKETS: 0,
7062
 
    BACKEND_WEBRTC: 1,
7063
7287
    BUFFER_SIZE: 10*1024, // initial size
7064
7288
    MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer
7065
7289
 
7066
 
    backend: 0, // default to websockets
7067
7290
    nextFd: 1,
7068
7291
    fds: {},
 
7292
    nextport: 1,
 
7293
    maxport: 65535,
 
7294
    peer: null,
 
7295
    connections: {},
 
7296
    portmap: {},
 
7297
    localAddr: 0xfe00000a, // Local address is always 10.0.0.254
 
7298
    addrPool: [            0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a,
 
7299
               0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a,
 
7300
               0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a], /* 0x0100000a is reserved */
7069
7301
    sockaddr_in_layout: Runtime.generateStructInfo([
7070
7302
      ['i32', 'sin_family'],
7071
7303
      ['i16', 'sin_port'],
7082
7314
      ['i32', 'msg_controllen'],
7083
7315
      ['i32', 'msg_flags'],
7084
7316
    ]),
7085
 
 
7086
 
    backends: {
7087
 
      0: { // websockets
7088
 
        connect: function(info) {
7089
 
          console.log('opening ws://' + info.host + ':' + info.port);
7090
 
          info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']);
7091
 
          info.socket.binaryType = 'arraybuffer';
7092
 
 
7093
 
          var i32Temp = new Uint32Array(1);
7094
 
          var i8Temp = new Uint8Array(i32Temp.buffer);
7095
 
 
7096
 
          info.inQueue = [];
7097
 
          info.hasData = function() { return info.inQueue.length > 0 }
7098
 
          if (!info.stream) {
7099
 
            var partialBuffer = null; // in datagram mode, inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message
7100
 
          }
7101
 
 
7102
 
          info.socket.onmessage = function(event) {
7103
 
            assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data!
7104
 
            var data = new Uint8Array(event.data); // make a typed array view on the array buffer
7105
 
#if SOCKET_DEBUG
7106
 
            Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]);
7107
 
#endif
7108
 
            if (info.stream) {
7109
 
              info.inQueue.push(data);
7110
 
            } else {
7111
 
              // we added headers with message sizes, read those to find discrete messages
7112
 
              if (partialBuffer) {
7113
 
                // append to the partial buffer
7114
 
                var newBuffer = new Uint8Array(partialBuffer.length + data.length);
7115
 
                newBuffer.set(partialBuffer);
7116
 
                newBuffer.set(data, partialBuffer.length);
7117
 
                // forget the partial buffer and work on data
7118
 
                data = newBuffer;
7119
 
                partialBuffer = null;
7120
 
              }
7121
 
              var currPos = 0;
7122
 
              while (currPos+4 < data.length) {
7123
 
                i8Temp.set(data.subarray(currPos, currPos+4));
7124
 
                var currLen = i32Temp[0];
7125
 
                assert(currLen > 0);
7126
 
                if (currPos + 4 + currLen > data.length) {
7127
 
                  break; // not enough data has arrived
7128
 
                }
7129
 
                currPos += 4;
7130
 
#if SOCKET_DEBUG
7131
 
                Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]);
7132
 
#endif
7133
 
                info.inQueue.push(data.subarray(currPos, currPos+currLen));
7134
 
                currPos += currLen;
7135
 
              }
7136
 
              // If data remains, buffer it
7137
 
              if (currPos < data.length) {
7138
 
                partialBuffer = data.subarray(currPos);
7139
 
              }
7140
 
            }
7141
 
          }
7142
 
          function send(data) {
7143
 
            // TODO: if browser accepts views, can optimize this
7144
 
#if SOCKET_DEBUG
7145
 
            Module.print('sender actually sending ' + Array.prototype.slice.call(data));
7146
 
#endif
7147
 
            // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning
7148
 
            info.socket.send(data.buffer);
7149
 
          }
7150
 
          var outQueue = [];
7151
 
          var intervalling = false, interval;
7152
 
          function trySend() {
7153
 
            if (info.socket.readyState != info.socket.OPEN) {
7154
 
              if (!intervalling) {
7155
 
                intervalling = true;
7156
 
                console.log('waiting for socket in order to send');
7157
 
                interval = setInterval(trySend, 100);
7158
 
              }
7159
 
              return;
7160
 
            }
7161
 
            for (var i = 0; i < outQueue.length; i++) {
7162
 
              send(outQueue[i]);
7163
 
            }
7164
 
            outQueue.length = 0;
7165
 
            if (intervalling) {
7166
 
              intervalling = false;
7167
 
              clearInterval(interval);
7168
 
            }
7169
 
          }
7170
 
          info.sender = function(data) {
7171
 
            if (!info.stream) {
7172
 
              // add a header with the message size
7173
 
              var header = new Uint8Array(4);
7174
 
              i32Temp[0] = data.length;
7175
 
              header.set(i8Temp);
7176
 
              outQueue.push(header);
7177
 
            }
7178
 
            outQueue.push(new Uint8Array(data));
7179
 
            trySend();
7180
 
          };
7181
 
        }
7182
 
      },
7183
 
      1: { // webrtc
7184
 
      }
7185
 
    }
7186
 
  },
7187
 
 
7188
 
  emscripten_set_network_backend__deps: ['$Sockets'],
7189
 
  emscripten_set_network_backend: function(backend) {
7190
 
    Sockets.backend = backend;
7191
 
  },
7192
 
 
 
7317
  },
 
7318
 
 
7319
#if SOCKET_WEBRTC
 
7320
  /* WebRTC sockets supports several options on the Module object.
 
7321
 
 
7322
     * Module['host']: true if this peer is hosting, false otherwise
 
7323
     * Module['webrtc']['broker']: hostname for the p2p broker that this peer should use
 
7324
     * Module['webrtc']['session']: p2p session for that this peer will join, or undefined if this peer is hosting
 
7325
     * Module['webrtc']['hostOptions']: options to pass into p2p library if this peer is hosting
 
7326
     * Module['webrtc']['onpeer']: function(peer, route), invoked when this peer is ready to connect
 
7327
     * Module['webrtc']['onconnect']: function(peer), invoked when a new peer connection is ready
 
7328
     * Module['webrtc']['ondisconnect']: function(peer), invoked when an existing connection is closed
 
7329
     * Module['webrtc']['onerror']: function(error), invoked when an error occurs
 
7330
   */
7193
7331
  socket__deps: ['$Sockets'],
7194
7332
  socket: function(family, type, protocol) {
7195
 
    var fd = Sockets.nextFd++;
 
7333
    var fd = FS.createFileHandle({
 
7334
      addr: null,
 
7335
      port: null,
 
7336
      inQueue: new CircularBuffer(INCOMING_QUEUE_LENGTH),
 
7337
      header: new Uint16Array(2),
 
7338
      bound: false,
 
7339
      socket: true
 
7340
    };
7196
7341
    assert(fd < 64); // select() assumes socket fd values are in 0..63
7197
7342
    var stream = type == {{{ cDefine('SOCK_STREAM') }}};
7198
7343
    if (protocol) {
7199
7344
      assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if stream, must be tcp
7200
7345
    }
7201
 
    if (Sockets.backend == Sockets.BACKEND_WEBRTC) {
7202
 
      assert(!stream); // If WebRTC, we can only support datagram, not stream
7203
 
    }
7204
 
    Sockets.fds[fd] = {
 
7346
 
 
7347
    // Open the peer connection if we don't have it already
 
7348
    if (null == Sockets.peer) {
 
7349
      var host = Module['host'];
 
7350
      var broker = Module['webrtc']['broker'];
 
7351
      var session = Module['webrtc']['session'];
 
7352
      var peer = new Peer(broker);
 
7353
      var listenOptions = Module['webrtc']['hostOptions'] || {};
 
7354
      peer.onconnection = function(connection) {
 
7355
        console.log('connected');
 
7356
        var addr;
 
7357
        /* If this peer is connecting to the host, assign 10.0.0.1 to the host so it can be
 
7358
           reached at a known address.
 
7359
         */
 
7360
        // Assign 10.0.0.1 to the host
 
7361
        if (session && session === connection['route']) {
 
7362
          addr = 0x0100000a; // 10.0.0.1
 
7363
        } else {
 
7364
          addr = Sockets.addrPool.shift();
 
7365
        }
 
7366
        connection['addr'] = addr;
 
7367
        Sockets.connections[addr] = connection;
 
7368
        connection.ondisconnect = function() {
 
7369
          console.log('disconnect');
 
7370
          // Don't return the host address (10.0.0.1) to the pool
 
7371
          if (!(session && session === Sockets.connections[addr]['route'])) {
 
7372
            Sockets.addrPool.push(addr);
 
7373
          }
 
7374
          delete Sockets.connections[addr];
 
7375
 
 
7376
          if (Module['webrtc']['ondisconnect'] && 'function' === typeof Module['webrtc']['ondisconnect']) {
 
7377
            Module['webrtc']['ondisconnect'](peer);
 
7378
          }
 
7379
        };
 
7380
        connection.onerror = function(error) {
 
7381
          if (Module['webrtc']['onerror'] && 'function' === typeof Module['webrtc']['onerror']) {
 
7382
            Module['webrtc']['onerror'](error);
 
7383
          }
 
7384
        };
 
7385
        connection.onmessage = function(label, message) {
 
7386
          if ('unreliable' === label) {
 
7387
            handleMessage(addr, message.data);
 
7388
          }
 
7389
        }
 
7390
 
 
7391
        if (Module['webrtc']['onconnect'] && 'function' === typeof Module['webrtc']['onconnect']) {
 
7392
          Module['webrtc']['onconnect'](peer);
 
7393
        }
 
7394
      };
 
7395
      peer.onpending = function(pending) {
 
7396
        console.log('pending from: ', pending['route'], '; initiated by: ', (pending['incoming']) ? 'remote' : 'local');
 
7397
      };
 
7398
      peer.onerror = function(error) {
 
7399
        console.error(error);
 
7400
      };
 
7401
      peer.onroute = function(route) {
 
7402
        if (Module['webrtc']['onpeer'] && 'function' === typeof Module['webrtc']['onpeer']) {
 
7403
          Module['webrtc']['onpeer'](peer, route);
 
7404
        }
 
7405
      };
 
7406
      function handleMessage(addr, message) {
 
7407
#if SOCKET_DEBUG
 
7408
        Module.print("received " + message.byteLength + " raw bytes");
 
7409
#endif
 
7410
        var header = new Uint16Array(message, 0, 2);
 
7411
        if (Sockets.portmap[header[1]]) {
 
7412
          Sockets.portmap[header[1]].inQueue.push([addr, message]);
 
7413
        } else {
 
7414
          console.log("unable to deliver message: ", addr, header[1], message);
 
7415
        }
 
7416
      }
 
7417
      window.onbeforeunload = function() {
 
7418
        var ids = Object.keys(Sockets.connections);
 
7419
        ids.forEach(function(id) {
 
7420
          Sockets.connections[id].close();
 
7421
        });
 
7422
      }
 
7423
      Sockets.peer = peer;
 
7424
    }
 
7425
 
 
7426
    var INCOMING_QUEUE_LENGTH = 64;
 
7427
 
 
7428
    function CircularBuffer(max_length) {
 
7429
      var buffer = new Array(++ max_length);
 
7430
      var head = 0;
 
7431
      var tail = 0;
 
7432
      var length = 0;
 
7433
 
 
7434
      return {
 
7435
        push: function(element) {
 
7436
          buffer[tail ++] = element;
 
7437
          length = Math.min(++ length, max_length - 1);
 
7438
          tail = tail % max_length;
 
7439
          if (tail === head) {
 
7440
            head = (head + 1) % max_length;
 
7441
          }
 
7442
        },
 
7443
        shift: function(element) {
 
7444
          if (length < 1) return undefined;
 
7445
 
 
7446
          var element = buffer[head];
 
7447
          -- length;
 
7448
          head = (head + 1) % max_length;
 
7449
          return element;
 
7450
        },
 
7451
        length: function() {
 
7452
          return length;
 
7453
        }
 
7454
      };
 
7455
    };
 
7456
 
 
7457
    return fd;
 
7458
  },
 
7459
 
 
7460
  mkport__deps: ['$Sockets'],
 
7461
  mkport: function() {
 
7462
    for(var i = 0; i < Sockets.maxport; ++ i) {
 
7463
      var port = Sockets.nextport ++;
 
7464
      Sockets.nextport = (Sockets.nextport > Sockets.maxport) ? 1 : Sockets.nextport;
 
7465
      if (!Sockets.portmap[port]) {
 
7466
        return port;
 
7467
      }
 
7468
    }
 
7469
    assert(false, 'all available ports are in use!');
 
7470
  },
 
7471
 
 
7472
  connect: function() {
 
7473
    // Stub: connection-oriented sockets are not supported yet.
 
7474
  },
 
7475
 
 
7476
  bind__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'mkport'],
 
7477
  bind: function(fd, addr, addrlen) {
 
7478
    var info = FS.streams[fd];
 
7479
    if (!info) return -1;
 
7480
    if (addr) {
 
7481
      info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16'));
 
7482
      // info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32');
 
7483
    }
 
7484
    if (!info.port) {
 
7485
      info.port = _mkport();
 
7486
    }
 
7487
    info.addr = Sockets.localAddr; // 10.0.0.254
 
7488
    info.host = __inet_ntop_raw(info.addr);
 
7489
    info.close = function() {
 
7490
      Sockets.portmap[info.port] = undefined;
 
7491
    }
 
7492
    Sockets.portmap[info.port] = info;
 
7493
    console.log("bind: ", info.host, info.port);
 
7494
    info.bound = true;
 
7495
  },
 
7496
 
 
7497
  sendmsg__deps: ['$Sockets', 'bind', '_inet_ntop_raw', 'ntohs'],
 
7498
  sendmsg: function(fd, msg, flags) {
 
7499
    var info = FS.streams[fd];
 
7500
    if (!info) return -1;
 
7501
    // if we are not connected, use the address info in the message
 
7502
    if (!info.bound) {
 
7503
      _bind(fd);
 
7504
    }
 
7505
 
 
7506
    var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
 
7507
    assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
 
7508
    var port = _ntohs(getValue(name + Sockets.sockaddr_in_layout.sin_port, 'i16'));
 
7509
    var addr = getValue(name + Sockets.sockaddr_in_layout.sin_addr, 'i32');
 
7510
    var connection = Sockets.connections[addr];
 
7511
    // var host = __inet_ntop_raw(addr);
 
7512
 
 
7513
    if (!(connection && connection.connected)) {
 
7514
      ___setErrNo(ERRNO_CODES.EWOULDBLOCK);
 
7515
      return -1;
 
7516
    }
 
7517
 
 
7518
    var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
 
7519
    var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
 
7520
#if SOCKET_DEBUG
 
7521
    Module.print('sendmsg vecs: ' + num);
 
7522
#endif
 
7523
    var totalSize = 0;
 
7524
    for (var i = 0; i < num; i++) {
 
7525
      totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
 
7526
    }
 
7527
    var data = new Uint8Array(totalSize);
 
7528
    var ret = 0;
 
7529
    for (var i = 0; i < num; i++) {
 
7530
      var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
 
7531
#if SOCKET_DEBUG
 
7532
    Module.print('sendmsg curr size: ' + currNum);
 
7533
#endif
 
7534
      if (!currNum) continue;
 
7535
      var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
 
7536
      data.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret);
 
7537
      ret += currNum;
 
7538
    }
 
7539
 
 
7540
    info.header[0] = info.port; // src port
 
7541
    info.header[1] = port; // dst port
 
7542
#if SOCKET_DEBUG
 
7543
    Module.print('sendmsg port: ' + info.header[0] + ' -> ' + info.header[1]);
 
7544
    Module.print('sendmsg bytes: ' + data.length + ' | ' + Array.prototype.slice.call(data));
 
7545
#endif
 
7546
    var buffer = new Uint8Array(info.header.byteLength + data.byteLength);
 
7547
    buffer.set(new Uint8Array(info.header.buffer));
 
7548
    buffer.set(data, info.header.byteLength);
 
7549
 
 
7550
    connection.send('unreliable', buffer.buffer);
 
7551
  },
 
7552
 
 
7553
  recvmsg__deps: ['$Sockets', 'bind', '__setErrNo', '$ERRNO_CODES', 'htons'],
 
7554
  recvmsg: function(fd, msg, flags) {
 
7555
    var info = FS.streams[fd];
 
7556
    if (!info) return -1;
 
7557
    // if we are not connected, use the address info in the message
 
7558
    if (!info.port) {
 
7559
      console.log('recvmsg on unbound socket');
 
7560
      assert(false, 'cannot receive on unbound socket');
 
7561
    }
 
7562
    if (info.inQueue.length() == 0) {
 
7563
      ___setErrNo(ERRNO_CODES.EWOULDBLOCK);
 
7564
      return -1;
 
7565
    }
 
7566
 
 
7567
    var entry = info.inQueue.shift();
 
7568
    var addr = entry[0];
 
7569
    var message = entry[1];
 
7570
    var header = new Uint16Array(message, 0, info.header.length);
 
7571
    var buffer = new Uint8Array(message, info.header.byteLength);
 
7572
 
 
7573
    var bytes = buffer.length;
 
7574
#if SOCKET_DEBUG
 
7575
    Module.print('recvmsg port: ' + header[1] + ' <- ' + header[0]);
 
7576
    Module.print('recvmsg bytes: ' + bytes + ' | ' + Array.prototype.slice.call(buffer));
 
7577
#endif
 
7578
    // write source
 
7579
    var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
 
7580
    {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'addr', 'i32') }}};
 
7581
    {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(header[0])', 'i16') }}};
 
7582
    // write data
 
7583
    var ret = bytes;
 
7584
    var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
 
7585
    var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
 
7586
    var bufferPos = 0;
 
7587
    for (var i = 0; i < num && bytes > 0; i++) {
 
7588
      var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
 
7589
#if SOCKET_DEBUG
 
7590
      Module.print('recvmsg loop ' + [i, num, bytes, currNum]);
 
7591
#endif
 
7592
      if (!currNum) continue;
 
7593
      currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..?
 
7594
      bytes -= currNum;
 
7595
      var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
 
7596
#if SOCKET_DEBUG
 
7597
      Module.print('recvmsg call recv ' + currNum);
 
7598
#endif
 
7599
      HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf);
 
7600
      bufferPos += currNum;
 
7601
    }
 
7602
    return ret;
 
7603
  },
 
7604
 
 
7605
  shutdown: function(fd, how) {
 
7606
    var info = FS.streams[fd];
 
7607
    if (!info) return -1;
 
7608
    info.close();
 
7609
    FS.removeFileHandle(fd);
 
7610
  },
 
7611
 
 
7612
  ioctl: function(fd, request, varargs) {
 
7613
    var info = FS.streams[fd];
 
7614
    if (!info) return -1;
 
7615
    var bytes = 0;
 
7616
    if (info.hasData()) {
 
7617
      bytes = info.inQueue[0].length;
 
7618
    }
 
7619
    var dest = {{{ makeGetValue('varargs', '0', 'i32') }}};
 
7620
    {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}};
 
7621
    return 0;
 
7622
  },
 
7623
 
 
7624
  setsockopt: function(d, level, optname, optval, optlen) {
 
7625
    console.log('ignoring setsockopt command');
 
7626
    return 0;
 
7627
  },
 
7628
 
 
7629
  accept: function(fd, addr, addrlen) {
 
7630
    // TODO: webrtc queued incoming connections, etc.
 
7631
    // For now, the model is that bind does a connect, and we "accept" that one connection,
 
7632
    // which has host:port the same as ours. We also return the same socket fd.
 
7633
    var info = FS.streams[fd];
 
7634
    if (!info) return -1;
 
7635
    if (addr) {
 
7636
      setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32');
 
7637
      setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32');
 
7638
      setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32');
 
7639
    }
 
7640
    return fd;
 
7641
  },
 
7642
 
 
7643
  select: function(nfds, readfds, writefds, exceptfds, timeout) {
 
7644
    // readfds are supported,
 
7645
    // writefds checks socket open status
 
7646
    // exceptfds not supported
 
7647
    // timeout is always 0 - fully async
 
7648
    assert(!exceptfds);
 
7649
 
 
7650
    var errorCondition = 0;
 
7651
 
 
7652
    function canRead(info) {
 
7653
      return info.inQueue.length() > 0;
 
7654
    }
 
7655
 
 
7656
    function canWrite(info) {
 
7657
      return true;
 
7658
    }
 
7659
 
 
7660
    function checkfds(nfds, fds, can) {
 
7661
      if (!fds) return 0;
 
7662
 
 
7663
      var bitsSet = 0;
 
7664
      var dstLow  = 0;
 
7665
      var dstHigh = 0;
 
7666
      var srcLow  = {{{ makeGetValue('fds', 0, 'i32') }}};
 
7667
      var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}};
 
7668
      nfds = Math.min(64, nfds); // fd sets have 64 bits
 
7669
 
 
7670
      for (var fd = 0; fd < nfds; fd++) {
 
7671
        var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh;
 
7672
        if (int & mask) {
 
7673
          // index is in the set, check if it is ready for read
 
7674
          var info = FS.streams[fd];
 
7675
          if (info && can(info)) {
 
7676
            // set bit
 
7677
            fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask);
 
7678
            bitsSet++;
 
7679
          }
 
7680
        }
 
7681
      }
 
7682
 
 
7683
      {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}};
 
7684
      {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}};
 
7685
      return bitsSet;
 
7686
    }
 
7687
 
 
7688
    var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite);
 
7689
    if (errorCondition) {
 
7690
      ___setErrNo(ERRNO_CODES.EBADF);
 
7691
      return -1;
 
7692
    } else {
 
7693
      return totalHandles;
 
7694
    }
 
7695
  },
 
7696
#else
 
7697
  socket__deps: ['$Sockets'],
 
7698
  socket: function(family, type, protocol) {
 
7699
    var stream = type == {{{ cDefine('SOCK_STREAM') }}};
 
7700
    if (protocol) {
 
7701
      assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp
 
7702
    }
 
7703
    var fd = FS.createFileHandle({ 
7205
7704
      connected: false,
7206
 
      stream: stream
7207
 
    };
 
7705
      stream: stream,
 
7706
      socket: true
 
7707
    });
 
7708
    assert(fd < 64); // select() assumes socket fd values are in 0..63
7208
7709
    return fd;
7209
7710
  },
7210
7711
 
7211
 
  connect__deps: ['$Sockets', '_inet_ntop_raw', 'htons', 'gethostbyname'],
 
7712
  connect__deps: ['$FS', '$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'],
7212
7713
  connect: function(fd, addr, addrlen) {
7213
 
    var info = Sockets.fds[fd];
 
7714
    var info = FS.streams[fd];
7214
7715
    if (!info) return -1;
7215
7716
    info.connected = true;
7216
7717
    info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32');
7225
7726
      assert(info.host, 'problem translating fake ip ' + parts);
7226
7727
    }
7227
7728
    try {
7228
 
      Sockets.backends[Sockets.backend].connect(info);
 
7729
      console.log('opening ws://' + info.host + ':' + info.port);
 
7730
      info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']);
 
7731
      info.socket.binaryType = 'arraybuffer';
 
7732
 
 
7733
      var i32Temp = new Uint32Array(1);
 
7734
      var i8Temp = new Uint8Array(i32Temp.buffer);
 
7735
 
 
7736
      info.inQueue = [];
 
7737
      info.hasData = function() { return info.inQueue.length > 0 }
 
7738
      if (!info.stream) {
 
7739
        var partialBuffer = null; // in datagram mode, inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message
 
7740
      }
 
7741
 
 
7742
      info.socket.onmessage = function(event) {
 
7743
        assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data!
 
7744
        var data = new Uint8Array(event.data); // make a typed array view on the array buffer
 
7745
#if SOCKET_DEBUG
 
7746
        Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]);
 
7747
#endif
 
7748
        if (info.stream) {
 
7749
          info.inQueue.push(data);
 
7750
        } else {
 
7751
          // we added headers with message sizes, read those to find discrete messages
 
7752
          if (partialBuffer) {
 
7753
            // append to the partial buffer
 
7754
            var newBuffer = new Uint8Array(partialBuffer.length + data.length);
 
7755
            newBuffer.set(partialBuffer);
 
7756
            newBuffer.set(data, partialBuffer.length);
 
7757
            // forget the partial buffer and work on data
 
7758
            data = newBuffer;
 
7759
            partialBuffer = null;
 
7760
          }
 
7761
          var currPos = 0;
 
7762
          while (currPos+4 < data.length) {
 
7763
            i8Temp.set(data.subarray(currPos, currPos+4));
 
7764
            var currLen = i32Temp[0];
 
7765
            assert(currLen > 0);
 
7766
            if (currPos + 4 + currLen > data.length) {
 
7767
              break; // not enough data has arrived
 
7768
            }
 
7769
            currPos += 4;
 
7770
#if SOCKET_DEBUG
 
7771
            Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]);
 
7772
#endif
 
7773
            info.inQueue.push(data.subarray(currPos, currPos+currLen));
 
7774
            currPos += currLen;
 
7775
          }
 
7776
          // If data remains, buffer it
 
7777
          if (currPos < data.length) {
 
7778
            partialBuffer = data.subarray(currPos);
 
7779
          }
 
7780
        }
 
7781
      }
 
7782
      function send(data) {
 
7783
        // TODO: if browser accepts views, can optimize this
 
7784
#if SOCKET_DEBUG
 
7785
        Module.print('sender actually sending ' + Array.prototype.slice.call(data));
 
7786
#endif
 
7787
        // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning
 
7788
        info.socket.send(data.buffer);
 
7789
      }
 
7790
      var outQueue = [];
 
7791
      var intervalling = false, interval;
 
7792
      function trySend() {
 
7793
        if (info.socket.readyState != info.socket.OPEN) {
 
7794
          if (!intervalling) {
 
7795
            intervalling = true;
 
7796
            console.log('waiting for socket in order to send');
 
7797
            interval = setInterval(trySend, 100);
 
7798
          }
 
7799
          return;
 
7800
        }
 
7801
        for (var i = 0; i < outQueue.length; i++) {
 
7802
          send(outQueue[i]);
 
7803
        }
 
7804
        outQueue.length = 0;
 
7805
        if (intervalling) {
 
7806
          intervalling = false;
 
7807
          clearInterval(interval);
 
7808
        }
 
7809
      }
 
7810
      info.sender = function(data) {
 
7811
        if (!info.stream) {
 
7812
          // add a header with the message size
 
7813
          var header = new Uint8Array(4);
 
7814
          i32Temp[0] = data.length;
 
7815
          header.set(i8Temp);
 
7816
          outQueue.push(header);
 
7817
        }
 
7818
        outQueue.push(new Uint8Array(data));
 
7819
        trySend();
 
7820
      };
7229
7821
    } catch(e) {
7230
7822
      Module.printErr('Error in connect(): ' + e);
7231
7823
      ___setErrNo(ERRNO_CODES.EACCES);
7232
7824
      return -1;
7233
7825
    }
 
7826
 
7234
7827
    return 0;
7235
7828
  },
7236
7829
 
7237
 
  recv__deps: ['$Sockets'],
 
7830
  recv__deps: ['$FS'],
7238
7831
  recv: function(fd, buf, len, flags) {
7239
 
    var info = Sockets.fds[fd];
 
7832
    var info = FS.streams[fd];
7240
7833
    if (!info) return -1;
7241
7834
    if (!info.hasData()) {
7242
7835
      ___setErrNo(ERRNO_CODES.EAGAIN); // no data, and all sockets are nonblocking, so this is the right behavior
7260
7853
    return buffer.length;
7261
7854
  },
7262
7855
 
7263
 
  send__deps: ['$Sockets'],
 
7856
  send__deps: ['$FS'],
7264
7857
  send: function(fd, buf, len, flags) {
7265
 
    var info = Sockets.fds[fd];
 
7858
    var info = FS.streams[fd];
7266
7859
    if (!info) return -1;
7267
7860
    info.sender(HEAPU8.subarray(buf, buf+len));
7268
7861
    return len;
7269
7862
  },
7270
7863
 
7271
 
  sendmsg__deps: ['$Sockets', 'connect'],
 
7864
  sendmsg__deps: ['$FS', '$Sockets', 'connect'],
7272
7865
  sendmsg: function(fd, msg, flags) {
7273
 
    var info = Sockets.fds[fd];
 
7866
    var info = FS.streams[fd];
7274
7867
    if (!info) return -1;
7275
7868
    // if we are not connected, use the address info in the message
7276
7869
    if (!info.connected) {
7281
7874
    var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
7282
7875
    var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
7283
7876
#if SOCKET_DEBUG
7284
 
      Module.print('sendmsg vecs: ' + num);
 
7877
    Module.print('sendmsg vecs: ' + num);
7285
7878
#endif
7286
7879
    var totalSize = 0;
7287
7880
    for (var i = 0; i < num; i++) {
7303
7896
    return ret;
7304
7897
  },
7305
7898
 
7306
 
  recvmsg__deps: ['$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES', 'htons'],
 
7899
  recvmsg__deps: ['$FS', '$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES', 'htons'],
7307
7900
  recvmsg: function(fd, msg, flags) {
7308
 
    var info = Sockets.fds[fd];
 
7901
    var info = FS.streams[fd];
7309
7902
    if (!info) return -1;
7310
7903
    // if we are not connected, use the address info in the message
7311
7904
    if (!info.connected) {
7312
7905
#if SOCKET_DEBUG
7313
 
      Module.print('recvmsg connecting');
 
7906
    Module.print('recvmsg connecting');
7314
7907
#endif
7315
7908
      var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
7316
7909
      assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
7361
7954
    return ret;
7362
7955
  },
7363
7956
 
7364
 
  recvfrom__deps: ['$Sockets', 'connect', 'recv'],
 
7957
  recvfrom__deps: ['$FS', 'connect', 'recv'],
7365
7958
  recvfrom: function(fd, buf, len, flags, addr, addrlen) {
7366
 
    var info = Sockets.fds[fd];
 
7959
    var info = FS.streams[fd];
7367
7960
    if (!info) return -1;
7368
7961
    // if we are not connected, use the address info in the message
7369
7962
    if (!info.connected) {
7374
7967
  },
7375
7968
 
7376
7969
  shutdown: function(fd, how) {
7377
 
    var info = Sockets.fds[fd];
 
7970
    var info = FS.streams[fd];
7378
7971
    if (!info) return -1;
7379
7972
    info.socket.close();
7380
 
    Sockets.fds[fd] = null;
 
7973
    FS.removeFileHandle(fd);
7381
7974
  },
7382
7975
 
7383
7976
  ioctl: function(fd, request, varargs) {
7384
 
    var info = Sockets.fds[fd];
 
7977
    var info = FS.streams[fd];
7385
7978
    if (!info) return -1;
7386
7979
    var bytes = 0;
7387
7980
    if (info.hasData()) {
7406
7999
    return 0;
7407
8000
  },
7408
8001
 
 
8002
  accept__deps: ['$FS', '$Sockets'],
7409
8003
  accept: function(fd, addr, addrlen) {
7410
8004
    // TODO: webrtc queued incoming connections, etc.
7411
8005
    // For now, the model is that bind does a connect, and we "accept" that one connection,
7412
8006
    // which has host:port the same as ours. We also return the same socket fd.
7413
 
    var info = Sockets.fds[fd];
 
8007
    var info = FS.streams[fd];
7414
8008
    if (!info) return -1;
7415
8009
    if (addr) {
7416
8010
      setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32');
7420
8014
    return fd;
7421
8015
  },
7422
8016
 
 
8017
  select__deps: ['$FS'],
7423
8018
  select: function(nfds, readfds, writefds, exceptfds, timeout) {
7424
8019
    // readfds are supported,
7425
8020
    // writefds checks socket open status
7426
8021
    // exceptfds not supported
7427
8022
    // timeout is always 0 - fully async
7428
8023
    assert(!exceptfds);
7429
 
    
 
8024
 
7430
8025
    var errorCondition = 0;
7431
8026
 
7432
8027
    function canRead(info) {
7433
 
      // make sure hasData exists. 
7434
 
      // we do create it when the socket is connected, 
 
8028
      // make sure hasData exists.
 
8029
      // we do create it when the socket is connected,
7435
8030
      // but other implementations may create it lazily
7436
8031
      if ((info.socket.readyState == WebSocket.CLOSING || info.socket.readyState == WebSocket.CLOSED) && info.inQueue.length == 0) {
7437
8032
        errorCondition = -1;
7441
8036
    }
7442
8037
 
7443
8038
    function canWrite(info) {
7444
 
      // make sure socket exists. 
7445
 
      // we do create it when the socket is connected, 
 
8039
      // make sure socket exists.
 
8040
      // we do create it when the socket is connected,
7446
8041
      // but other implementations may create it lazily
7447
8042
      if ((info.socket.readyState == WebSocket.CLOSING || info.socket.readyState == WebSocket.CLOSED)) {
7448
8043
        errorCondition = -1;
7465
8060
        var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh;
7466
8061
        if (int & mask) {
7467
8062
          // index is in the set, check if it is ready for read
7468
 
          var info = Sockets.fds[fd];
 
8063
          var info = FS.streams[fd];
7469
8064
          if (info && can(info)) {
7470
8065
            // set bit
7471
8066
            fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask);
7487
8082
      return totalHandles;
7488
8083
    }
7489
8084
  },
 
8085
#endif
7490
8086
 
7491
8087
  socketpair__deps: ['__setErrNo', '$ERRNO_CODES'],
7492
8088
  socketpair: function(domain, type, protocol, sv) {
7521
8117
  },
7522
8118
 
7523
8119
  emscripten_run_script_int: function(ptr) {
7524
 
    return eval(Pointer_stringify(ptr));
 
8120
    return eval(Pointer_stringify(ptr))|0;
7525
8121
  },
7526
8122
 
7527
8123
  emscripten_run_script_string: function(ptr) {