jsish

Check-in [5106a19352]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Release "2.7.2". Changes to support embedding in fossil. Stop using min version of markdeep. Move to new documentation. Disallow loading .so from noexec mount. Cleanup option parsing. Support for unix exec with no shell (for jails).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:5106a19352172746dc316645a16057c22ac1c379
User & Date: pmacdona 2019-01-24 23:58:13
Context
2019-02-20
22:53
Fix issue [04a7eee212]: String.slice(0) off by one in the length, and negatives seem wrong. check-in: aa80fab311 user: pmacdona tags: trunk
2019-01-24
23:58
Release "2.7.2". Changes to support embedding in fossil. Stop using min version of markdeep. Move to new documentation. Disallow loading .so from noexec mount. Cleanup option parsing. Support for unix exec with no shell (for jails). check-in: 5106a19352 user: pmacdona tags: trunk
2018-12-02
03:12
Cleanups check-in: e68e7c1ad1 user: pmacdona tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Makefile.

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
...
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
WEBSOCKDIR = websocket
WEBSOCKSRC = $(WEBSOCKDIR)/src
SQLITEDIR = sqlite
ACFILES	= src/parser.c
#ACFILES	= src/jsiParser.c
BUILDSYS = $(shell uname -o)

CFLAGS += -I. -Isrc -Wall -Wsign-compare -Wtype-limits -Wuninitialized
# -pg
#CFLAGS += -g -O3
CFLAGS += -g -Og -O0
#CFLAGS += -g -Og -g3
SLIBCFLAGS = -Wl,--export-dynamic -shared -DJSI_USE_STUBS=1

MAKEFILE=Makefile
................................................................................
	rm -f $@
	cp $(PROGBINA) $@
ifneq ($(wildcard .fslckout),) 
	fossil info | grep ^checkout | cut -b15- > lib/sourceid.txt
endif
	$(PROGBINMIN) lib/Jsi_Zip.jsi create $@ $(ZIPDIR) lib
endif
	@echo "Congratulations, build is now complete. Test with './jsish -u tests'"

apps: ledger.zip
#apps: sqliteui$(EXEEXT)

ledger.zip: .FORCE
	rm $@
	(cd ../Ledger && zip -r  ../jsi/$@ .)
................................................................................
	echo "#endif //JSI__READLINE==1" >> $@
	echo "struct jsi_Pstate;" >> $@
	cat $(WIFILES) $(REFILES) $(HFILES) src/jsiCode.c $(PCFILES) | grep -v '^#line' >> $@
	echo "#ifndef JSI_LITE_ONLY" >> $@
	grep -v '^#line' $(ACFILES)  >> $@
	echo "#endif //JSI_LITE_ONLY" >> $@
	cat $(WFILES) $(EFILES)  >> $@
	echo "#if JSI__MAIN==1" >> $@
	cat src/main.c  >> $@
	echo "#endif //JSI__MAIN==1" >> $@
	echo "#endif //JSI_IN_AMALGAMATION" >> $@

# Create the single compile file jsiOne.c
jsiOne.c: src/jsi.h  $(REFILES) $(HFILES) $(CFILES) $(MAKEFILE) $(MAKECONF)
	echo '#include "src/jsi.h"' > $@
	echo "#define JSI_AMALGAMATION" >> $@
	echo "#if JSI__MINIZ==1" >> $@
	echo '#include "'$(MINIZDIR)/miniz.c'"' >> $@
	echo "#endif" >> $@
	echo "#if JSI__READLINE==1" >> $@
	echo '#include "'src/linenoise.c'"' >> $@
	echo "#endif" >> $@
	echo "struct jsi_Pstate;" >> $@
	for ii in  $(REFILES) $(HFILES) src/jsiCode.c $(PCFILES); do echo '#include "'$$ii'"' >> $@; done
	echo "#ifndef JSI_LITE_ONLY" >> $@
	for ii in $(ACFILES); do echo '#include "'$$ii'"' >> $@; done
	echo "#endif" >> $@
	for ii in $(WFILES) $(EFILES); do echo '#include "'$$ii'"' >> $@; done
	echo "#if JSI__MAIN==1" >> $@
	echo '#include "src/main.c"'  >> $@
	echo "#endif" >> $@

stubs:
	(cd src && ../$(PROGBIN) ../tools/mkstubs.jsi)

ref:
	./$(PROGBIN) tools/mkproto.jsi > tools/protos.jsi
	$(MAKE) -C www







|







 







|







 







<

<








|


|




|

<

<







3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
...
364
365
366
367
368
369
370

371

372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389

390

391
392
393
394
395
396
397
WEBSOCKDIR = websocket
WEBSOCKSRC = $(WEBSOCKDIR)/src
SQLITEDIR = sqlite
ACFILES	= src/parser.c
#ACFILES	= src/jsiParser.c
BUILDSYS = $(shell uname -o)

CFLAGS += -I. -Isrc -Wall -Wsign-compare -Wtype-limits -Wuninitialized -DJSI__MAIN=1
# -pg
#CFLAGS += -g -O3
CFLAGS += -g -Og -O0
#CFLAGS += -g -Og -g3
SLIBCFLAGS = -Wl,--export-dynamic -shared -DJSI_USE_STUBS=1

MAKEFILE=Makefile
................................................................................
	rm -f $@
	cp $(PROGBINA) $@
ifneq ($(wildcard .fslckout),) 
	fossil info | grep ^checkout | cut -b15- > lib/sourceid.txt
endif
	$(PROGBINMIN) lib/Jsi_Zip.jsi create $@ $(ZIPDIR) lib
endif
	@echo "Finished '$(TARGET)' build. Test with './jsish -u tests'"

apps: ledger.zip
#apps: sqliteui$(EXEEXT)

ledger.zip: .FORCE
	rm $@
	(cd ../Ledger && zip -r  ../jsi/$@ .)
................................................................................
	echo "#endif //JSI__READLINE==1" >> $@
	echo "struct jsi_Pstate;" >> $@
	cat $(WIFILES) $(REFILES) $(HFILES) src/jsiCode.c $(PCFILES) | grep -v '^#line' >> $@
	echo "#ifndef JSI_LITE_ONLY" >> $@
	grep -v '^#line' $(ACFILES)  >> $@
	echo "#endif //JSI_LITE_ONLY" >> $@
	cat $(WFILES) $(EFILES)  >> $@

	cat src/main.c  >> $@

	echo "#endif //JSI_IN_AMALGAMATION" >> $@

# Create the single compile file jsiOne.c
jsiOne.c: src/jsi.h  $(REFILES) $(HFILES) $(CFILES) $(MAKEFILE) $(MAKECONF)
	echo '#include "src/jsi.h"' > $@
	echo "#define JSI_AMALGAMATION" >> $@
	echo "#if JSI__MINIZ==1" >> $@
	echo '#include "'$(MINIZDIR)/miniz.c'"' >> $@
	echo "#endif //JSI__MINIZ==1" >> $@
	echo "#if JSI__READLINE==1" >> $@
	echo '#include "'src/linenoise.c'"' >> $@
	echo "#endif //JSI__READLINE==1" >> $@
	echo "struct jsi_Pstate;" >> $@
	for ii in  $(REFILES) $(HFILES) src/jsiCode.c $(PCFILES); do echo '#include "'$$ii'"' >> $@; done
	echo "#ifndef JSI_LITE_ONLY" >> $@
	for ii in $(ACFILES); do echo '#include "'$$ii'"' >> $@; done
	echo "#endif //JSI_LITE_ONLY" >> $@
	for ii in $(WFILES) $(EFILES); do echo '#include "'$$ii'"' >> $@; done

	echo '#include "src/main.c"'  >> $@


stubs:
	(cd src && ../$(PROGBIN) ../tools/mkstubs.jsi)

ref:
	./$(PROGBIN) tools/mkproto.jsi > tools/protos.jsi
	$(MAKE) -C www

Changes to c-demos/Makefile.

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
dyn.so: dyn.c $(ALLDEPS)
	$(CC) $(ACFLAGS) -I$(JSIDIR) $(SHFLAGS) dyn.c -o dyn.so

prime.so: prime.c $(ALLDEPS)
	$(CC) $(CFLAGS) $(SHFLAGS) prime.c -o prime.so

demo1: demo1.c $(ALLDEPS)
	$(CC) -o demo1 $(CFLAGS) -DJSI_USER_EXTENSION=Jsi_Initdemo1 demo1.c $(JSIDIR)/main.c $(JSIDIR)/jsiOne.c $(LDFLAGS)
        
demo2: demo2.c $(ALLDEPS)
	$(CC) -o demo2 $(CFLAGS) -DJSI_USER_EXTENSION=Jsi_Initdemo2 demo2.c $(JSIDIR)/main.c $(JSIDIR)/jsiOne.c $(LDFLAGS)

demo2.so: demo2.c $(ALLDEPS)
	$(CC) $(SHFLAGS) -o demo2.so $(CFLAGS) demo2.c $(JSIDIR)/main.c $(LDFLAGS)


litedemo: litedemo.c $(ALLDEPS)
	$(CC) $(CFLAGS) -DJSI__SQLITE=1 litedemo.c -o litedemo -lm -lpthread -lsqlite3 -ldl

testmem:
	JSI_INTERP_OPTS='{memDebug:1}' $(MAKE) test

test: all
	./listdemo
	./litedemo







|


|


|



|







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
dyn.so: dyn.c $(ALLDEPS)
	$(CC) $(ACFLAGS) -I$(JSIDIR) $(SHFLAGS) dyn.c -o dyn.so

prime.so: prime.c $(ALLDEPS)
	$(CC) $(CFLAGS) $(SHFLAGS) prime.c -o prime.so

demo1: demo1.c $(ALLDEPS)
	$(CC) -o demo1 $(CFLAGS) -DJSI_USER_EXTENSION=Jsi_Initdemo1 demo1.c -DJSI__MAIN=1 $(JSIDIR)/jsiOne.c $(LDFLAGS)
        
demo2: demo2.c $(ALLDEPS)
	$(CC) -o demo2 $(CFLAGS) -DJSI_USER_EXTENSION=Jsi_Initdemo2 demo2.c -DJSI__MAIN=1 $(JSIDIR)/jsiOne.c $(LDFLAGS)

demo2.so: demo2.c $(ALLDEPS)
	$(CC) $(SHFLAGS) -o demo2.so $(CFLAGS) demo2.c -DJSI__MAIN=1 $(LDFLAGS)


litedemo: litedemo.c $(ALLDEPS)
	$(CC) $(CFLAGS) -DJSI__SQLITE=1 litedemo.c -o litedemo -lsqlite3 -lm -lpthread -ldl

testmem:
	JSI_INTERP_OPTS='{memDebug:1}' $(MAKE) test

test: all
	./listdemo
	./litedemo

Changes to configure.

15
16
17
18
19
20
21
22
23
24
25
26
    JSICURVER=`fgrep 'define JSI_VERSION_' src/jsi.h | cut -b29- | xargs | sed 's/ /./g'`
    if [ "$JSIMINVER" != "$JSICURVER" ]; then
        rm -f jsimin
    fi
fi

if [ ! -x jsimin ]; then
    ${CC} -o jsimin src/main.c src/jsi.c -lm -lz -lpthread ${LDL}
    echo "Created jsimin"
fi
./jsimin tools/configure.js $*








|




15
16
17
18
19
20
21
22
23
24
25
26
    JSICURVER=`fgrep 'define JSI_VERSION_' src/jsi.h | cut -b29- | xargs | sed 's/ /./g'`
    if [ "$JSIMINVER" != "$JSICURVER" ]; then
        rm -f jsimin
    fi
fi

if [ ! -x jsimin ]; then
    ${CC} -o jsimin src/jsi.c -DJSI__MAIN=1 -lm -lz -lpthread ${LDL}
    echo "Created jsimin"
fi
./jsimin tools/configure.js $*

Name change from doc/dumpdeep.js to doc/deepdoc.js.

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


// Script to dump markdeep export via websocket.
console.log('dumpdeep.js');

function DeepDumpWs() {
    console.log('DeepDumpWsTO');
    if (document.URL.indexOf("?export=save")<0) return;

    var url = document.URL.replace(/^http/,"ws");
    var ws = new WebSocket(url, "ws");
    ws.onopen = function() {
        var data = document.querySelectorAll("body pre");
        if (!data[0]) data = document.querySelectorAll("body code");
        if (!data[0]) return;
        data = data[0].innerText;
        console.log('DATA '+data);
        clearInterval(DeepDumpWsTO);
        ws.send(JSON.stringify({cmd:"save", url:url, data:data}));
    };
    ws.onmessage = function(msg) {
        if (msg.data !== 'DONE!!!')
            document.location = msg.data+"?export=save";
        else
            document.body.innerHTML = '<style>body{background:#ddd;}</style>DOWNLOAD COMPLETE: PLEASE CLOSE THIS TAB';
    };
};




window.onload = DeepDumpWs;
var DeepDumpWsTO = setTimeout(DeepDumpWs, 10000);



<
>

<

>








|









>
>
>
>
|
<
>
>
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
// Script to dump markdeep export via websocket.


function DeepDumpWs() {

    if (document.URL.indexOf("?export=save")<0) return;
    console.log('DeepDumpWs');
    var url = document.URL.replace(/^http/,"ws");
    var ws = new WebSocket(url, "ws");
    ws.onopen = function() {
        var data = document.querySelectorAll("body pre");
        if (!data[0]) data = document.querySelectorAll("body code");
        if (!data[0]) return;
        data = data[0].innerText;
        console.log('DATA '+data);
        //clearInterval(DeepDumpWsTO);
        ws.send(JSON.stringify({cmd:"save", url:url, data:data}));
    };
    ws.onmessage = function(msg) {
        if (msg.data !== 'DONE!!!')
            document.location = msg.data+"?export=save";
        else
            document.body.innerHTML = '<style>body{background:#ddd;}</style>DOWNLOAD COMPLETE: PLEASE CLOSE THIS TAB';
    };
};

function DeepDocCmd() {
    //console.log('in Deep');
    document.title=location.pathname.match(/\/([-\w]+)[^\/]*$/)[1];
    DeepDumpWs();

}

Changes to doc/include.shtml.


1

2
3










4
5
6
7






8
9
10




11
12
13
14
15
16
17

18
19

20
21
22
23

<style class="fallback">body{visibility:hidden;}</style>

<script>window.markdeepOptions={tocStyle:"medium"}</script>











<center style="">
    <span class="image"><a href="Jsish"><img class="markdeep" src="logojsi.png" /></a ><div class="imagecaption"><strong class="asterisk">Javascript Shell Interpreter</strong></div></span>
    <div style="font-size: 75%">
        &nbsp;[DOWNLOAD](Start)&nbsp;/&nbsp;[DEVELOP](Develop)&nbsp;/&nbsp;[DEPLOY](Deploy)






    </div>
        <hr style="margin:0;">
[Types](Types)&nbsp;[Builtin](Builtin)&nbsp;[Reference](Reference)&nbsp;&nbsp;[Index](Index)&nbsp;&nbsp;[**Fossil**](https://jsish.org/jsi)&nbsp;&nbsp;[Demos](Demos)&nbsp;[Log](Logging)&nbsp;[Test](Testing)&nbsp;[Debug](Debug)&nbsp;[Misc](Misc)







</center>

<!--#include file="$md"-->


<link rel="stylesheet" href="jsistyle.css" type="text/css" media="screen" />
<!-- Markdeep: --><style class="fallback">body{visibility:hidden;}</style><script src="markdeep.min.js"></script>

<script>var startOfMarkDeep=true; window.alreadyProcessedMarkdeep||(document.body.style.visibility='visible'); document.title=location.pathname.match(/\/([\w]+)[^\/]*$/)[1];</script>
<script src="dumpdeep.js"></script>


>

>
|
<
>
>
>
>
>
>
>
>
>
>
|
<
<
<
>
>
>
>
>
>

<
<
>
>
>
>

<
<
<
<


>

<
>
|
|


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
<meta charset="UTF-8">
<style class="fallback">body{visibility:hidden;}</style>
<meta name="viewport" content="width=device-width">
<script>window.markdeepOptions={tocStyle:"medium", mode:"markdeep", onLoad:function(){DeepDocCmd();}}</script>

<div class="header">
<a href="Jsish"><img src="logojsi.png" style="float:left"/></a >
<div class="hdrpre">
<span class="hdrstart">
    <b>Web-Interp</b>
    <span>
        <i>[Install](Install)/[Code](Coding)/[Deploy](Deploy)</i>
    </span><br>
</span>
<br>
<hr style="margin:0;">



<span class="hdrnav">
    <div>
    [Demos](Demos)
    [Docs](Docs)
    [Cookbook](Cookbook)
    <b>[Source](https://jsish.org/jsi)</b>
    </div>


</div>
    </div>
<br>
</div>





<!--#include file="$md"-->


<link rel="stylesheet" href="jsistyle.css" type="text/css" media="screen" />

<!-- Markdeep: --><script src="markdeep.js"></script>
<script>var startOfMarkDeep=true; window.alreadyProcessedMarkdeep||(document.body.style.visibility='visible'); </script>
<script src="deepdoc.js"></script>


Changes to doc/jsistyle.css.

1
2
3
4
5
6
7
8











.md .mediumTOC {background:#fff; border:1px solid #ccc; padding-right:3px;}
.md .mediumTOC center { display:none; }
h1,h2,h3,h4 { clear:none; padding:0px; }
.tocNumber {display:none; }
.md h1::before, .md h2::before, .md h3::before,.md h4::before { display:none; }
body { max-width:980px; }
.output { background:#000; color:#fff; }
.markdeepFooter { visibility:hidden; }











|







>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.md .mediumTOC {background:#fff; border:1px solid #ccc; padding-right:3px; margin-top:2.4em; }
.md .mediumTOC center { display:none; }
h1,h2,h3,h4 { clear:none; padding:0px; }
.tocNumber {display:none; }
.md h1::before, .md h2::before, .md h3::before,.md h4::before { display:none; }
body { max-width:980px; }
.output { background:#000; color:#fff; }
.markdeepFooter { visibility:hidden; }
.header a:link, .header a:visited { margin:.1em; } 
.header img { margin: 0 .4em 0 0; }
.hdrstart b { float:left; }
.hdrstart span { float:right; }
.hdrnav div { text-align:center; }
.md svg.diagram { fill:#35A; }
@-moz-document url-prefix() {
  .hdrpre {
    margin: -2em;
  }
}

Name change from doc/md/Builtin.md to doc/md/Builtins.md.

1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
..
20
21
22
23
24
25
26





27
28
29
30
31
32
33
...
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
Builtins
========

Jsi implements the following built-in commands.

[CData](Reference#CData)
----
See [CData](CData).



[Channel](Reference#Channel)
----
A Channel object can be created for reading and/or writing individual files.
Generally an object is instantiated to open the file and then read/write operations are performed.

~~~~ JS linenumbers
................................................................................
else {
    while((n = f.gets())!=undefined) {
        puts(n);
    }
}
~~~~







[Event](Reference#Event)
----

As in Ecmascript, events are scheduled using the standard functions:

function setTimeout(callback:function, msdelay:number):number
................................................................................
The direct approach uses the script "tools/mkjsize.js":

~~~~
cp jsish jsize
tools/mkjsize.js create jsize zipdir
~~~~

For further working example applications see [Start#start/examples].


### Writing
To create a zip archive use:
~~~~
  Zvfs.create('arch.zip', file.glob('*.js'))
~~~~



|
<
<
<
<
<
>







 







>
>
>
>
>







 







|







1
2
3
4





5
6
7
8
9
10
11
12
..
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
...
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
Builtins
========

The following provides more detailed information and examples on the built-in commands





than in the auto-generated the Reference.

[Channel](Reference#Channel)
----
A Channel object can be created for reading and/or writing individual files.
Generally an object is instantiated to open the file and then read/write operations are performed.

~~~~ JS linenumbers
................................................................................
else {
    while((n = f.gets())!=undefined) {
        puts(n);
    }
}
~~~~


[CData](Reference#CData)
----
See [CData](CData).


[Event](Reference#Event)
----

As in Ecmascript, events are scheduled using the standard functions:

function setTimeout(callback:function, msdelay:number):number
................................................................................
The direct approach uses the script "tools/mkjsize.js":

~~~~
cp jsish jsize
tools/mkjsize.js create jsize zipdir
~~~~

For further working example applications see [Install#getting/examples].


### Writing
To create a zip archive use:
~~~~
  Zvfs.create('arch.zip', file.glob('*.js'))
~~~~

Name change from doc/md/Develop.md to doc/md/Coding.md.

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
..
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
...
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
...
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
...
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
Develop
========
Code in Jsish is usually developed as modules, which are
usable either by code or the command-line.


Modules
----


The most basic Jsi module is a function with array parameter:
























~~~~ JS linenumbers
function add(args) { return args.join(', '); }
~~~~

When the file rootname matches the function name, we can invoke it with "-m":

~~~~
jsish -m add.jsi Able Baker Charlie
~~~~ the output
Able, Baker, Charlie
~~~~

Note "-m" is equivalent to adding an explicit **runModule()**:

~~~~ JS linenumbers
function add(args) { return args.join(', '); }
runModule(add);
~~~~


### Options
A Jsi module that can handle switches uses <b>parseOpts</b>:

~~~~ JS linenumbers
function add(args, conf) {
................................................................................

~~~~
jsish -m -create httpd.jsi -web true
~~~~


### Help
Modules support "-h" and "--help":
~~~~
jsish add.jsi -h
~~~~ the output
/home/user/src/ii/add.jsi:3: help: ...
.  Options are:
     -name -start
Use --help for long help.
~~~~


#### Long Help
For longer help we need a more complete module with type-checking and comments:

~~~~ JS linenumbers
................................................................................
}
runModule(add);
~~~~

Then comments will be extracted from **options**:

~~~~
jsish add.jsi --help
~~~~ the output
/home/user/src/ii/add.jsi:7: help: ...
Concat args into list..  Options are:
    -name       ""      // Name prefix.
    -start      0       // Start position.

Accepted by all .jsi modules: -Debug, -Trace, -Test.
................................................................................
[ "/zvfs/lib/autoload.jsi" ]
~~~~

Which means it loads the file [Zip-FS](../lib/autoload.jsi], usually from the self-mounted [b ./js-zvfs.md.html) on /zvfs.

After this Jsi_Auto members are accessed to load the function.

Additionally, if an application is run from a [.zip file](../Start#start/files), it's autoload.jsi
file gets added to autoFiles automatically.

This 2-stage approach results in no overhead, until a function call fails.

#### Object Functions
Autoloading non-constructor object functions can use the following approach
(the example ignores the fact that JSON is not actually loadable):
................................................................................

The output may seem overly verbose, but is advantageous when executed
from within geany (or vim) in that we can click to navigate through the file.

If simpler traces are desired, try:

~~~~
jsish -ItraceCall funcs,args tests/module.js
~~~~ the output
#1: > mod([]) in module.js:12
#1: > process([ 9 ]) in module.js:22
  #2: > sub([ 10 ]) in module.js:17
20
1
#1: > fmod([]) in module.js:36
................................................................................
~~~~


### Code Profile
Jsi can output detailed execution profile information for functions using:

~~~~
jsish -Iprofile true  SCRIPT
~~~~

The following demonstrates this on unix:

~~~~
jsish -Iprofile true  /tmp/while2.js   2>&1 | grep ^PROFILE: | sort -g -r -t= -k2
~~~~ the output
PROFILE: TOTAL: time=4.169039, func=3.099403, cmd=1.068323, #funcs=10003, #cmds=300001, cover=58.0%, #values=1860447, #objs=610397
PROFILE:  self=3.000902  all=4.069200  #calls=10000     self/call=0.000300  all/call=0.000407  cover=100.0%  func=foo file=/tmp/while2.js:29
PROFILE:  self=1.068298  all=1.068298  #calls=300000    self/call=0.000004  all/call=0.000004  cmd=Info.funcs
PROFILE:  self=0.098484  all=4.167684  #calls=1         self/call=0.098484  all/call=4.167684  cover= 75.0%  func=bar file=/tmp/while2.js:44
PROFILE:  self=0.000024  all=0.000024  #calls=1         self/call=0.000024  all/call=0.000024  cmd=puts
PROFILE:  self=0.000017  all=4.167700  #calls=1         self/call=0.000017  all/call=4.167700  cover=100.0%  func=aa file=/tmp/while2.js:27
................................................................................
| func      | Name of the function                                   |
| cmd       | Name of the command                                    |
| file      | File and line number of function                       |


### Code Coverage
In addition to the simple coverage statistics available with profile,
detailed code coverage can be obtained with -Icoverage, eg:

~~~~
jsish -Icoverage true  /tmp/while2.js   2>&1 | grep ^COVERAGE: | sort -g -r -t= -k4
~~~~ the output
COVERAGE: func=bar  file=/tmp/while2.js:48  cover=75.0%  hits=6,  all=8,  misses=56-57
COVERAGE: func=cc  file=/tmp/while2.js:7  cover=30.0%  hits=4,  all=13,  misses=10-13,18-22
COVERAGE: func=bb  file=/tmp/while2.js:27  cover=0%
~~~~

Output is produced only for functions with less than 100% coverage.
|
|
|
<




>

<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





<
<






<
<
<
<
<
<







 







|






|







 







|







 







|







 







|







 







|





|







 







|


|







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
...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
...
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
...
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
Coding
====
A module in Jsish is usable either from the command-line, or by other code.



Modules
----
The most basic module is a file containing a function of the same basename:


~~~~
echo 'function hello() { return "Hello World!"; }' > hello.jsi
jsish -m hello.jsi
~~~~ the output
Hello World!
~~~~

Note the "-m" is not needed if an explicit **runModule()** is added:

~~~~ JS linenumbers
function hello() { return "Hello World!"; }
runModule(hello);
~~~~

~~~~
jsish hello.jsi
~~~~ the output
Hello World!
~~~~


### Arguments
Modules handle arguments via an array parameter:

~~~~ JS linenumbers
function add(args) { return args.join(', '); }
~~~~



~~~~
jsish -m add.jsi Able Baker Charlie
~~~~ the output
Able, Baker, Charlie
~~~~









### Options
A Jsi module that can handle switches uses <b>parseOpts</b>:

~~~~ JS linenumbers
function add(args, conf) {
................................................................................

~~~~
jsish -m -create httpd.jsi -web true
~~~~


### Help
Modules support "-h" and "-help":
~~~~
jsish add.jsi -h
~~~~ the output
/home/user/src/ii/add.jsi:3: help: ...
.  Options are:
     -name -start
Use -help for long help.
~~~~


#### Long Help
For longer help we need a more complete module with type-checking and comments:

~~~~ JS linenumbers
................................................................................
}
runModule(add);
~~~~

Then comments will be extracted from **options**:

~~~~
jsish add.jsi -help
~~~~ the output
/home/user/src/ii/add.jsi:7: help: ...
Concat args into list..  Options are:
    -name       ""      // Name prefix.
    -start      0       // Start position.

Accepted by all .jsi modules: -Debug, -Trace, -Test.
................................................................................
[ "/zvfs/lib/autoload.jsi" ]
~~~~

Which means it loads the file [Zip-FS](../lib/autoload.jsi], usually from the self-mounted [b ./js-zvfs.md.html) on /zvfs.

After this Jsi_Auto members are accessed to load the function.

Additionally, if an application is run from a [.zip file](../Install#files), it's autoload.jsi
file gets added to autoFiles automatically.

This 2-stage approach results in no overhead, until a function call fails.

#### Object Functions
Autoloading non-constructor object functions can use the following approach
(the example ignores the fact that JSON is not actually loadable):
................................................................................

The output may seem overly verbose, but is advantageous when executed
from within geany (or vim) in that we can click to navigate through the file.

If simpler traces are desired, try:

~~~~
jsish --I traceCall:funcs,args tests/module.js
~~~~ the output
#1: > mod([]) in module.js:12
#1: > process([ 9 ]) in module.js:22
  #2: > sub([ 10 ]) in module.js:17
20
1
#1: > fmod([]) in module.js:36
................................................................................
~~~~


### Code Profile
Jsi can output detailed execution profile information for functions using:

~~~~
jsish --I profile  SCRIPT
~~~~

The following demonstrates this on unix:

~~~~
jsish --I profile  /tmp/while2.js   2>&1 | grep ^PROFILE: | sort -g -r -t= -k2
~~~~ the output
PROFILE: TOTAL: time=4.169039, func=3.099403, cmd=1.068323, #funcs=10003, #cmds=300001, cover=58.0%, #values=1860447, #objs=610397
PROFILE:  self=3.000902  all=4.069200  #calls=10000     self/call=0.000300  all/call=0.000407  cover=100.0%  func=foo file=/tmp/while2.js:29
PROFILE:  self=1.068298  all=1.068298  #calls=300000    self/call=0.000004  all/call=0.000004  cmd=Info.funcs
PROFILE:  self=0.098484  all=4.167684  #calls=1         self/call=0.098484  all/call=4.167684  cover= 75.0%  func=bar file=/tmp/while2.js:44
PROFILE:  self=0.000024  all=0.000024  #calls=1         self/call=0.000024  all/call=0.000024  cmd=puts
PROFILE:  self=0.000017  all=4.167700  #calls=1         self/call=0.000017  all/call=4.167700  cover=100.0%  func=aa file=/tmp/while2.js:27
................................................................................
| func      | Name of the function                                   |
| cmd       | Name of the command                                    |
| file      | File and line number of function                       |


### Code Coverage
In addition to the simple coverage statistics available with profile,
detailed code coverage can be obtained with --I coverage, eg:

~~~~
jsish --I coverage  /tmp/while2.js   2>&1 | grep ^COVERAGE: | sort -g -r -t= -k4
~~~~ the output
COVERAGE: func=bar  file=/tmp/while2.js:48  cover=75.0%  hits=6,  all=8,  misses=56-57
COVERAGE: func=cc  file=/tmp/while2.js:7  cover=30.0%  hits=4,  all=13,  misses=10-13,18-22
COVERAGE: func=bb  file=/tmp/while2.js:27  cover=0%
~~~~

Output is produced only for functions with less than 100% coverage.

Changes to doc/md/Debug.md.

292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
   assert clearInterval console decodeURI encodeURI exit file format include info
   isFinite isNaN load parseFloat parseInt puts quote setInterval setTimeout signal
   sys util zvfs.
~~~~

The file and line number is reported, as well as an enumeration of known commands
in the given scope.
This allows errors to be [parsable by IDE's](Start#start/editors).


### Method Introspection
Upon error, an objects sub-methods are enumerated, if possible:

~~~~
Info.xx()







|







292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
   assert clearInterval console decodeURI encodeURI exit file format include info
   isFinite isNaN load parseFloat parseInt puts quote setInterval setTimeout signal
   sys util zvfs.
~~~~

The file and line number is reported, as well as an enumeration of known commands
in the given scope.
This allows errors to be [parsable by IDE's](Install#getting/editors).


### Method Introspection
Upon error, an objects sub-methods are enumerated, if possible:

~~~~
Info.xx()

Changes to doc/md/Deploy.md.

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
..
45
46
47
48
49
50
51
52
53

54
55
56
57
58
59
60
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

- contains one or more applications.
- provides all files required by application.
- supports remote update, syncing and multiple versions (fossil only).


Typically there is a file "main.jsi", plus zero or more directories
of [packages](Develop#develop/files) which can be run:

~~~~SH
jsish -a jsi-app.zip Ledger
~~~~

Fossil
------
................................................................................
manager can mount the latest compatible version.

This is what is meant by safe update.  Although all releases (past and present) may be available,
the latest safe one will be chosen for us automatically.
Or we can choose to fallback previous versions without re-installing.
Thus like /zvfs, dependency issues are avoided.

Publishing
==========

During development of an App-Archive, there is the additional step
of application changes getting pushed up to the repos, with appropriate
tags to enforce dependencies.

        *********************************
        *              .--------------. *
        *              |   Internet   | *
................................................................................
    For an example see the [jsi-app](https://jsish.org/jsi-app) deploy which
    hosts the [online Ledger demo](https://jsish.org/App10/Ledger/html/main.htmli).

Jsish can run applications directly from downloaded fossil repositories, eg:

~~~~SH
fossil clone http://jsish.org/jsi-app jsi-app.fossil
jsish -a jsi-app.fossil Ledger --help
~~~~

### Updating
To bring a repository up-to-date use:

~~~~SH
fossil pull -R jsi-app.fossil







|







 







|
<
>







 







|







4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
..
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
..
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

- contains one or more applications.
- provides all files required by application.
- supports remote update, syncing and multiple versions (fossil only).


Typically there is a file "main.jsi", plus zero or more directories
of [packages](Coding#files) which can be run:

~~~~SH
jsish -a jsi-app.zip Ledger
~~~~

Fossil
------
................................................................................
manager can mount the latest compatible version.

This is what is meant by safe update.  Although all releases (past and present) may be available,
the latest safe one will be chosen for us automatically.
Or we can choose to fallback previous versions without re-installing.
Thus like /zvfs, dependency issues are avoided.

Publish

----
During development of an App-Archive, there is the additional step
of application changes getting pushed up to the repos, with appropriate
tags to enforce dependencies.

        *********************************
        *              .--------------. *
        *              |   Internet   | *
................................................................................
    For an example see the [jsi-app](https://jsish.org/jsi-app) deploy which
    hosts the [online Ledger demo](https://jsish.org/App10/Ledger/html/main.htmli).

Jsish can run applications directly from downloaded fossil repositories, eg:

~~~~SH
fossil clone http://jsish.org/jsi-app jsi-app.fossil
jsish -a jsi-app.fossil Ledger -help
~~~~

### Updating
To bring a repository up-to-date use:

~~~~SH
fossil pull -R jsi-app.fossil

Name change from doc/md/Index.md to doc/md/Docs.md.

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
Index
=====

| NAME                   | DESCRIPTION                    |
|------------------------|--------------------------------|
| [Builtin](Builtin)     | The built-in commands          |
| [C-API](C-API)         | The C API                      |
| [CData](CData)         | Using C structs in Jsi         |

| [DBQuery](DBQuery)     | Sqlite database queries from C |
| [Debug](Debug)         | Debugging in Jsi               |
| [Demos](Demos)         | Demo applications              |
| [Deploy](Deploy)       | Deploying Jsi apps             |
| [Develop](Develop)     | Developing with Jsi            |
| [Interp](Interp)       | The Interp API                 |
| [Jsish](Jsish)         | The Jsi shell                  |
| [Ledger](Ledger)       | Financial app developed in Jsi |
| [Logging](Logging)     | Logging support                |
| [Misc](Misc)           | Various topics                 |
| [MySql](MySql)         | MySql database API             |
| [Reference](Reference) | Built-in commands (generated)  |
| [Sqlite](Sqlite)       | Sqlite database API            |
| [Start](Start)         | Getting started with Jsi       |
| [Testing](Testing)     | Testing facility for scripts   |
| [Types](Types)         | Function parameter types       |

                  
|




|


>




|








<




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
Docs
=====

| NAME                   | DESCRIPTION                    |
|------------------------|--------------------------------|
| [Builtins](Builtins)   | The built-in commands          |
| [C-API](C-API)         | The C API                      |
| [CData](CData)         | Using C structs in Jsi         |
| [Coding](Coding)       | Developing with Jsi            |
| [DBQuery](DBQuery)     | Sqlite database queries from C |
| [Debug](Debug)         | Debugging in Jsi               |
| [Demos](Demos)         | Demo applications              |
| [Deploy](Deploy)       | Deploying Jsi apps             |
| [Install](Install)     | Installing and getting started |
| [Interp](Interp)       | The Interp API                 |
| [Jsish](Jsish)         | The Jsi shell                  |
| [Ledger](Ledger)       | Financial app developed in Jsi |
| [Logging](Logging)     | Logging support                |
| [Misc](Misc)           | Various topics                 |
| [MySql](MySql)         | MySql database API             |
| [Reference](Reference) | Built-in commands (generated)  |
| [Sqlite](Sqlite)       | Sqlite database API            |

| [Testing](Testing)     | Testing facility for scripts   |
| [Types](Types)         | Function parameter types       |

                  

Name change from doc/md/Start.md to doc/md/Install.md.

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
...
234
235
236
237
238
239
240
241
242
243
244
245
246
247




248
249
250
251
252
253
254

255
256
257
258
259
260
261
262
263
264
265
266
267

268
269
270
271
272
273

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
...
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
Start
====
This page describes getting started with Jsi.

Download
----
If you don't want to build Jsi from source,
limited binaries are available here: https://jsish.org/jsi-bin


### Source
To download the zipped source:
~~~~
mkdir jsi && cd jsi && wget https://jsish.org/jsi/zip -O jsish.zip && unzip jsish.zip
~~~~

Then follow [build](#buildingjsi) directions.

!!!
    Note: it is better to get the source with Fossil.


### Fossil
As Jsi is version-controlled with [fossil](http://www.fossil-scm.org),
the easiest way to keep up to date is by cloning the Jsi repository:

~~~~
mkdir jsi && cd jsi && fossil clone https://jsish.org/jsi jsi.fossil && fossil open jsi.fossil
~~~~

Then follow [build](#buildingjsi) directions.

To get fossil:

- On Ubuntu: "apt-get install fossil", or
- Get a binary from [http://www.fossil-scm.org/index.html/uv/download.html]


Building
----
Jsi is written in C, but can be compiled as either native C, or native C++ (does not use **extern C**).

................................................................................
On Debian a few packages are required:

~~~~SH
sudo apt-get install build-essential bison libreadline-dev libsqlite3-dev libwebsockets-dev libncurses-dev cmake libmysqlclient-dev
~~~~

!!!
    Some of these (eg. **cmake**) are only required for specific configurations.


### Linux
To build the Linux target there are are two steps:

~~~~SH
./configure
................................................................................
Help
----
To see the supported switches in jsish use -h

~~~~
jsish -h
~~~~ the output
usage: jsish -h/--help | -v/--version | -d/--debug | -D/--debugui | -u/--unittest | -U/-UU
    | -s/--safe | -Z/--zip | -S/--sqliteui | -w/--wget | -W/--websrv | -H/--htmli | -J/--jsi
    | -C/--cssi | -c/--cdata | -M/--module | -m/--make | -e/--eval | -t/--tracecall
    | -a/--archive | -T/--typecheck OPT | -IOPT VAL | FILE arg arg ...
Use --help for long help.
~~~~





### Switches
Available switches in Jsish are:

| Options              | Description                                                          |
|----------------------|----------------------------------------------------------------------|
| -a/--archive FILE    | Mount an archive (zip, sqlar or fossil repo) and run module.         |
| -c/--cdata FILE      | Generate .c or JSON output from a .jsc description.                  |

| -C/--cssi FILE       | Preprocess embedded CSS in .css file.                                |
| -d/--debug FILE      | Run console debugger on script.                                      |
| -D/--debugui FILE    | Run web-gui debugger on script.                                      |
| -e/--eval STRING     | Evaluate a javascript string and exit.                               |
| -h/--help            | Print help in short or long form.                                    |
| -H/--htmli FILE      | Preprocess embedded jsi in .htmli file.                              |
| -J/--jsi FILE        | Preprocess a .jsi file to typecheck in standard javascript.          |
| -m/--module FILE     | Source file and invoke runModule.                                    |
| -M/--make FILE       | Preprocess script as a Jsi Makefile.                                 |
| -s/--safe FILE       | Run script in safe sub-interp.                                       |
| -S/--sqliteui DBFILE | Run Sqlite web-gui.                                                  |
| -t/--tracecall       | Trace all function calls.                                            |
| -T/--typecheck OPT   | Enable typechecking.                                                 |

| -u/--unittest FILE   | Run unit-tests on a script file, or a dir containing .js/.jsi files. |
| -U/-UU SCRIPT        | Show output from unit-test mode, omitting pass/fail compare.         |
| -v/--version         | Print short/long version info and exit.                              |
| -Z/--zip             | Used to append/manage zip files at end of executable.                |
| -w/--wget URL        | Web client to download file from url.                                |
| -W/--websrv FILE     | Serve out file in web server, with preprocessing.                    |



### Modules
Help for module commands can similarly be displayed, eg:

~~~~
jsish -d -h
~~~~ the output
/zvfs/lib/Jsi_Debug.jsi:34: help: ...
A command-line Debugger for Jsi scripts..  Options are:
     -echoCmd -safe
Use --help for long help.
~~~~

or the long form:

~~~~ SH input
jsish -d --help
~~~~ the output
/zvfs/lib/Jsi_Debug.jsi:34: help: ...
A command-line Debugger for Jsi scripts..  Options are:
    -echoCmd    true        // Echo user cmds.
    -safe       false       // Debug program in a safe interp (untested)

Accepted by all .jsi modules: -Debug, -Trace, -Test
................................................................................

Jsi is distributed with several demonstration web applications:

- [DebugUI](app/debugui): a Debugger user interface for Jsi scripts.
- [SqliteUI](app/sqliteui): a web user interface to Sqlite.
- [LedgerJS](app/ledgerjs): an accounting program.

These can all be run as [standalone](Builtin#zvfs) applications.

Shell
----
You can use jsish
as an enhanced replacement for [#!/usr/bin/env](https://en.wikipedia.org/wiki/Shebang_(Unix)).
This lets you run scripts from the command line with default arguments:

................................................................................
(there must be a %s and at least one argument)

From geany you can now run the script with F9, and step through
warnings and errors.

This also works for [logging](Logging) messages: [mytest2](../js-demos/log/mytest2.jsi)


Autocomplete
----
Autocompletion can be set-up on unix by adding to .bashrc:


~~~~
complete -W '-h --help -v --version -d --debug -D --debugui -u -unittest -s --safe -Z --zip \
  -S --sqliteui -W --websrv -H --htmli -J --jsi -c --cdata -C --cssi -m --module -e --eval \
  -T --typecheck -t --tracecall' -f '*.jsi'  'jsish'
~~~~

or running:

~~~~
make setup.
~~~~

Next time you login you can use autocompletion with TAB.

This can make running
jsish from the command-line a little nicer.


Editors
----


### Geany
[Geany](http://www.geany.org) is a convenient editor to use with Jsi.
|

|



|
<





|


|


|










|



|







 







|







 







|
|
<
<
<
<

>
>
>
>
|
|

|
<
|
|
>
|
|
|
|
|
|
|
|
|
|
|
|
<
>
|
<
<
<
<
<
>







<
<
<
<
<
<
<
<
<
<
<







 







|







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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
...
233
234
235
236
237
238
239
240
241




242
243
244
245
246
247
248
249
250

251
252
253
254
255
256
257
258
259
260
261
262
263
264
265

266
267





268
269
270
271
272
273
274
275











276
277
278
279
280
281
282
...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
...
315
316
317
318
319
320
321























322
323
324
325
326
327
328
Install
====
Following is an overview of getting, installing, building and using Jsi.

Download
----
If building from source is inconvenient, binaries are available: https://jsish.org/jsi-bin.



### Source
To download the zipped source:
~~~~
mkdir jsi && cd jsi && wget http://jsish.org/jsi/zip -O jsish.zip && unzip jsish.zip
~~~~

Then follow [build](#start/building) directions.

!!!
    Note: get source with Fossil if possible.


### Fossil
As Jsi is version-controlled with [fossil](http://www.fossil-scm.org),
the easiest way to keep up to date is by cloning the Jsi repository:

~~~~
mkdir jsi && cd jsi && fossil clone https://jsish.org/jsi jsi.fossil && fossil open jsi.fossil
~~~~

Then follow [build](#start/building) directions.

To get fossil:

- On Debian try "apt-get install fossil".
- Get a binary from [http://www.fossil-scm.org/index.html/uv/download.html]


Building
----
Jsi is written in C, but can be compiled as either native C, or native C++ (does not use **extern C**).

................................................................................
On Debian a few packages are required:

~~~~SH
sudo apt-get install build-essential bison libreadline-dev libsqlite3-dev libwebsockets-dev libncurses-dev cmake libmysqlclient-dev
~~~~

!!!
    Some packages (eg. **cmake**) are required only for specific configurations.


### Linux
To build the Linux target there are are two steps:

~~~~SH
./configure
................................................................................
Help
----
To see the supported switches in jsish use -h

~~~~
jsish -h
~~~~ the output
USAGE:
  jsish [PREFIX-OPTS] [COMMAND-OPTS|FILE] ...





PREFIX-OPTS:
  --F           Trace all function calls/returns.
  --I OPT:VAL   Interp option: equivalent to Interp.conf({OPT:VAL}).
  --T OPT       Typecheck option: equivalent to "use OPT".
  --U           Display unittest output, minus pass/fail compare.
  --V           Same as --U, but adds file and line number to output.

COMMAND-OPTS:

  -a            Archive: mount an archive (zip, sqlar or fossil repo) and run module.
  -c            CData: generate .c or JSON output from a .jsc description.
  -d            Debug: console script debugger.
  -e STRING     Eval: run javascript in STRING and exit.
  -g            Gendeep: generate html output from markdeep source.
  -h            Help: show this help.
  -m MOD        Module: invoke runModule, after source if file.
  -s            Safe: runs script in safe sub-interp.
  -u            UnitTest: test script file(s) or directories .js/.jsi files.
  -w            Wget: web client to download file from url.
  -v            Version: show version info.
  -z            Zip: append/manage zip files at end of executable.
  -D            DebugUI: web-gui script debugger.
  -S            SqliteUI: web-gui for sqlite database file.
  -W            Websrv: web server to serve out content.


Interp options may also be set via the environment eg. JSI_INTERP_OPTS='{coverage:true}'





~~~~


### Modules
Help for module commands can similarly be displayed, eg:

~~~~
jsish -d -h











~~~~ the output
/zvfs/lib/Jsi_Debug.jsi:34: help: ...
A command-line Debugger for Jsi scripts..  Options are:
    -echoCmd    true        // Echo user cmds.
    -safe       false       // Debug program in a safe interp (untested)

Accepted by all .jsi modules: -Debug, -Trace, -Test
................................................................................

Jsi is distributed with several demonstration web applications:

- [DebugUI](app/debugui): a Debugger user interface for Jsi scripts.
- [SqliteUI](app/sqliteui): a web user interface to Sqlite.
- [LedgerJS](app/ledgerjs): an accounting program.

These can all be run as [standalone](Builtins#zvfs) applications.

Shell
----
You can use jsish
as an enhanced replacement for [#!/usr/bin/env](https://en.wikipedia.org/wiki/Shebang_(Unix)).
This lets you run scripts from the command line with default arguments:

................................................................................
(there must be a %s and at least one argument)

From geany you can now run the script with F9, and step through
warnings and errors.

This also works for [logging](Logging) messages: [mytest2](../js-demos/log/mytest2.jsi)

























Editors
----


### Geany
[Geany](http://www.geany.org) is a convenient editor to use with Jsi.

Changes to doc/md/Interp.md.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283

- Programmatically within a script:

~~~~
Interp.conf({typeCheck:["error"]});
~~~~

- From the command-line using -I:

~~~~ SH
jsish -ItypeCheck error foo.s
~~~~

- Environment variables:

~~~~ SH
JSI_INTERP_OPTS='{typeCheck:["parse"]}' jsish foo.js
~~~~
................................................................................

Options
-------

When we create an interpreter, we can pass in an number of options.
The available options are described [here](Reference#new InterpOptions).

We can also set or get [options](Start#options) for the current interpreter in the usual way:

!!! NOTE
    Options marked as initOnly may only be set at interpreter creation time.

~~~~
Interp.conf(); // Dump all options
Interp.conf('strict'); // Get one option
................................................................................
~~~~


Events
------
Events in javascript are traditionally created by the standard setTimeout()/setInterval() methods.

In Jsi, various commands and extensions (eg. [websockets](Builtin#websocket)) implement asynchronous
features that use events.

Events explicitly get processed by calls to update().
For more details see [Events](Builtin#event).


Call
----
Due to overhead of parsing, eval()
is actually the least efficient way to run commands.

................................................................................
~~~~


Interps and Data
----------------
To reduce coupling and increase integrity within interpreters, data objects are never shared.

To accomplish this, all data is internally converted to and from [JSON](Builtin#json) at the Interp-Interp interface.

This all happens automatically, so normally users don't need to be worry about the details.

But there are potential performance issues with this JSON translation.










|


|







 







|







 







|



|







 







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283

- Programmatically within a script:

~~~~
Interp.conf({typeCheck:["error"]});
~~~~

- From the command-line using --I:

~~~~ SH
jsish --I typeCheck:error foo.s
~~~~

- Environment variables:

~~~~ SH
JSI_INTERP_OPTS='{typeCheck:["parse"]}' jsish foo.js
~~~~
................................................................................

Options
-------

When we create an interpreter, we can pass in an number of options.
The available options are described [here](Reference#new InterpOptions).

We can also set or get [options](Install#options) for the current interpreter in the usual way:

!!! NOTE
    Options marked as initOnly may only be set at interpreter creation time.

~~~~
Interp.conf(); // Dump all options
Interp.conf('strict'); // Get one option
................................................................................
~~~~


Events
------
Events in javascript are traditionally created by the standard setTimeout()/setInterval() methods.

In Jsi, various commands and extensions (eg. [websockets](Builtins#websocket)) implement asynchronous
features that use events.

Events explicitly get processed by calls to update().
For more details see [Events](Builtins#event).


Call
----
Due to overhead of parsing, eval()
is actually the least efficient way to run commands.

................................................................................
~~~~


Interps and Data
----------------
To reduce coupling and increase integrity within interpreters, data objects are never shared.

To accomplish this, all data is internally converted to and from [JSON](Builtins#json) at the Interp-Interp interface.

This all happens automatically, so normally users don't need to be worry about the details.

But there are potential performance issues with this JSON translation.



Changes to doc/md/Jsish.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
..
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
Jsish
=====
Jsish is a self-contained and dependency-free server-side Web development environment.
There is no need to learn a new language:
Jsi javascript interpreter (enhanced with 
[parameter types](Types)) provides everything required for web application development already built-in.

    *********************************
    * .---------.                   *
    * | Browser |                   *
    * '----+----'                   *
    *      ^         .------.       *
    *      |   .--->|Database|      *
................................................................................
    * |  Jsish  +   | Script  |     *
    * +---------+<--+  File   |     *
    * |  ZVFS   |   '---------'     *
    *  '-------'                    *
    *                               *
    *********************************

WebSocket and Sqlite are compiled in and
support scripts are bundled into a ZVFS appended to the binary.


Deploy
------
A [Deploy](Deploy) is a zip/sqlar archive or fossil
repository containing one or more applications, which Jsi can mount and execute.

................................................................................

Security
----
As Jsi is self contained, running it standalone in a chroot-jail needs only
a few files from /dev and /etc.   For example, this [Ledger](https://jsish.org/App01/Ledger)
demo is run in an unprivileged chroot containing a single executable (*jsish*),
with non-data files made immutable with **chattr**.
And if this is not secure enough, Jsi offers [Safe-mode](Interp#interp/safe-mode).

In addition, serving content from a zip or fossil repository adds
another abstraction layer, making it that much harder to corrupt or hijack content.

An advantage of the chroot approach as compared with something like containers, is
that far less disk space and system memory is required.
Jsi and fossil together total around 10Meg of disk. Using hard links you


|

|
|







 







|
|







 







|







1
2
3
4
5
6
7
8
9
10
11
12
13
..
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
Jsish
=====
Jsish is a self-contained, dependency-free server-side Web development environment.
There is no need to learn a new language:
just an enhanced javascript with 
[parameter types](Types), and all prerequisites built-in.

    *********************************
    * .---------.                   *
    * | Browser |                   *
    * '----+----'                   *
    *      ^         .------.       *
    *      |   .--->|Database|      *
................................................................................
    * |  Jsish  +   | Script  |     *
    * +---------+<--+  File   |     *
    * |  ZVFS   |   '---------'     *
    *  '-------'                    *
    *                               *
    *********************************

Jsi comes with WebSocket and Sqlite compiled in.
Support scripts are bundled into a ZVFS appended to the binary.


Deploy
------
A [Deploy](Deploy) is a zip/sqlar archive or fossil
repository containing one or more applications, which Jsi can mount and execute.

................................................................................

Security
----
As Jsi is self contained, running it standalone in a chroot-jail needs only
a few files from /dev and /etc.   For example, this [Ledger](https://jsish.org/App01/Ledger)
demo is run in an unprivileged chroot containing a single executable (*jsish*),
with non-data files made immutable with **chattr**.
And if this is not secure enough, Jsi offers [Safe-mode](Interp#safe-mode).

In addition, serving content from a zip or fossil repository adds
another abstraction layer, making it that much harder to corrupt or hijack content.

An advantage of the chroot approach as compared with something like containers, is
that far less disk space and system memory is required.
Jsi and fossil together total around 10Meg of disk. Using hard links you

Changes to doc/md/Logging.md.

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
}
~~~~ the output
mytest.jsi:3, "INFO:  test loop: 1",
mytest.jsi:3, "INFO:  test loop: 2",
mytest.jsi:3, "INFO:  test loop: 3",
~~~~

One place this is useful is when running scripts from [geany](Start#geany), as you can navigate
through the messages just as you would compiler warnings.

The available Log commands are:

| Command  | Notes                                               |
|----------|-----------------------------------------------------|
| LogDebug | Outputs only when [logOpts.Debug](#logopts) is true |
................................................................................


Module Logging
--------------
In a larger application it may be undesirable to turn on global logging.
Which is where module-local logging comes in.

Here is a minimal [module](Develop#modules) ([mycall](../js-demos/log/mycall.jsi)):

~~~~ JS linenumbers
function mycall(args:array=void, conf:object=void) {
    var options = {};
    var self = { cnt:0 };
    parseOpts(self,options,conf);

................................................................................
mycall.jsi:6,   "DEBUG: MYLOC 1: A,B,C", mycall()
mycall.jsi:7,   "TRACE: MYLOC 2: A,B,C", mycall()
~~~~

This demonstrates that logging can be local, global or a mixture of both.

!!! NOTE
    The [parseOpts](Develop) implicitly accepts the boolean options **Debug, Trace, Test**.


More Modules
------------
Here is a more complete module [mytest1](../js-demos/log/mytest1.jsi) using parseOpts and logging:

~~~~ JS linenumbers
................................................................................
with support for arguments:

~~~~ js linenumbers
#!/usr/local/bin/jsish -T Debug %s -Trace true myinput1.txt
puts(console.args.join(' '));
~~~~

See [Shell](Start#start/shell)


Interp logOpts
--------------
The interp option [logOpts](Reference#logOptsOptions) is used to control when the above commands are
to output the current file, line and function.








|







 







|







 







|







 







|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
}
~~~~ the output
mytest.jsi:3, "INFO:  test loop: 1",
mytest.jsi:3, "INFO:  test loop: 2",
mytest.jsi:3, "INFO:  test loop: 3",
~~~~

One place this is useful is when running scripts from [geany](Install#geany), as you can navigate
through the messages just as you would compiler warnings.

The available Log commands are:

| Command  | Notes                                               |
|----------|-----------------------------------------------------|
| LogDebug | Outputs only when [logOpts.Debug](#logopts) is true |
................................................................................


Module Logging
--------------
In a larger application it may be undesirable to turn on global logging.
Which is where module-local logging comes in.

Here is a minimal [module](Coding#modules) ([mycall](../js-demos/log/mycall.jsi)):

~~~~ JS linenumbers
function mycall(args:array=void, conf:object=void) {
    var options = {};
    var self = { cnt:0 };
    parseOpts(self,options,conf);

................................................................................
mycall.jsi:6,   "DEBUG: MYLOC 1: A,B,C", mycall()
mycall.jsi:7,   "TRACE: MYLOC 2: A,B,C", mycall()
~~~~

This demonstrates that logging can be local, global or a mixture of both.

!!! NOTE
    The [parseOpts](Coding) implicitly accepts the boolean options **Debug, Trace, Test**.


More Modules
------------
Here is a more complete module [mytest1](../js-demos/log/mytest1.jsi) using parseOpts and logging:

~~~~ JS linenumbers
................................................................................
with support for arguments:

~~~~ js linenumbers
#!/usr/local/bin/jsish -T Debug %s -Trace true myinput1.txt
puts(console.args.join(' '));
~~~~

See [Shell](Install#shell)


Interp logOpts
--------------
The interp option [logOpts](Reference#logOptsOptions) is used to control when the above commands are
to output the current file, line and function.

Changes to doc/md/Misc.md.

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
...
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
~~~~ the output
[ "#Interp_1" ]
~~~~

and so on.



Web Servers
----
Although Jsi can serve web content directly, on the Internet
it is more common to use "nginx"
as a reverse proxy via localhost:

~~~~
location /App00/Ledger { proxy_pass http://localhost:8800; include proxy_params; }
~~~~

And jsish might be invoked as:

~~~~SH
jsish -a jsi-app.fossil Ledger -port 8800 -urlPrefix /App00/Ledger -noGui true
~~~~

Or run via chroot/su.


### Chroot Setup
Given it's small size, jsish is well suited for chroot deployments.
On the jsish.org site the user **jsiusr00** was created with directory contents:

~~~~SH
ls -RF jsiusr00
~~~~ the output
jsiusr00:
bin/  dev/  etc/  ledgerjs.db  tmp/  usr/

jsiusr00/bin:
fossil*  jsish*

jsiusr00/dev:
null  random  urandom

jsiusr00/etc:
hosts  resolv.conf

jsiusr00/tmp:

jsiusr00/usr:
jsi-app.fossil  jsi-app.sqlar  jsi-app.zip  ledgerjs.db
~~~~

To scale this up while saving space, multiple users "jsiusrNN" are created with
readonly files hard-linked to "jsiusr00".
Finally "chattr +i" is used make them immutable.
Thus incremental size for each additional
user is really only the data file "ledgerjs.db".

~~~~SH
du -s jsiusr*
~~~~ the output
12468   jsiusr00
980 jsiusr01
960 jsiusr02
960 jsiusr03
...
1224    jsiusr10
960 jsiuser11
...
960 jsiuser19
~~~~

All previous directories have no shell, so with the addition of quotas and ulimits we end up with a
deployment that is simple but secure.

!!! NOTE
    The slight bump in "jsiusr10" is due to the addition of "sh", to allow execing fossil in a chroot.


Syntax
----
The following syntax is implemented by Jsi (see [Reference](Reference) for commands):

~~~~
continue [IDENT] ;
break [IDENT] ;
................................................................................
### Terminals
| Name      | Description                                                                       |
|-----------|-----------------------------------------------------------------------------------|
| ARGS      | Zero or more comma-seperated arguments                                            |
| EXPR      | An expression (see below)                                                         |
| FUNC      | A function value                                                                  |
| IDENT     | Is an valid identifier                                                            |
| PRIMITIVE | A primitive value acceptable as an [argument type](Types#types/availtypes). |
| STMTS     | Is zero or more statements                                                        |
| TYPE      | A type value acceptable as [defaults](Types#types/defaults)                    |


### Keywords
Keywords can be displayed using Info.keywords():

~~~~
  "...", "any", "arguments", "array", "boolean", "break", "case", "catch",
................................................................................
control can resides in C-code.

Although Jsi was originally was based off source from [quad-wheel](https://code.google.com/archive/p/quad-wheel),
it is now internally modelled after [Tcl](https://www.tcl.tk/doc/scripting.html).

ECMA Compatibilty
----
Jsi implements version 5.1 of the
[Ecma-script 262 standard](http://www.ecma-international.org/ecma-262/5.1/Ecma-262.pdf),
with the following deviations:

- Semicolons are not auto-inserted.
- Using empty array/object elements will kick an error, eg. **[1,,3]**.
- **delete** actually deletes things, not just object properties.
- **length** works for objects, as well as arrays/strings/functions.
- The value of **typeof[]** is **"array"** instead of **"object"**.
- The **Error** object is unimplemented: the argument to **catch()** is just a string.
................................................................................
----
Following are principle goals  Jsi:

- Support embedded development using plain **C** (C99).
- But should also be compilable by **native GNU g++**, without use of *"extern C"*.
- Have as few dependencies as possible.
- Be generally free of value/object/memory leaks (verified with -fsanitize).
- Provide amalgamated source for simplified [application integration](Start#amalgamation) .
- Low-level C-functions available in a **C-only** [Lite](C-API#jsi-lite) version.
- Come with a [Debugger](Debug).
- Support Web applications, particularly with database and websockets.
- Support [standalone applications](Builtin#zvfs) via self-mounting .zip.
- [Package](Develop.md.html) and extension support.

And while compiling as C++ is supported, it is mostly used for integrity checking.

!!! NOTE
    C-integration is the main priority in Jsi, not speed of script execution.

Shortcomings
----
Following is a partial list of things that are either incomplete or unimplemented:







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|

|







 







|
|
|







 







|



|
|

|







74
75
76
77
78
79
80








































































81
82
83
84
85
86
87
...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
...
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
~~~~ the output
[ "#Interp_1" ]
~~~~

and so on.










































































Syntax
----
The following syntax is implemented by Jsi (see [Reference](Reference) for commands):

~~~~
continue [IDENT] ;
break [IDENT] ;
................................................................................
### Terminals
| Name      | Description                                                                       |
|-----------|-----------------------------------------------------------------------------------|
| ARGS      | Zero or more comma-seperated arguments                                            |
| EXPR      | An expression (see below)                                                         |
| FUNC      | A function value                                                                  |
| IDENT     | Is an valid identifier                                                            |
| PRIMITIVE | A primitive value acceptable as an [argument type](Types#typenames). |
| STMTS     | Is zero or more statements                                                        |
| TYPE      | A type value acceptable as [defaults](defaults)                    |


### Keywords
Keywords can be displayed using Info.keywords():

~~~~
  "...", "any", "arguments", "array", "boolean", "break", "case", "catch",
................................................................................
control can resides in C-code.

Although Jsi was originally was based off source from [quad-wheel](https://code.google.com/archive/p/quad-wheel),
it is now internally modelled after [Tcl](https://www.tcl.tk/doc/scripting.html).

ECMA Compatibilty
----
Jsi implements much of version 5.1 of the
[Ecma-script 262 standard](http://www.ecma-international.org/ecma-262/5.1/ECMA-262.pdf).
Notable deviations include:

- Semicolons are not auto-inserted.
- Using empty array/object elements will kick an error, eg. **[1,,3]**.
- **delete** actually deletes things, not just object properties.
- **length** works for objects, as well as arrays/strings/functions.
- The value of **typeof[]** is **"array"** instead of **"object"**.
- The **Error** object is unimplemented: the argument to **catch()** is just a string.
................................................................................
----
Following are principle goals  Jsi:

- Support embedded development using plain **C** (C99).
- But should also be compilable by **native GNU g++**, without use of *"extern C"*.
- Have as few dependencies as possible.
- Be generally free of value/object/memory leaks (verified with -fsanitize).
- Provide amalgamated source for simplified [application integration](Install#amalgamation) .
- Low-level C-functions available in a **C-only** [Lite](C-API#jsi-lite) version.
- Come with a [Debugger](Debug).
- Support Web applications, particularly with database and websockets.
- Support [standalone applications](Builtins#zvfs) via self-mounting .zip.
- [Package](Coding) and extension support.

Compiling as C++ is supported, mostly for integrity checks.

!!! NOTE
    C-integration is the main priority in Jsi, not speed of script execution.

Shortcomings
----
Following is a partial list of things that are either incomplete or unimplemented:

Changes to doc/md/MySql.md.

169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
...
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424

!!! NOTE
    Output for some modes is affected by the headers and separator options.


#### JSON
The json modes are useful
when data is destined to be sent to a web browser, eg. via [websockets](Builtin#websocket).

~~~~ js linenumbers
db.exec('DROP TABLE IF EXISTS foo; CREATE TABLE foo(a,b);');
var n = 0, x = 99;
while (n++ < 3) {
    db.query('INSERT INTO foo VALUES(@x,@n)');
    x -= 4;
................................................................................
### Differences From Sqlite
Differences include a greater dependance on types, and requiring a user, password and database.


### Building
The MySql driver does not (by default) come builtin to Jsi.

However, once you [download the source](Start) you can build it in with:

~~~~
make mysql
~~~~

Alternatively, the shared library can be built (for unix) with:

~~~~
make libmysql
~~~~







|







 







|










169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
...
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424

!!! NOTE
    Output for some modes is affected by the headers and separator options.


#### JSON
The json modes are useful
when data is destined to be sent to a web browser, eg. via [websockets](Builtins#websocket).

~~~~ js linenumbers
db.exec('DROP TABLE IF EXISTS foo; CREATE TABLE foo(a,b);');
var n = 0, x = 99;
while (n++ < 3) {
    db.query('INSERT INTO foo VALUES(@x,@n)');
    x -= 4;
................................................................................
### Differences From Sqlite
Differences include a greater dependance on types, and requiring a user, password and database.


### Building
The MySql driver does not (by default) come builtin to Jsi.

However, once you [download the source](Install) you can build it in with:

~~~~
make mysql
~~~~

Alternatively, the shared library can be built (for unix) with:

~~~~
make libmysql
~~~~

Changes to doc/md/Reference.md.

1
2
3
4
5
6
7
8
9
10
...
417
418
419
420
421
422
423

424
425
426
427
428
429
430
...
609
610
611
612
613
614
615

616
617
618
619
620
621
622
....
1321
1322
1323
1324
1325
1326
1327

1328
1329
1330
1331
1332
1333
1334
<title>Reference</title>
<p>
<B>JSI REFERENCE</B> (See <a href="#System">System</a> for globals)
<a name="Array"></a>

<hr>


<h1>Array</h1>

................................................................................
<tr><td>isdir</td><td>isdir(file:string):boolean </td><td>Return true if file is a directory.</td></tr>
<tr><td>isfile</td><td>isfile(file:string):boolean </td><td>Return true if file is a normal file.</td></tr>
<tr><td>isrelative</td><td>isrelative(file:string):boolean </td><td>Return true if file path is relative.</td></tr>
<tr><td>join</td><td>join(path:string, path:string):string </td><td>Join two file realpaths, or just second if an absolute path.</td></tr>
<tr><td>link</td><td>link(src:string, dest:string, ishard:boolean=false) </td><td>Link a file. The second argument is the destination file to be created. If a third bool argument is true, a hard link is created.</td></tr>
<tr><td>lstat</td><td>lstat(file:string):object </td><td>Return status info for file.</td></tr>
<tr><td>mkdir</td><td>mkdir(file:string) </td><td>Create a directory.</td></tr>

<tr><td>mtime</td><td>mtime(file:string):number </td><td>Return file modified time.</td></tr>
<tr><td>owned</td><td>owned(file:string):boolean </td><td>Return true if file is owned by user.</td></tr>
<tr><td>pwd</td><td>pwd():string </td><td>Return current directory.</td></tr>
<tr><td>read</td><td>read(file:string, mode:string='rb'):string </td><td>Read a file.</td></tr>
<tr><td>readable</td><td>readable(file:string):boolean </td><td>Return true if file is readable.</td></tr>
<tr><td>readlink</td><td>readlink(file:string):string </td><td>Read file link destination.</td></tr>
<tr><td>realpath</td><td>realpath(file:string):string </td><td>Return absolute file name minus .., ./ etc.</td></tr>
................................................................................
<tr><td>onEval</td><td><i>FUNC</i></td><td>Function to get control for interactive evals. @function(cmd:string)</td><td><i></i></td></tr>
<tr><td>onExit</td><td><i>FUNC</i></td><td>Command to call in parent on exit, returns true to continue. @function()</td><td><i>initOnly</i></td></tr>
<tr><td>opTrace</td><td><i>INT</i></td><td>Set debugging level for OPCODE execution.</td><td><i></i></td></tr>
<tr><td>pkgDirs</td><td><i>ARRAY</i></td><td>list of library directories for require() to search.</td><td><i></i></td></tr>
<tr><td>profile</td><td><i>BOOL</i></td><td>On exit generate profile of function calls.</td><td><i></i></td></tr>
<tr><td>recvCallback</td><td><i>CUSTOM</i></td><td>Command to recv 'send' msgs from parent interp.</td><td><i></i></td></tr>
<tr><td>retValue</td><td><i>VALUE</i></td><td>Return value from last eval.</td><td><i>readOnly</i></td></tr>

<tr><td>safeReadDirs</td><td><i>ARRAY</i></td><td>In safe mode, files/dirs to allow reads from.</td><td><i>initOnly</i></td></tr>
<tr><td>safeWriteDirs</td><td><i>ARRAY</i></td><td>In safe mode, files/dirs to allow writes to.</td><td><i>initOnly</i></td></tr>
<tr><td>safeExecPattern</td><td><i>STRKEY</i></td><td>In safe mode, regexp pattern allow exec of commands.</td><td><i>initOnly</i></td></tr>
<tr><td>scriptStr</td><td><i>STRKEY</i></td><td>Interp init script string.</td><td><i>initOnly</i></td></tr>
<tr><td>scriptFile</td><td><i>STRING</i></td><td>Interp init script file.</td><td><i></i></td></tr>
<tr><td>stdinStr</td><td><i>STRING</i></td><td>String to use as stdin for console.input().</td><td><i></i></td></tr>
<tr><td>stdoutStr</td><td><i>STRING</i></td><td>String to collect stdout for puts().</td><td><i></i></td></tr>
................................................................................
<table border="1" class="optstbl table">
<tr><th>Option</th> <th>Type</th> <th>Description</th><th>Flags</th></tr>
<tr><td>bg</td><td><i>BOOL</i></td><td>Run command in background using system() and return OS code.</td><td><i></i></td></tr>
<tr><td>chdir</td><td><i>STRING</i></td><td>Change to directory.</td><td><i></i></td></tr>
<tr><td>inputStr</td><td><i>STRING</i></td><td>Use string as input and return OS code.</td><td><i></i></td></tr>
<tr><td>noError</td><td><i>BOOL</i></td><td>Suppress all OS errors.</td><td><i></i></td></tr>
<tr><td>noRedir</td><td><i>BOOL</i></td><td>Disable redirect and shell escapes in command.</td><td><i></i></td></tr>

<tr><td>trim</td><td><i>BOOL</i></td><td>Trim trailing whitespace from output.</td><td><i></i></td></tr>
<tr><td>retAll</td><td><i>BOOL</i></td><td>Return the OS return code and data as an object.</td><td><i></i></td></tr>
<tr><td>retCode</td><td><i>BOOL</i></td><td>Return only the OS return code.</td><td><i></i></td></tr>
</table>


<a name="System.sourceOptions"></a>


|







 







>







 







>







 







>







1
2
3
4
5
6
7
8
9
10
...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
...
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
....
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
<title>Reference</title>
<p>
<B>JSI REFERENCE</B>: <a href="#System">System</a> contains global methods
<a name="Array"></a>

<hr>


<h1>Array</h1>

................................................................................
<tr><td>isdir</td><td>isdir(file:string):boolean </td><td>Return true if file is a directory.</td></tr>
<tr><td>isfile</td><td>isfile(file:string):boolean </td><td>Return true if file is a normal file.</td></tr>
<tr><td>isrelative</td><td>isrelative(file:string):boolean </td><td>Return true if file path is relative.</td></tr>
<tr><td>join</td><td>join(path:string, path:string):string </td><td>Join two file realpaths, or just second if an absolute path.</td></tr>
<tr><td>link</td><td>link(src:string, dest:string, ishard:boolean=false) </td><td>Link a file. The second argument is the destination file to be created. If a third bool argument is true, a hard link is created.</td></tr>
<tr><td>lstat</td><td>lstat(file:string):object </td><td>Return status info for file.</td></tr>
<tr><td>mkdir</td><td>mkdir(file:string) </td><td>Create a directory.</td></tr>
<tr><td>mknod</td><td>mknod(file:string, mode:number, dev:number) </td><td>Create unix device file using mknod.</td></tr>
<tr><td>mtime</td><td>mtime(file:string):number </td><td>Return file modified time.</td></tr>
<tr><td>owned</td><td>owned(file:string):boolean </td><td>Return true if file is owned by user.</td></tr>
<tr><td>pwd</td><td>pwd():string </td><td>Return current directory.</td></tr>
<tr><td>read</td><td>read(file:string, mode:string='rb'):string </td><td>Read a file.</td></tr>
<tr><td>readable</td><td>readable(file:string):boolean </td><td>Return true if file is readable.</td></tr>
<tr><td>readlink</td><td>readlink(file:string):string </td><td>Read file link destination.</td></tr>
<tr><td>realpath</td><td>realpath(file:string):string </td><td>Return absolute file name minus .., ./ etc.</td></tr>
................................................................................
<tr><td>onEval</td><td><i>FUNC</i></td><td>Function to get control for interactive evals. @function(cmd:string)</td><td><i></i></td></tr>
<tr><td>onExit</td><td><i>FUNC</i></td><td>Command to call in parent on exit, returns true to continue. @function()</td><td><i>initOnly</i></td></tr>
<tr><td>opTrace</td><td><i>INT</i></td><td>Set debugging level for OPCODE execution.</td><td><i></i></td></tr>
<tr><td>pkgDirs</td><td><i>ARRAY</i></td><td>list of library directories for require() to search.</td><td><i></i></td></tr>
<tr><td>profile</td><td><i>BOOL</i></td><td>On exit generate profile of function calls.</td><td><i></i></td></tr>
<tr><td>recvCallback</td><td><i>CUSTOM</i></td><td>Command to recv 'send' msgs from parent interp.</td><td><i></i></td></tr>
<tr><td>retValue</td><td><i>VALUE</i></td><td>Return value from last eval.</td><td><i>readOnly</i></td></tr>
<tr><td>safeMode</td><td><i>STRKEY</i></td><td>Set isSafe mode and setup safeReadDirs and/or safeWriteDirs for pwd and script-dir. (one of: <b>none</b>, <b>read</b>, <b>write</b>, <b>write2</b>)</td><td><i>initOnly</i></td></tr>
<tr><td>safeReadDirs</td><td><i>ARRAY</i></td><td>In safe mode, files/dirs to allow reads from.</td><td><i>initOnly</i></td></tr>
<tr><td>safeWriteDirs</td><td><i>ARRAY</i></td><td>In safe mode, files/dirs to allow writes to.</td><td><i>initOnly</i></td></tr>
<tr><td>safeExecPattern</td><td><i>STRKEY</i></td><td>In safe mode, regexp pattern allow exec of commands.</td><td><i>initOnly</i></td></tr>
<tr><td>scriptStr</td><td><i>STRKEY</i></td><td>Interp init script string.</td><td><i>initOnly</i></td></tr>
<tr><td>scriptFile</td><td><i>STRING</i></td><td>Interp init script file.</td><td><i></i></td></tr>
<tr><td>stdinStr</td><td><i>STRING</i></td><td>String to use as stdin for console.input().</td><td><i></i></td></tr>
<tr><td>stdoutStr</td><td><i>STRING</i></td><td>String to collect stdout for puts().</td><td><i></i></td></tr>
................................................................................
<table border="1" class="optstbl table">
<tr><th>Option</th> <th>Type</th> <th>Description</th><th>Flags</th></tr>
<tr><td>bg</td><td><i>BOOL</i></td><td>Run command in background using system() and return OS code.</td><td><i></i></td></tr>
<tr><td>chdir</td><td><i>STRING</i></td><td>Change to directory.</td><td><i></i></td></tr>
<tr><td>inputStr</td><td><i>STRING</i></td><td>Use string as input and return OS code.</td><td><i></i></td></tr>
<tr><td>noError</td><td><i>BOOL</i></td><td>Suppress all OS errors.</td><td><i></i></td></tr>
<tr><td>noRedir</td><td><i>BOOL</i></td><td>Disable redirect and shell escapes in command.</td><td><i></i></td></tr>
<tr><td>noShell</td><td><i>BOOL</i></td><td>Do not use native popen which invokes via /bin/sh.</td><td><i></i></td></tr>
<tr><td>trim</td><td><i>BOOL</i></td><td>Trim trailing whitespace from output.</td><td><i></i></td></tr>
<tr><td>retAll</td><td><i>BOOL</i></td><td>Return the OS return code and data as an object.</td><td><i></i></td></tr>
<tr><td>retCode</td><td><i>BOOL</i></td><td>Return only the OS return code.</td><td><i></i></td></tr>
</table>


<a name="System.sourceOptions"></a>

Changes to doc/md/Sqlite.md.

161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

!!! NOTE
    Output for some modes is affected by the headers and separator options.


#### JSON
The json modes are useful
when data is destined to be sent to a web browser, eg. via [websockets](Builtin#websocket).

~~~~
db.exec('DROP TABLE IF EXISTS foo; CREATE TABLE foo(a,b);');
var n = 0, x = 99;
while (n++ < 3) {
    db.query('INSERT INTO foo VALUES(@x,@n)');
    x -= 4;







|







161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

!!! NOTE
    Output for some modes is affected by the headers and separator options.


#### JSON
The json modes are useful
when data is destined to be sent to a web browser, eg. via [websockets](Builtins#websocket).

~~~~
db.exec('DROP TABLE IF EXISTS foo; CREATE TABLE foo(a,b);');
var n = 0, x = 99;
while (n++ < 3) {
    db.query('INSERT INTO foo VALUES(@x,@n)');
    x -= 4;

Changes to lib/Jsi_GenDeep.jsi.

35
36
37
38
39
40
41







42
43
44
45
46
47
48
..
56
57
58
59
60
61
62
63


64
65
66
67
68
69
70
        if (!File.exists(fh))
            links.push([fh, str, self.curFile]);
        var sfx = vals[2];
        if (!sfx) sfx = '';
        return '<a href="'+fh+sfx+'">';
    }








    function DeepFileSave(ws:userobj, id:number, data:object) {
        if (data.cmd === 'save') {
            var file = File.tail(data.url);
            file = file.split('?')[0];
            file = File.rootname(file)+self.extn;
            var ndat = data.data;
            //ndat = ndat.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ' ');
................................................................................
                    ndat = ndat.substr(0,nend-1)+'</div></div><p></p>&nbsp;<p></p>';
            }
            ndat += '<style>body{visibility:visible;}</style>';
            self.curFile = file;
            var base = '';
            if (self.outDir != '.')
                base = '<base href="..">';
            ndat = base'+ndat.replace(/<a href="[-a-zA-Z1-9_]+(#[-a-zA-Z1-9_\/]+)?">/gm, repHref);


            File.write(self.outDir+'/'+file, ndat);
            puts('Saved file',file);
            if (!self.fileList.length) {
                ws.send(id, 'DONE!!!');
                checkLinks();
                setTimeout(function () { puts('DONE!!!'); exit(0); }, 1000);
                return;







>
>
>
>
>
>
>







 







|
>
>







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
..
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
        if (!File.exists(fh))
            links.push([fh, str, self.curFile]);
        var sfx = vals[2];
        if (!sfx) sfx = '';
        return '<a href="'+fh+sfx+'">';
    }

    function repHref2(str:string) {
        var reg = /^<a href="(#[-a-zA-Z1-9_\/]+)"$/;
        var vals = reg.exec(str);
        var fnam = vals[1];
        return '<a href="'+self.outDir+'/'+self.curFile +fnam+'"';
    }
    
    function DeepFileSave(ws:userobj, id:number, data:object) {
        if (data.cmd === 'save') {
            var file = File.tail(data.url);
            file = file.split('?')[0];
            file = File.rootname(file)+self.extn;
            var ndat = data.data;
            //ndat = ndat.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, ' ');
................................................................................
                    ndat = ndat.substr(0,nend-1)+'</div></div><p></p>&nbsp;<p></p>';
            }
            ndat += '<style>body{visibility:visible;}</style>';
            self.curFile = file;
            var base = '';
            if (self.outDir != '.')
                base = '<base href="..">';
            ndat = base+ndat.replace(/<a href="[-a-zA-Z1-9_]+(#[-a-zA-Z1-9_\/]+)?">/gm, repHref);
            if (self.outDir != '.')
                ndat = ndat.replace(/<a href="#[-a-zA-Z1-9_\/]+"/gm, repHref2);
            File.write(self.outDir+'/'+file, ndat);
            puts('Saved file',file);
            if (!self.fileList.length) {
                ws.send(id, 'DONE!!!');
                checkLinks();
                setTimeout(function () { puts('DONE!!!'); exit(0); }, 1000);
                return;

Changes to lib/Jsi_Help.jsi.

344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
            throw('expected 0 or 1 args');
        var rv, arg = (args.length?args[0]:'');
        if (self.web) {
            rv = dumpWeb();
            if (!self.wiki) {
                if (require('Jsi_Websrv')<0)
                    throw("websrv unavailable");
                var css = File.read('/zvfs/lib/web/Websrv.css');
                rv = '<style>'+css+'</style>\n' + rv;
                var anchor = (arg===''?'':'#'+arg);
                if (System[arg])
                    anchor = '#System';
                else if (arg.indexOf('.')>0)
                    anchor = '#'+arg.split('.')[0];
                Jsi_Websrv([], {pageStr:rv, anchor:anchor, timeout:self.timeout});







|







344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
            throw('expected 0 or 1 args');
        var rv, arg = (args.length?args[0]:'');
        if (self.web) {
            rv = dumpWeb();
            if (!self.wiki) {
                if (require('Jsi_Websrv')<0)
                    throw("websrv unavailable");
                var css = File.read('/zvfs/lib/web/jsiweb.css');
                rv = '<style>'+css+'</style>\n' + rv;
                var anchor = (arg===''?'':'#'+arg);
                if (System[arg])
                    anchor = '#System';
                else if (arg.indexOf('.')>0)
                    anchor = '#'+arg.split('.')[0];
                Jsi_Websrv([], {pageStr:rv, anchor:anchor, timeout:self.timeout});

Changes to lib/Jsi_Safe.jsi.

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    
    function main() {
        LogDebug('Starting');
        debugger;
        if (!self.rootdir)
            self.rootdir=Info.scriptDir();
        self.args = [];
        var scr = null,
            fn = self.srcFile = fileargs[0];
        if (fn === undefined)
            fn = '.';
        fn = File.realpath(fn);
        self.args = fileargs.splice(1);
        LogDebug('ARGS:', self.args);
        if (!File.exists(fn))
            throw("script file does not exist: "+fn);







<
|







43
44
45
46
47
48
49

50
51
52
53
54
55
56
57
    
    function main() {
        LogDebug('Starting');
        debugger;
        if (!self.rootdir)
            self.rootdir=Info.scriptDir();
        self.args = [];

        var scr = null, fn = self.srcFile = fileargs[0];
        if (fn === undefined)
            fn = '.';
        fn = File.realpath(fn);
        self.args = fileargs.splice(1);
        LogDebug('ARGS:', self.args);
        if (!File.exists(fn))
            throw("script file does not exist: "+fn);

Changes to lib/Jsi_SqliteUI/Jsi_SqliteUI.jsi.

590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
            rootdir:self.rootdir, 
            onRecv:WsRecv,
            onOpen:WsOpen,
            onCloseLast:CloseLast,
            debug:self.wsdebug,
            extHandlers:true,
            urlRedirect:self.urlPrefix+'/html/main.htmli',
            urlPrefix:'/SqliteUI'
        };
        if (self.local)
            self.wsopts.noCompress = true;
        if (self.server) {
            self.closeTimeout = 0;
            self.noGui = true;
        }







|







590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
            rootdir:self.rootdir, 
            onRecv:WsRecv,
            onOpen:WsOpen,
            onCloseLast:CloseLast,
            debug:self.wsdebug,
            extHandlers:true,
            urlRedirect:self.urlPrefix+'/html/main.htmli',
            urlPrefix:self.urlPrefix
        };
        if (self.local)
            self.wsopts.noCompress = true;
        if (self.server) {
            self.closeTimeout = 0;
            self.noGui = true;
        }

Changes to lib/Jsi_SqliteUI/html/dialog.html.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
..
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
..
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
...
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
...
179
180
181
182
183
184
185
186
187
188
189
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmitTVals("Update")'>Update</button></td>
      <td><button onclick='DlgSubmitTVals("Duplicate")'>Duplicate</button></td>
      <td><button onclick='DlgSubmitTVals("Delete")'>Delete</button></td>
      <td><button onclick='DlgSubmitTVals("Cancel")'>Cancel</button></td>
  </tr></table>
</div>

<div id="dlg_add_tvals" style="display:none" class="dlg">
  <center><h3>Add Row</h3></center>
  <table class=dlgtbl>
  <thead>
................................................................................
  <tbody>

  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_add","Add")'>Add</button></td>
      <td><button onclick='DlgSubmit("tool_cancel","Cancel")'>Cancel</button></td>
  </tr></table>
</div>


<!-- The first step is to set the <tbody> to display: block so an overflow and height can be applied.
 From there the rows in the <thead> need to be set to position: relative and display: block 
 so that they’ll sit on top of the now scrollable <tbody>.
................................................................................
  <tbody style="display:block; overflow:auto; height=400px; position:fixed">

  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_files","Ok")'>Ok</button></td>
      <td><button onclick='DlgSubmit("tool_files","Cancel")'>Cancel</button></td>
  </tr></table>
</div>
-->

<div id="dlg_tool_files" style="display:none" class="dlg">
  <center><h3>File Selection</h3></center>
  <table class=tlisttbl id=tblfiles onclick='ClickFList(event)' style="border:1px; height=500px">
................................................................................
  <tbody>

  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_files","Ok")'>Ok</button></td>
      <td><button onclick='DlgSubmit("tool_files","Cancel")'>Cancel</button></td>
  </tr></table>
</div>

<div id="dlg_tool_import" style="display:none" class="dlg">
  <center><h3>Import File</h3></center>
  <table class="dlgtbl tooltbl">
  <tbody>
................................................................................
        <tr><td class=right>Null:</td><td> <input id=parm_tool_import_null></td></tr>
        <tr><td class=right>Limit:</td><td> <input id=parm_tool_import_limit></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_import","Import")'>OK</button></td>
      <td><button onclick='DlgSubmit("tool_import","Cancel")'>Cancel</button></td>
  </tr></table>
</div>

<div id="dlg_tool_export" style="display:none" class="dlg">
  <center><h3 title="Dialog to export table to a file">Export To File</h3></center>
  <table class="dlgtbl tooltbl">
  <tbody>
................................................................................
        <tr><td class=right title="Prefix output with create table statement (for mode 'insert')">Add CREATE TABLE:</td>
            <td> <input type=checkbox id=parm_tool_export_schema value=0 onclick='ToggleMe(this)'></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_export","Export")'>OK</button></td>
      <td><button onclick='DlgSubmit("tool_export","Cancel")'>Cancel</button></td>
  </tr></table>
</div>


<div id="dlg_tool_backup" style="display:none" class="dlg">
  <center><h3>Backup DB</h3></center>
  <table class="dlgtbl tooltbl">
................................................................................
        <tr><td class=right>File:</td><td> <input id=parm_tool_backup_file></td></tr>
        <tr><td class=right>DB:</td><td><input id=parm_tool_backup_db value=main></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_backup","Backup")'>OK</button></td>
      <td><button onclick='DlgSubmit("tool_backup", "Cancel")'>Cancel</button></td>
  </tr></table>
</div>


<div id="dlg_tool_restore" style="display:none" class="dlg">
  <center><h3>Restore DB</h3></center>
  <table class="dlgtbl tooltbl">
................................................................................
        <tr><td class=right>File:</td><td> <input id=parm_tool_restore_file></td></tr>
        <tr><td class=right>DB:</td><td><input id=parm_tool_restore_db value=main></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_restore","Restore")'>OK</button></td>
      <td><button onclick='DlgSubmit("tool_restore","Cancel")'>Cancel</button></td>
  </tr></table>
</div>


<div id="dlg_tool_read" style="display:none" class="dlg">
  <center><h3 title="Evaluate an SQL file">SQL File</h3></center>
  <table class="dlgtbl tooltbl">
................................................................................
        <tr><td class=right title="Wrap output in BEGIN/COMMIT for faster loads (for mode 'insert')">Add BEGIN/COMMIT:</td>
            <td><input type=checkbox id=parm_tool_read_begin value=0 onclick='ToggleMe(this)'></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_read","Read")'>OK</button></td>
      <td><button onclick='DlgSubmit("tool_read","Cancel")'>Cancel</button></td>
  </tr></table>
</div>








<







 







<







 







<







 







<







 







<







 







<







 







<







 







<







 







<



10
11
12
13
14
15
16

17
18
19
20
21
22
23
..
25
26
27
28
29
30
31

32
33
34
35
36
37
38
..
46
47
48
49
50
51
52

53
54
55
56
57
58
59
..
63
64
65
66
67
68
69

70
71
72
73
74
75
76
..
93
94
95
96
97
98
99

100
101
102
103
104
105
106
...
125
126
127
128
129
130
131

132
133
134
135
136
137
138
...
140
141
142
143
144
145
146

147
148
149
150
151
152
153
...
155
156
157
158
159
160
161

162
163
164
165
166
167
168
...
171
172
173
174
175
176
177

178
179
180
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmitTVals("Update")'>Update</button></td>
      <td><button onclick='DlgSubmitTVals("Duplicate")'>Duplicate</button></td>
      <td><button onclick='DlgSubmitTVals("Delete")'>Delete</button></td>

  </tr></table>
</div>

<div id="dlg_add_tvals" style="display:none" class="dlg">
  <center><h3>Add Row</h3></center>
  <table class=dlgtbl>
  <thead>
................................................................................
  <tbody>

  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_add","Add")'>Add</button></td>

  </tr></table>
</div>


<!-- The first step is to set the <tbody> to display: block so an overflow and height can be applied.
 From there the rows in the <thead> need to be set to position: relative and display: block 
 so that they’ll sit on top of the now scrollable <tbody>.
................................................................................
  <tbody style="display:block; overflow:auto; height=400px; position:fixed">

  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_files","Ok")'>Ok</button></td>

  </tr></table>
</div>
-->

<div id="dlg_tool_files" style="display:none" class="dlg">
  <center><h3>File Selection</h3></center>
  <table class=tlisttbl id=tblfiles onclick='ClickFList(event)' style="border:1px; height=500px">
................................................................................
  <tbody>

  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_files","Ok")'>Ok</button></td>

  </tr></table>
</div>

<div id="dlg_tool_import" style="display:none" class="dlg">
  <center><h3>Import File</h3></center>
  <table class="dlgtbl tooltbl">
  <tbody>
................................................................................
        <tr><td class=right>Null:</td><td> <input id=parm_tool_import_null></td></tr>
        <tr><td class=right>Limit:</td><td> <input id=parm_tool_import_limit></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_import","Import")'>OK</button></td>

  </tr></table>
</div>

<div id="dlg_tool_export" style="display:none" class="dlg">
  <center><h3 title="Dialog to export table to a file">Export To File</h3></center>
  <table class="dlgtbl tooltbl">
  <tbody>
................................................................................
        <tr><td class=right title="Prefix output with create table statement (for mode 'insert')">Add CREATE TABLE:</td>
            <td> <input type=checkbox id=parm_tool_export_schema value=0 onclick='ToggleMe(this)'></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_export","Export")'>OK</button></td>

  </tr></table>
</div>


<div id="dlg_tool_backup" style="display:none" class="dlg">
  <center><h3>Backup DB</h3></center>
  <table class="dlgtbl tooltbl">
................................................................................
        <tr><td class=right>File:</td><td> <input id=parm_tool_backup_file></td></tr>
        <tr><td class=right>DB:</td><td><input id=parm_tool_backup_db value=main></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_backup","Backup")'>OK</button></td>

  </tr></table>
</div>


<div id="dlg_tool_restore" style="display:none" class="dlg">
  <center><h3>Restore DB</h3></center>
  <table class="dlgtbl tooltbl">
................................................................................
        <tr><td class=right>File:</td><td> <input id=parm_tool_restore_file></td></tr>
        <tr><td class=right>DB:</td><td><input id=parm_tool_restore_db value=main></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_restore","Restore")'>OK</button></td>

  </tr></table>
</div>


<div id="dlg_tool_read" style="display:none" class="dlg">
  <center><h3 title="Evaluate an SQL file">SQL File</h3></center>
  <table class="dlgtbl tooltbl">
................................................................................
        <tr><td class=right title="Wrap output in BEGIN/COMMIT for faster loads (for mode 'insert')">Add BEGIN/COMMIT:</td>
            <td><input type=checkbox id=parm_tool_read_begin value=0 onclick='ToggleMe(this)'></td></tr>
  </tbody>
  </table>

  <table class=dlgbuts><tr style="text-align:center">
      <td><button onclick='DlgSubmit("tool_read","Read")'>OK</button></td>

  </tr></table>
</div>

Changes to lib/Jsi_SqliteUI/html/sqlite.cssi.

129
130
131
132
133
134
135
















.shade {
    background:silver;
    opacity:0.5;
    filter:alpha'(opacity=50)';
    pointer-events:none;
    overflow:hidden;
}























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
.shade {
    background:silver;
    opacity:0.5;
    filter:alpha'(opacity=50)';
    pointer-events:none;
    overflow:hidden;
}

.close-icon
{
  display:block;
  box-sizing:border-box;
  width:20px;
  height:20px;
  border-width:3px;
  border-style: solid;
  border-color:red;
  border-radius:100%;
  background: -webkit-linear-gradient(-45deg, transparent 0%, transparent 46%, white 46%,  white 56%,transparent 56%, transparent 100%), -webkit-linear-gradient(45deg, transparent 0%, transparent 46%, white 46%,  white 56%,transparent 56%, transparent 100%);
  background-color:red;
  box-shadow:0px 0px 5px 2px rgba(0,0,0,0.5);
  transition: all 0.3s ease;
}

Changes to lib/Jsi_SqliteUI/html/sqlite.jsi.

371
372
373
374
375
376
377






378
379
380
381
382
383
384
        //document.body.style.overflow = "hidden";
        $$('#'+fend).classList.add('shade');
        self.frames.push(name);
        self.escFunc = function () { DlgOpen(name, false); };
        var z = $('input', id);
        if (z && z[0])
            z[0].focus();






    } else {
        if (fend != name) return;
        $$('#'+name).setAttribute('style','display:none');
        self.frames.pop();
        fend = self.frames[self.frames.length-1];
        $$('#'+fend).classList.remove('shade');
        if (self.frames.length<=1)







>
>
>
>
>
>







371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
        //document.body.style.overflow = "hidden";
        $$('#'+fend).classList.add('shade');
        self.frames.push(name);
        self.escFunc = function () { DlgOpen(name, false); };
        var z = $('input', id);
        if (z && z[0])
            z[0].focus();
        if (id.firstElementChild.className !== 'close-icon') {
            var ct = document.createElement('button');
            ct.onclick = self.escFunc;
            ct.className='close-icon';
            id.insertBefore(ct, id.firstElementChild);
        }
    } else {
        if (fend != name) return;
        $$('#'+name).setAttribute('style','display:none');
        self.frames.pop();
        fend = self.frames[self.frames.length-1];
        $$('#'+fend).classList.remove('shade');
        if (self.frames.length<=1)

Name change from lib/web/dumpdeep.js to lib/web/deepdoc.js.

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



// Script to dump markdeep export via websocket.
console.log('dumpdeep.js');

function DeepDumpWs() {
    console.log('DeepDumpWsTO');
    if (document.URL.indexOf("?export=save")<0) return;

    var url = document.URL.replace(/^http/,"ws");
    var ws = new WebSocket(url, "ws");
    ws.onopen = function() {
        var data = document.querySelectorAll("body pre");
        if (!data[0]) data = document.querySelectorAll("body code");
        if (!data[0]) return;
        data = data[0].innerText;
        console.log('DATA '+data);
        clearInterval(DeepDumpWsTO);
        ws.send(JSON.stringify({cmd:"save", url:url, data:data}));
    };
    ws.onmessage = function(msg) {
        if (msg.data !== 'DONE!!!')
            document.location = msg.data+"?export=save";
        else
            document.body.innerHTML = '<style>body{background:#ddd;}</style>DOWNLOAD COMPLETE: PLEASE CLOSE THIS TAB';
    };
};


















window.onload = DeepDumpWs;
var DeepDumpWsTO = setTimeout(DeepDumpWs, 10000);




|
>

<

>








|









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
>
>
>
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
// Script to dump markdeep export via websocket.
(function () {

function DeepDumpWs() {

    if (document.URL.indexOf("?export=save")<0) return;
    console.log('DeepDumpWs');
    var url = document.URL.replace(/^http/,"ws");
    var ws = new WebSocket(url, "ws");
    ws.onopen = function() {
        var data = document.querySelectorAll("body pre");
        if (!data[0]) data = document.querySelectorAll("body code");
        if (!data[0]) return;
        data = data[0].innerText;
        console.log('DATA '+data);
        //clearInterval(DeepDumpWsTO);
        ws.send(JSON.stringify({cmd:"save", url:url, data:data}));
    };
    ws.onmessage = function(msg) {
        if (msg.data !== 'DONE!!!')
            document.location = msg.data+"?export=save";
        else
            document.body.innerHTML = '<style>body{background:#ddd;}</style>DOWNLOAD COMPLETE: PLEASE CLOSE THIS TAB';
    };
};

function DeepDocCmd() {
    document.title=location.pathname.match(/\/([-\w]+)[^\/]*$/)[1];
    if (location.hash !== '') setTimeout(function() { location.href=location.href; console.log(location.href);}, 10);
    DeepDumpWs();
}

if(window.attachEvent) {
    window.attachEvent('onload', DeepDocCmd);
} else {
    if(window.onload) {
        var curronload = window.onload;
        var newonload = function(evt) {
            curronload(evt);
            DeepDocCmd(evt);
        };
        window.onload = newonload;
    } else {
        window.onload = DeepDocCmd;

    }
}
})();

Name change from lib/web/Websrv.css to lib/web/jsiweb.css.

217
218
219
220
221
222
223
















      padding: 0 .5em;
      margin-right: .5em;
      width:2em;
      color: #888
    }
  }
}























>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
      padding: 0 .5em;
      margin-right: .5em;
      width:2em;
      color: #888
    }
  }
}

.close-icon
{
  display:block;
  box-sizing:border-box;
  width:20px;
  height:20px;
  border-width:3px;
  border-style: solid;
  border-color:red;
  border-radius:100%;
  background: -webkit-linear-gradient(-45deg, transparent 0%, transparent 46%, white 46%,  white 56%,transparent 56%, transparent 100%), -webkit-linear-gradient(45deg, transparent 0%, transparent 46%, white 46%,  white 56%,transparent 56%, transparent 100%);
  background-color:red;
  box-shadow:0px 0px 5px 2px rgba(0,0,0,0.5);
  transition: all 0.3s ease;
}

Changes to lib/web/jsiweb.js.

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152









153
154
155
156
157
                    case Object: return "object";
                    default: return "userobj";
                }
            default: return "any";
        }
    },
    
    SaveFile(filename, data, mime) {
        var blob = new Blob([data], {type: (mime?mime:'text/html')});
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var elem = window.document.createElement('a');
            elem.href = window.URL.createObjectURL(blob);
            elem.download = filename;
            document.body.appendChild(elem);
            elem.click();
            document.body.removeChild(elem);
        }









    }
};

var Jsi = new __Jsi();








|











>
>
>
>
>
>
>
>
>





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
                    case Object: return "object";
                    default: return "userobj";
                }
            default: return "any";
        }
    },
    
    SaveFile: function(filename, data, mime) {
        var blob = new Blob([data], {type: (mime?mime:'text/html')});
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var elem = window.document.createElement('a');
            elem.href = window.URL.createObjectURL(blob);
            elem.download = filename;
            document.body.appendChild(elem);
            elem.click();
            document.body.removeChild(elem);
        }
    },
    
    AddClose: function(id, escFunc) {
        if (id.firstElementChild.className !== 'close-icon') {
            var ct = document.createElement('button');
            ct.onclick = escFunc;
            ct.className='close-icon';
            id.insertBefore(ct, id.firstElementChild);
        }
    }
};

var Jsi = new __Jsi();

Added lib/web/markdeep.js.

cannot compute difference between binary files

Deleted lib/web/markdeep.min.js.

cannot compute difference between binary files

Changes to lib/web/markdeep/include.shtml.

1
2
3
4
5
6
7
8
9
10
11
<style class="fallback">body{visibility:hidden;}</style>
<script>window.markdeepOptions={tocStyle:"medium"}</script>

<!--#include file="$md"-->

<link rel="stylesheet" href="jsistyle.css" type="text/css" media="screen" />
<!-- Markdeep: --><style class="fallback">body{visibility:hidden;}</style><script src="markdeep.min.js"></script>
<script>var startOfMarkDeep=true; window.alreadyProcessedMarkdeep||(document.body.style.visibility='visible'); document.title=location.pathname.match(/\/([\w]+)[^\/]*$/)[1];</script>
<script src="dumpdeep.js"></script>



|




|
|
|


1
2
3
4
5
6
7
8
9
10
11
<style class="fallback">body{visibility:hidden;}</style>
<script>window.markdeepOptions={tocStyle:"medium", onLoad:function(){DeepDocCmd();}}</script>

<!--#include file="$md"-->

<link rel="stylesheet" href="jsistyle.css" type="text/css" media="screen" />
<!-- Markdeep: --><style class="fallback">body{visibility:hidden;}</style><script src="markdeep.js"></script>
<script>var startOfMarkDeep=true; window.alreadyProcessedMarkdeep||(document.body.style.visibility='visible'); </script>
<script src="deepdoc.js"></script>


Changes to src/jsi.c.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
...
583
584
585
586
587
588
589

590
591
592
593
594
595
596
.....
13373
13374
13375
13376
13377
13378
13379







13380
13381
13382
13383
13384


13385
13386
13387
13388
13389
13390
13391
.....
16134
16135
16136
16137
16138
16139
16140
16141



16142

16143
16144
16145
16146
16147
16148
16149
.....
16293
16294
16295
16296
16297
16298
16299

16300
16301
16302
16303
16304
16305
16306
16307
16308
.....
18493
18494
18495
18496
18497
18498
18499

18500
18501
18502
18503
18504
18505
18506
.....
18534
18535
18536
18537
18538
18539
18540
18541

18542
18543
18544
18545
18546
18547
18548
.....
19066
19067
19068
19069
19070
19071
19072


19073
19074
19075
19076
19077
19078
19079
.....
19090
19091
19092
19093
19094
19095
19096
19097
19098
19099
19100
19101
19102
19103
19104
19105
19106
19107
19108
19109
19110
19111
19112
19113
19114
19115

19116








19117
19118

19119
19120
19121
19122
19123
19124
19125

19126
19127
19128
19129
19130
19131
19132
19133
19134
19135
19136
19137



19138
19139
19140
19141



19142
19143
19144
19145







19146
19147
19148
19149
19150
19151
19152
19153

19154
19155
19156
19157

19158












19159
19160
19161
19162

19163


19164


19165
19166
19167
19168
19169
19170
19171
19172
19173
19174


19175
19176


19177
19178
19179


19180
19181










19182
19183
19184
19185


19186
19187
19188
19189
19190
19191
19192
19193


19194
19195
19196
19197
19198
19199
19200
19201
19202
19203
19204
19205
19206
19207
19208
19209
19210
19211
19212
19213




19214
19215
19216
19217
19218
19219
19220
19221
19222
19223
19224
19225
19226
19227
19228
19229
19230
19231
19232
19233
19234
19235
19236
19237
19238
19239
19240
19241
19242
19243
19244
19245
19246
19247
19248
19249
19250
19251
19252
19253
19254
19255
19256
19257
19258
19259
19260
19261
19262
19263
19264
19265
19266
19267
19268
19269
.....
19468
19469
19470
19471
19472
19473
19474

19475
19476
19477
19478
19479
19480
19481
.....
19512
19513
19514
19515
19516
19517
19518
19519

19520
19521
19522
19523
19524
19525
19526
19527
19528

19529
19530

19531



19532
19533
19534
19535
19536
19537
19538
.....
19571
19572
19573
19574
19575
19576
19577
19578
19579
19580
19581

19582
19583

19584
19585
19586
19587
19588
19589

19590
19591
19592
19593
19594
19595
19596

19597
19598
19599

19600
19601
19602
19603

19604
19605
19606




19607





19608
19609
19610
19611
19612
19613
19614
19615
19616
19617


19618
19619
19620
19621
19622
19623
19624




19625
19626
19627
19628
19629
19630
19631
.....
19713
19714
19715
19716
19717
19718
19719


19720


19721
19722
19723
19724
19725
19726
19727
.....
19832
19833
19834
19835
19836
19837
19838
19839
19840
19841
19842
19843
19844
19845
19846
.....
19868
19869
19870
19871
19872
19873
19874
19875
19876
19877
19878
19879
19880
19881
19882
.....
20884
20885
20886
20887
20888
20889
20890



20891

20892
20893
20894
20895
20896
20897
20898
.....
24739
24740
24741
24742
24743
24744
24745





24746
24747
24748
24749
24750
24751
24752
.....
24861
24862
24863
24864
24865
24866
24867
24868
24869
24870
24871
24872
24873
24874
24875
24876
24877
24878




24879
24880
24881
24882
24883
24884
24885
24886
.....
24993
24994
24995
24996
24997
24998
24999

















25000
25001
25002
25003
25004
25005
25006
.....
28504
28505
28506
28507
28508
28509
28510

28511
28512
28513
28514
28515
28516
28517
.....
28540
28541
28542
28543
28544
28545
28546







28547
28548
28549
28550
28551
28552
28553
.....
32290
32291
32292
32293
32294
32295
32296



32297
32298
32299
32300
32301
32302
32303
.....
33451
33452
33453
33454
33455
33456
33457
















































33458
33459
33460
33461
33462
33463
33464
33465
33466
33467
33468

33469
33470
33471
33472
33473
33474
33475
33476

33477
33478
33479
33480
33481
33482
33483
.....
33486
33487
33488
33489
33490
33491
33492

33493
33494
33495
33496
33497
33498
33499
.....
33509
33510
33511
33512
33513
33514
33515
33516
33517
33518
33519
33520
33521
33522
33523
33524
.....
33552
33553
33554
33555
33556
33557
33558




33559
33560
33561
33562
33563
33564
33565
33566
33567
33568
33569
33570
33571
33572
33573
33574
33575
33576
33577
33578
33579
33580
33581
33582





33583
33584
33585








33586
33587
33588


33589


33590
33591
33592
33593
33594
33595
33596
.....
33634
33635
33636
33637
33638
33639
33640

33641
33642
33643
33644
33645
33646
33647
.....
35934
35935
35936
35937
35938
35939
35940
35941
35942
35943
35944
35945
35946
35947
35948
35949
35950
35951
35952
.....
36243
36244
36245
36246
36247
36248
36249
36250
36251
36252
36253
36254
36255
36256
36257
.....
36259
36260
36261
36262
36263
36264
36265
36266
36267
36268
36269
36270
36271
36272
36273
.....
36313
36314
36315
36316
36317
36318
36319
36320
36321
36322
36323
36324
36325
36326
36327
36328
36329
36330



36331
36332
36333
36334
36335
36336
36337
.....
36400
36401
36402
36403
36404
36405
36406
36407
36408
36409
36410
36411
36412
36413
36414
.....
36445
36446
36447
36448
36449
36450
36451
36452
36453
36454
36455
36456
36457
36458
36459
.....
36581
36582
36583
36584
36585
36586
36587
36588
36589

36590
36591
36592
36593
36594
36595
36596
.....
36919
36920
36921
36922
36923
36924
36925

36926
36927
36928
36929
36930
36931
36932
36933
36934
36935
36936
36937
36938






36939
36940
36941
36942
36943
36944
36945
36946
36947
36948
36949
36950
36951








36952
36953
36954
36955
36956
36957
36958
.....
36987
36988
36989
36990
36991
36992
36993





















36994
36995
36996
36997
36998
36999
37000
.....
37804
37805
37806
37807
37808
37809
37810

37811
37812
37813
37814
37815
37816
37817
.....
38083
38084
38085
38086
38087
38088
38089
38090


38091
38092
38093
38094
38095
38096
38097
.....
38168
38169
38170
38171
38172
38173
38174
38175
38176
38177
38178
38179
38180
38181
38182
.....
51124
51125
51126
51127
51128
51129
51130
51131
51132
51133
51134
51135
51136
51137
51138
.....
51644
51645
51646
51647
51648
51649
51650
51651

51652

51653
51654
51655
51656
51657
51658
51659
.....
52933
52934
52935
52936
52937
52938
52939


52940
52941
52942
52943
52944
52945
52946
.....
53019
53020
53021
53022
53023
53024
53025
53026
53027
53028
53029
53030
53031
53032
53033
.....
65321
65322
65323
65324
65325
65326
65327






















65328



65329

65330
65331
65332
65333
65334
65335
65336
.....
65507
65508
65509
65510
65511
65512
65513
65514
65515
65516
65517
65518
65519
65520
65521

65522
65523
65524
65525
65526
65527
65528
65529
.....
65556
65557
65558
65559
65560
65561
65562

65563



65564
/* jsi.h : External API header file for Jsi. */
#ifndef __JSI_H__
#define __JSI_H__

#define JSI_VERSION_MAJOR   2
#define JSI_VERSION_MINOR   7
#define JSI_VERSION_RELEASE 1

#define JSI_VERSION (JSI_VERSION_MAJOR + ((Jsi_Number)JSI_VERSION_MINOR/100.0) + ((Jsi_Number)JSI_VERSION_RELEASE/10000.0))

#ifndef JSI_EXTERN
#define JSI_EXTERN extern
#endif

................................................................................

JSI_EXTERN Jsi_Value* Jsi_ValueNewNull(Jsi_Interp *interp); /*STUB = 100*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewBoolean(Jsi_Interp *interp, int bval); /*STUB = 101*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewNumber(Jsi_Interp *interp, Jsi_Number n); /*STUB = 102*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewBlob(Jsi_Interp *interp, uchar *s, uint len); /*STUB = 103*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewString(Jsi_Interp *interp, const char *s, int len); /*STUB = 104*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringKey(Jsi_Interp *interp, const char *s); /*STUB = 105*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringConst(Jsi_Interp *interp, const char *s, int len); /*STUB = 409*/ /*LAST*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringDup(Jsi_Interp *interp, const char *s); /*STUB = 106*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewArray(Jsi_Interp *interp, const char **items, int count); /*STUB = 107*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewObj(Jsi_Interp *interp, Jsi_Obj *o) ; /*STUB = 108*/
#define Jsi_ValueNewBlobString(interp, s) Jsi_ValueNewBlob(interp, (uchar*)s, Jsi_Strlen(s))

JSI_EXTERN Jsi_RC Jsi_GetStringFromValue(Jsi_Interp* interp, Jsi_Value *value, const char **s); /*STUB = 109*/
JSI_EXTERN Jsi_RC Jsi_GetNumberFromValue(Jsi_Interp* interp, Jsi_Value *value, Jsi_Number *n); /*STUB = 110*/
................................................................................
JSI_EXTERN Jsi_RC Jsi_RegExpMatch( Jsi_Interp *interp,  Jsi_Value *pattern, const char *str, int *rc, Jsi_DString *dStr); /*STUB = 189*/
JSI_EXTERN Jsi_RC Jsi_RegExpMatches(Jsi_Interp *interp, Jsi_Value *pattern, const char *str, Jsi_Value *ret); /*STUB = 190*/
JSI_EXTERN bool Jsi_GlobMatch(const char *pattern, const char *string, int nocase); /*STUB = 191*/
JSI_EXTERN char* Jsi_FileRealpath(Jsi_Interp *interp, Jsi_Value *path, char *newpath); /*STUB = 192*/
JSI_EXTERN char* Jsi_FileRealpathStr(Jsi_Interp *interp, const char *path, char *newpath); /*STUB = 193*/
JSI_EXTERN char* Jsi_NormalPath(Jsi_Interp *interp, const char *path, Jsi_DString *dStr); /*STUB = 194*/
JSI_EXTERN char* Jsi_ValueNormalPath(Jsi_Interp *interp, Jsi_Value *path, Jsi_DString *dStr); /*STUB = 195*/

JSI_EXTERN Jsi_RC Jsi_JSONParse(Jsi_Interp *interp, const char *js, Jsi_Value **ret, int flags); /*STUB = 196*/
JSI_EXTERN Jsi_RC Jsi_JSONParseFmt(Jsi_Interp *interp, Jsi_Value **ret, const char *fmt, ...) /*STUB = 197*/ __attribute__((format (printf,3,4)));
JSI_EXTERN char* Jsi_JSONQuote(Jsi_Interp *interp, const char *str, int len, Jsi_DString *dStr); /*STUB = 198*/
JSI_EXTERN Jsi_RC Jsi_EvalString(Jsi_Interp* interp, const char *str, int flags); /*STUB = 199*/
JSI_EXTERN Jsi_RC Jsi_EvalFile(Jsi_Interp* interp, Jsi_Value *fname, int flags); /*STUB = 200*/
JSI_EXTERN Jsi_RC Jsi_EvalCmdJSON(Jsi_Interp *interp, const char *cmd, const char *jsonArgs, Jsi_DString *dStr, int flags); /*STUB = 201*/
JSI_EXTERN Jsi_RC Jsi_EvalZip(Jsi_Interp *interp, const char *exeFile, const char *mntDir, int *jsFound); /*STUB = 202*/
................................................................................
    jsi_ScopeChain *scope_save;         /* saved scope (used in catch block/with block)*/
    Jsi_Value *curscope_save;           /* saved current scope */
    struct jsi_TryList *next;
    bool inCatch;
    bool inFinal;
} jsi_TryList;








struct Jsi_Interp {
#ifdef JSI_HAS_SIG
    jsi_Sig sig;
#endif
    bool isSafe;


    Jsi_Value *safeReadDirs;
    Jsi_Value *safeWriteDirs;
    const char *safeExecPattern;
    Jsi_DebugInterp debugOpts;
    struct jsi_TryList *tryList;
    bool deleting;
    bool destroying;
................................................................................
    assert(v->refCnt>=0);
    jsi_DebugValue(v,"Incr", jsi_DebugValueCallIdx(), interp);
    return ++(v->refCnt);
}

int Jsi_DecrRefCount(Jsi_Interp* interp, Jsi_Value *v) {
    SIGASSERT(v,VALUE);
    if (v->refCnt<0)



        return -2;

    int ref;
    jsi_DebugValue(v,"Decr", jsi_DebugValueCallIdx(), interp);
    if ((ref = --(v->refCnt)) <= 0) {
        v->refCnt = -1;
        Jsi_ValueFree(interp, v);
    }
    return ref;
................................................................................
#ifdef JSI_MEM_DEBUG
    //if (v->VD.interp != interp)  //TODO: InterpAliasCmd leaking Values.
     //   fprintf(stderr, "cross interp delete: %p\n", v);
    if (v->VD.hPtr) {
        if (!Jsi_HashEntryDelete(v->VD.hPtr))
            fprintf(stderr, "Value not in hash\n");
    }

#endif
    _JSI_MEMCLEAR(v);
    Jsi_Free(v);
}

/* Reset a value back to undefined, releasing string/obj if necessary. */
void Jsi_ValueReset(Jsi_Interp *interp, Jsi_Value **vPtr) {
    Jsi_Value *v = *vPtr;
    SIGASSERTV(v,VALUE);
................................................................................
    JSI_OPT(BOOL,  jsi_SubOptions, mutexUnlock, .help="Unlock own mutex when evaling in other interps (true)", jsi_IIOF),
    JSI_OPT(BOOL,  jsi_SubOptions, noproto,     .help="Disable support of the OOP symbols:  __proto__, prototype, constructor, etc"),
    JSI_OPT(BOOL,  jsi_SubOptions, noReadline,  .help="In interactive mode disable use of readline" ),
    JSI_OPT(BOOL,  jsi_SubOptions, outUndef,     .help="In interactive mode output result values that are undefined"),
    JSI_OPT_END(jsi_SubOptions, .help="Lesser sub-feature options")
};


static const char *jsi_TypeChkStrs[] = { "parse", "run", "all", "error", "strict", "noundef", "nowith", "funcsig", NULL };
static const char *jsi_callTraceStrs[] = { "funcs", "cmds", "new", "return", "args", "notrunc", "noparent", "full", "before", NULL};
const char *jsi_AssertModeStrs[] = { "throw", "log", "puts", NULL};

static Jsi_OptionSpec InterpOptions[] = {
    JSI_OPT(ARRAY, Jsi_Interp, args,        .help="The console.arguments for interp", jsi_IIOF),
    JSI_OPT(BOOL,  Jsi_Interp, asserts,     .help="Enable assert" ),
................................................................................
    JSI_OPT(FUNC,  Jsi_Interp, onComplete,  .help="Function to return commands completions for interactive mode.  Default uses Info.completions ", .flags=0, .custom=0, .data=(void*)"prefix:string, start:number, end:number" ),
    JSI_OPT(FUNC,  Jsi_Interp, onEval,      .help="Function to get control for interactive evals", .flags=0, .custom=0, .data=(void*)"cmd:string" ),
    JSI_OPT(FUNC,  Jsi_Interp, onExit,      .help="Command to call in parent on exit, returns true to continue", jsi_IIOF , .custom=0, .data=(void*)""),
    JSI_OPT(INT,   Jsi_Interp, opTrace,     .help="Set debugging level for OPCODE execution"),
    JSI_OPT(ARRAY, Jsi_Interp, pkgDirs,     .help="list of library directories for require() to search" ),
    JSI_OPT(BOOL,  Jsi_Interp, profile,     .help="On exit generate profile of function calls"),
    JSI_OPT(CUSTOM,Jsi_Interp, recvCallback,.help="Command to recv 'send' msgs from parent interp", .flags=0, .custom=Jsi_Opt_SwitchParentFunc, .data=(void*)"msg:string"),
    JSI_OPT(VALUE, Jsi_Interp, retValue,      .help="Return value from last eval", jsi_IIRO),

    JSI_OPT(ARRAY, Jsi_Interp, safeReadDirs,.help="In safe mode, files/dirs to allow reads from", jsi_IIOF),
    JSI_OPT(ARRAY, Jsi_Interp, safeWriteDirs,.help="In safe mode, files/dirs to allow writes to", jsi_IIOF),
    JSI_OPT(STRKEY, Jsi_Interp,safeExecPattern,.help="In safe mode, regexp pattern allow exec of commands", jsi_IIOF),
    JSI_OPT(STRKEY,Jsi_Interp, scriptStr,   .help="Interp init script string", jsi_IIOF),
    JSI_OPT(STRING,Jsi_Interp, scriptFile,  .help="Interp init script file"),
    JSI_OPT(STRING,Jsi_Interp, stdinStr,    .help="String to use as stdin for console.input()"),
    JSI_OPT(STRING,Jsi_Interp, stdoutStr,   .help="String to collect stdout for puts()"),
................................................................................
        argv = opts->argv;
    }
    if (!interp)
        interp = Jsi_InterpNew(opts);
    if (!interp)
        return NULL;
    Jsi_InterpOnDelete(interp, &jsi_InterpDelete, (void*)&jsi_InterpDelete);



#ifndef NO_JAZ
    /* Mount zip at end of executable */
    Jsi_Value *v = Jsi_Executable(interp);
    const char *exeFile = (v?Jsi_ValueString(interp, v, NULL):NULL);
    int jsFound = 0;
    if (v && (argc != 2 || Jsi_Strcmp(argv[1], "--nozvfs"))) {
................................................................................
            else if (rc != 0) {
                fprintf(stderr, "Error\n");
                return jsi_DoExit(interp, 1);
            }
        }
    }
#endif
    const char *iext = (argc<=1?NULL:Jsi_Strrchr(argv[1], '.'));
    if (interp->selfZvfs && iext && Jsi_Strcmp(iext,".fossil")==0) {
        rc = Jsi_EvalString(interp, "runModule('Jsi_Archive');", JSI_EVAL_ISMAIN);
        goto done;
    }
    Jsi_ShiftArgs(interp, NULL);
    if (argc == 1) {
iact:
        if (interp->opts.no_interactive)
            return interp;
        rc = Jsi_Interactive(interp, JSI_OUTPUT_QUOTE|JSI_OUTPUT_NEWLINES);
    } else {
        if (argc == 2 && !Jsi_Strcmp(argv[1], "-h" )) {
            puts("usage: jsish -h/--help | -v/--version | -d/--debug | -D/--debugui\n\t"
            "| -u/--unittest | -U/-UU | -s/--safe | -Z/--zip | -S/--sqliteui\n\t"
            "| -w/--wget | -W/--websrv | -H/--htmli | -J/--jsi | -C/--cssi\n\t"
            "| -c/--cdata | -m/--module | -M/--make | -e/--eval | -t/--tracecall\n\t"
            "| -a/--archive | -T/--typecheck OPT | -IOPT VAL | FILE arg arg ...\nUse --help for long help.");
            return jsi_DoExit(interp, 1);

        }








        if (argc == 2 && !Jsi_Strcmp(argv[1], "--help")) {
            puts("jsish arguments:\n"

              "  -a/--archive FILE\tMount an archive (zip, sqlar or fossil repo) and run module.\n"
              "  -c/--cdata FILE\tGenerate .c or JSON output from a .jsc description.\n"
              "  -C/--cssi FILE\tPreprocess embedded CSS in .css file.\n"
              "  -d/--debug FILE\tRun console debugger on script.\n"
              "  -D/--debugui FILE\tRun web-gui debugger on script.\n"
              "  -e/--eval STRING\tEvaluate a javascript string and exit.\n"
              "  -g/--gendeep FILES\tGenerate html output from markdeep source.\n"

              "  -h/--help\t\tPrint help in short or long form.\n"
              "  -H/--htmli FILE\tPreprocess embedded jsi in .htmli file.\n"
              "  -J/--jsi FILE\t\tPreprocess a .jsi file to typecheck in standard javascript.\n"
              "  -m/--module FILE\tSource file and invoke runModule.\n"
              "  -M/--make FILE\tPreprocess script as a Jsi Makefile.\n"
              "  -s/--safe FILE\tRun script in safe sub-interp.\n"
              "  -S/--sqliteui DBFILE\tRun Sqlite web-gui.\n"
              "  -t/--tracecall\tTrace all function calls.\n"
              "  -T/--typecheck OPT\tEnable typechecking.\n"
              "  -u/--unittest FILE\tRun unit-tests on a script file, or a dir containing .js/.jsi files.\n"
              "  -U/-UU SCRIPT\t\tShow output from unit-test mode, omitting pass/fail compare.\n"
              "  -v/--version\t\tPrint short/long version info and exit.\n"



              "  -Z/--zip\t\tUsed to append/manage zip files at end of executable.\n"
              "  -w/--wget URL\t\tWeb client to download file from url.\n"
              "  -W/--websrv FILE\tServe out file in web server, with preprocessing.\n"
              "  -IOPT VAL\t\tSet an internal interp option value.\n"



              "\nInterp options may also be set via the environment eg. JSI_INTERP_OPTS='{coverage:true}'\n"
               );
            return jsi_DoExit(interp, 1);
        }







        if (argc == 2 && !Jsi_Strcmp(argv[1], "--version")) {
            char str[200] = "\n";
            Jsi_Channel chan = Jsi_Open(interp, Jsi_ValueNewStringKey(interp, "/zvfs/lib/sourceid.txt"), "r");
            if (chan)
                Jsi_Read(interp, chan, str, sizeof(str));
            printf("%u.%u.%u %.4" JSI_NUMGFMT " %s", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE, Jsi_Version(), str);
            return jsi_DoExit(interp, 1);
        }

        if (argc == 2 && !Jsi_Strcmp(argv[1], "-v" )) {
            printf("%u.%u.%u\n", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE);
            return jsi_DoExit(interp, 1);
        }

        if (argc > 2 && (Jsi_Strcmp(argv[1], "--module")==0 || Jsi_Strcmp(argv[1], "-m" )==0)) {












            if (argv[2][0] == '-')
                rc = Jsi_EvalString(interp, "runModule('Jsi_Module');", JSI_EVAL_ISMAIN);
            else {
                Jsi_DString dStr = {};

                const char* cpe = Jsi_Strrchr(argv[2], '.');


                int len = (cpe?cpe-argv[2]:(int)Jsi_Strlen(argv[2]));


                Jsi_DSPrintf(&dStr, "source(\"%s\"); puts(runModule(\"%.*s\",console.args.slice(1)));", argv[2], len, argv[2]);
                rc = Jsi_EvalString(interp, Jsi_DSValue(&dStr), JSI_EVAL_NOSKIPBANG);
                Jsi_DSFree(&dStr);
            }
        }
        else if (argc == 3 && (Jsi_Strcmp(argv[1], "--eval")==0 || Jsi_Strcmp(argv[1], "-e" )==0))
            rc = Jsi_EvalString(interp, argv[2], JSI_EVAL_NOSKIPBANG);

        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--debug")==0 || Jsi_Strcmp(argv[1], "-d" )==0)) {
            interp->debugOpts.isDebugger = 1;


            rc = Jsi_EvalString(interp, "runModule('Jsi_Debug');", JSI_EVAL_ISMAIN);
        } else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--safe")==0 || Jsi_Strcmp(argv[1], "-s" )==0))


            rc = Jsi_EvalString(interp, "runModule('Jsi_Safe');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--debugui")==0 || Jsi_Strcmp(argv[1], "-D" )==0)) {
            interp->debugOpts.isDebugger = 1;


            rc = Jsi_EvalString(interp, "runModule('Jsi_DebugUI');", JSI_EVAL_ISMAIN);
        } else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--wget")==0 || Jsi_Strcmp(argv[1], "-w" )==0))










            rc = Jsi_EvalString(interp, "runModule('Jsi_Wget');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--gendeep")==0 || Jsi_Strcmp(argv[1], "-g" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_GenDeep');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--websrv")==0 || Jsi_Strcmp(argv[1], "-W" )==0))


            rc = Jsi_EvalString(interp, "runModule('Jsi_Websrv');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--archive")==0 || Jsi_Strcmp(argv[1], "-a" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_Archive');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--sqliteui")==0 || Jsi_Strcmp(argv[1], "-S" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_SqliteUI');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--cdata")==0 || Jsi_Strcmp(argv[1], "-c" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_CData');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--zip")==0 || Jsi_Strcmp(argv[1], "-Z" )==0))


            rc = Jsi_EvalString(interp, "runModule('Jsi_Zip');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--cssi")==0 || Jsi_Strcmp(argv[1], "-C" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_Csspp');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--jsi")==0 || Jsi_Strcmp(argv[1], "-J" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_Jspp');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--htmli")==0 || Jsi_Strcmp(argv[1], "-H" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_Htmlpp');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--unittest")==0 || Jsi_Strcmp(argv[1], "-u" )==0))
            rc = Jsi_EvalString(interp, "exit(runModule('Jsi_UnitTest'));", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--make")==0 || Jsi_Strcmp(argv[1], "-M" )==0))
            rc = Jsi_EvalString(interp, "exit(runModule('Jsi_Make'));", JSI_EVAL_ISMAIN);
        else {
            int iocnt;
            for (iocnt = 1; (iocnt+1)<argc; iocnt+=2) {
                if (Jsi_Strcmp(argv[iocnt], "-t") == 0 || Jsi_Strcmp(argv[iocnt], "--tracecall") == 0) {iocnt--; continue; }
                if (Jsi_Strcmp(argv[iocnt], "-T") == 0 || Jsi_Strcmp(argv[iocnt], "--typecheck") == 0) {continue; }
                if (Jsi_Strcmp(argv[iocnt], "-U") == 0) {iocnt--; continue; }
                if (Jsi_Strcmp(argv[iocnt], "-UU") == 0) {iocnt--; continue; }
                if (Jsi_Strncmp(argv[iocnt], "-I", 2) == 0) continue;
                break;




            }
            argc -= (iocnt-1);
            argv += (iocnt-1);
            if (argc<2)
                goto iact;
            const char *ext = Jsi_Strrchr(argv[1], '.');

            /* Support running "main.jsi" from a zip file. */
            if (ext && (Jsi_Strcmp(ext,".zip")==0 ||Jsi_Strcmp(ext,".jsz")==0 ) ) {
                rc = Jsi_EvalZip(interp, argv[1], NULL, &jsFound);
                if (rc<0) {
                    fprintf(stderr, "zip mount failed\n");
                    return jsi_DoExit(interp, 1);
                }
                if (!(jsFound&JSI_ZIP_MAIN)) {
                    fprintf(stderr, "main.jsi not found\n");
                    return jsi_DoExit(interp, 1);
                }
            } else {
                if (argc>1) {
                    jsi_vf = Jsi_ValueNewStringKey(interp, argv[1]);
                    Jsi_IncrRefCount(interp, jsi_vf);
                }
                rc = Jsi_EvalFile(interp, jsi_vf, JSI_EVAL_ARGV0|JSI_EVAL_AUTOINDEX);
                if (jsi_vf) {
                    Jsi_DecrRefCount(interp, jsi_vf);
                    jsi_vf = NULL;
                }

            }
        }
        if (jsi_deleted) //TODO: rationalize jsi_deleted, jsi_exitCode, etc
            return jsi_DoExit(rc==JSI_EXIT?NULL:interp, jsi_exitCode);
        if (rc == 0) {
            /* Skip output from an ending semicolon which evaluates to undefined */
            Jsi_Value *ret = Jsi_ReturnValue(interp);
            if (!Jsi_ValueIsType(interp, ret, JSI_VT_UNDEF)) {
                Jsi_DString dStr = {};
                fputs(Jsi_ValueGetDString(interp, ret, &dStr, 0), stdout);
                Jsi_DSFree(&dStr);
                fputs("\n", stdout);
            }
        } else {
            if (!interp->parent && !interp->isHelp)
                fputs("ERROR\n", stderr);
            return jsi_DoExit(interp, 1);
        }

    }
done:
    if (rc == JSI_EXIT) {
        if (opts)
            opts->exitCode = jsi_exitCode;
        return NULL;
    }
    if (jsi_deleted == 0 && interp->opts.auto_delete) {
................................................................................
        iopts->interp = interp;
        interp->opts = *iopts;
    }
    interp->logOpts.file = 1;
    interp->logOpts.func = 1;
    int argc = interp->opts.argc;
    char **argv = interp->opts.argv;

    interp->parent = parent;
    interp->topInterp = (parent == NULL ? interp: parent->topInterp);
    if (jsiIntData.mainInterp == NULL)
        jsiIntData.mainInterp = interp->topInterp;
    interp->mainInterp = jsiIntData.mainInterp; // The first interps handles exit.
    interp->memDebug = interp->opts.mem_debug;
    if (parent) {
................................................................................
    const char *ocp2;
    if (ocp && ((ocp2=Jsi_Strstr(ocp,"memDebug:"))))
        interp->memDebug=strtol(ocp+sizeof("memDebug:"), NULL, 0);
    if (ocp && ((ocp2=Jsi_Strstr(ocp,"compat:"))))
        interp->subOpts.compat=(ocp[sizeof("compat:")]=='t');
    for (iocnt = 1; (iocnt+1)<argc; iocnt+=2)
    {
        if (Jsi_Strcmp(argv[iocnt], "-T") == 0 || Jsi_Strcmp(argv[iocnt], "--typecheck") == 0) {

            continue;
        }
        if (Jsi_Strcmp(argv[iocnt], "-t") == 0 || Jsi_Strcmp(argv[iocnt], "--tracecall") == 0
            || Jsi_Strcmp(argv[iocnt], "-U") == 0 || Jsi_Strcmp(argv[iocnt], "-UU") == 0) {
            iocnt--;
            continue;
        }
        if (Jsi_Strncmp(argv[iocnt], "-I", 2)) break;
        if (!Jsi_Strcmp("memDebug", argv[iocnt]+2))

            interp->memDebug=strtol(argv[iocnt+1], NULL, 0);
        else if (!Jsi_Strcmp("compat", argv[iocnt]+2))

            interp->subOpts.compat=strtol(argv[iocnt+1], NULL, 0);



    }
    SIGINIT(interp,INTERP);
    interp->NullValue = Jsi_ValueNewNull(interp);
    Jsi_IncrRefCount(interp, interp->NullValue);
#ifdef __WIN32
    Jsi_DString cwdStr;
    Jsi_DSInit(&cwdStr);
................................................................................
    mapType = JSI_MAP_TREE;
#endif

    if (interp == jsiIntData.mainInterp || interp->threadId != jsiIntData.mainInterp->threadId) {
        interp->strKeyTbl = Jsi_MapNew(interp,  mapType, JSI_KEYS_STRING, NULL);
        interp->subOpts.privKeys = 1;
    }
    // Handle interp options: -T|--typecheck value and -Ixxx value
    for (iocnt = 1; (iocnt+1)<argc; iocnt+=2)
    {
        if (Jsi_Strcmp(argv[iocnt], "-t") == 0 || Jsi_Strcmp(argv[iocnt], "--tracecall") == 0) {

            interp->traceCall |= (jsi_callTraceFuncs |jsi_callTraceArgs |jsi_callTraceReturn | jsi_callTraceBefore | jsi_callTraceFullPath);
            iocnt--;

            continue;
        }
        if (Jsi_Strcmp(argv[iocnt], "-U") == 0) {
            interp->asserts = 1;
            interp->unitTest = 1;
            iocnt--;

            continue;
        }
        if (Jsi_Strcmp(argv[iocnt], "-UU") == 0) {
            interp->asserts = 1;
            interp->unitTest = 5;
            interp->tracePuts = 1;
            iocnt--;

            continue;
        }
        if (Jsi_Strcmp(argv[iocnt], "-T") == 0 || Jsi_Strcmp(argv[iocnt], "--typecheck") == 0) {

            if (jsi_ParseTypeCheckStr(interp, argv[iocnt+1]) != JSI_OK) {
                Jsi_InterpDelete(interp);
                return NULL;
            }

            continue;
        }
        if (Jsi_Strncmp(argv[iocnt], "-I", 2)) break;




        const char *argStr = argv[iocnt+1];





        DECL_VALINIT(argV);
        Jsi_Value *argValue = &argV;
        Jsi_Number dv;
        bool bv;
        if (Jsi_GetBool(interp, argStr, &bv) == JSI_OK) {
            Jsi_ValueMakeBool(interp, &argValue, bv);
        } else if (Jsi_GetDouble(interp, argStr, &dv) == JSI_OK) {
            Jsi_ValueMakeNumber(interp, &argValue, dv);
        } else if (!Jsi_Strcmp("null", argStr)) {
            Jsi_ValueMakeNull(interp, &argValue);


        } else {
            Jsi_ValueMakeStringKey(interp, &argValue, argStr);
        }
        if (JSI_OK != Jsi_OptionsSet(interp, InterpOptions, interp, argv[iocnt]+2, argValue, 0)) {
            Jsi_InterpDelete(interp);
            return NULL;
        }




    }
    if (!interp->strKeyTbl)
        interp->strKeyTbl = jsiIntData.mainInterp->strKeyTbl;
    if (opts) {
        interp->inopts = opts = Jsi_ValueDupJSON(interp, opts);
        if (Jsi_OptionsProcess(interp, InterpOptions, interp, opts, 0) < 0) {
            Jsi_DecrRefCount(interp, opts);
................................................................................
    interp->threadId = Jsi_CurrentThread();
    if (interp->parent && interp->subthread==0 && interp->threadId != interp->parent->threadId) {
        interp->threadId = interp->parent->threadId;
#ifndef JSI_MEM_DEBUG
        Jsi_LogWarn("non-threaded sub-interp created by different thread than parent");
#endif
    }


    if (!interp->parent) {


        if (interp->debugOpts.msgCallback)
            Jsi_LogWarn("ignoring msgCallback");
        if (interp->debugOpts.putsCallback)
            Jsi_LogWarn("ignoring putsCallback");
        if (interp->busyCallback)
            Jsi_LogWarn("ignoring busyCallback");
        if (interp->recvCallback)
................................................................................

    if (!interp->isSafe) {
        JSIDOINIT(Load);
#if JSI__SIGNAL==1
        JSIDOINIT(Signal);
#endif
    }
    if (interp->isSafe == 0 || interp->safeWriteDirs!=NULL || interp->safeReadDirs!=NULL) {
#if JSI__FILESYS==1
        JSIDOINIT(FileCmds);
        JSIDOINIT(Filesys);
#endif
    }
#if JSI__SQLITE==1
    JSIDOINIT2(Sqlite);
................................................................................
    if (JSI_USER_EXTENSION (interp, 0) != JSI_OK) {
        fprintf(stderr, "extension load failed");
        return jsi_DoExit(interp, 1);
    }
#endif
    Jsi_PkgProvide(interp, "Jsi", JSI_VERSION, NULL);
    if (argc > 0) {
        char *ss = argv[0];
        char epath[PATH_MAX] = ""; // Path of executable
#ifdef __WIN32

        if (GetModuleFileName(NULL, epath, sizeof(epath))>0)
            ss = epath;
#else
#ifndef PROC_SELF_DIR
................................................................................
    if (!mntDir)
        ret = Jsi_ValueNew(interp);
    else {
        vmnt = Jsi_ValueNewStringKey(interp, mntDir);
        Jsi_IncrRefCount(interp, vmnt);
        Jsi_HashSet(interp->genValueTbl, vmnt, vmnt);
    }



    rc =Jsi_Mount(interp, vexe, vmnt, &ret);

    if (rc != JSI_OK)
        return rc;
    Jsi_DString dStr, bStr;
    Jsi_DSInit(&dStr);
    Jsi_DSInit(&bStr);
    if (!mntDir) {
        mntDir = Jsi_KeyAdd(interp, Jsi_ValueString(interp, ret, NULL));
................................................................................
    if (fsPtr == NULL || !fsPtr->chmodProc) return -1;
    return fsPtr->chmodProc(interp, path, mode);
}
int Jsi_Access(Jsi_Interp *interp, Jsi_Value* path, int mode) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->accessProc) return -1;





    return fsPtr->accessProc(interp, path, mode);
}
int Jsi_Remove(Jsi_Interp *interp, Jsi_Value* path, int flags) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->removeProc) return -1;
    return fsPtr->removeProc(interp, path, flags);
................................................................................
    {
        Jsi_LogError("can not open directory: %s", fileName);
        goto done;
    }
    fsPtr = Jsi_FilesystemForPath(interp, file, &data);
    writ = (Jsi_Strchr(s,'w') || Jsi_Strchr(s,'a') || Jsi_Strchr(s,'+'));
    aflag = (writ ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);
    if (interp->isSafe && Jsi_InterpAccess(interp, file, aflag) != JSI_OK) {
        Jsi_LogError("%s access denied", writ?"write":"read");
        goto done;
    }
    if (fsPtr && fsPtr != &jsiFilesystem) {
        ch = fsPtr->openProc(interp, file, Mode);
        if (ch)
            ch->isNative = 0;
        else
            Jsi_LogError("File open failed '%s'", fileName);
    } else {




        FILE *fp = fopen(fileName, Mode);
        fsPtr = &jsiFilesystem;
        if (!fp) {
            if (!quiet)
                Jsi_LogError("File open failed '%s'", fileName);
            goto done;
        }
        ch = (Jsi_Chan *)Jsi_Calloc(1,sizeof(*ch));
................................................................................
    if (!path) return NULL;
    return Jsi_FileRealpathStr(interp, path, newname);
}

static char* jsi_FSRealPathProc(Jsi_Interp *interp, Jsi_Value *src, char *newPath) {
    return Jsi_FileRealpath(interp, src, newPath);
}


















char *Jsi_Realpath(Jsi_Interp *interp, Jsi_Value *src, char *newname)
{
    /* TODO: resolve pwd first. */
    void *data;
    const char *cp = NULL;
    Jsi_Filesystem *fsPtr;
................................................................................

#ifdef __WIN32
#define dlsym(l,s) GetProcAddress(l,s)
#define dlclose(l) FreeLibrary(l)
#include <windows.h>
#else
#include <dlfcn.h>

#endif

#ifndef RTLD_NOW
    #define RTLD_NOW 0
#endif
#ifndef RTLD_LOCAL
    #define RTLD_LOCAL 0
................................................................................
Jsi_RC Jsi_LoadLibrary(Jsi_Interp *interp, const char *pathName, bool noInit)
{
    if (interp->noLoad)
        return Jsi_LogError("shared lib load is disabled");
#ifdef __WIN32
    HMODULE handle = LoadLibrary(pathName);
#else







    void *handle = dlopen(pathName, RTLD_NOW | RTLD_LOCAL);
#endif
    if (handle == NULL) {
        // FYI: Valgrind shows a mem-leak here.
        Jsi_LogError("loading extension \"%s\": %s", pathName, dlerror());
        return JSI_ERROR;
    }
................................................................................
char *strptime(const char *buf, const char *fmt, struct tm *tm);
#endif
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <signal.h>
#include <limits.h>




static Jsi_RC consoleInputCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char buf[1024];
    char *cp, *p = buf;
    buf[0] = 0;
................................................................................
        pid = (Jsi_Number)getppid();
    else
        pid = (Jsi_Number)getpid();
    Jsi_ValueMakeNumber(interp, ret, pid);
    return JSI_OK;
#endif
}

















































typedef struct {
    Jsi_Value* inputStr;
    Jsi_Value* chdir;
    bool bg;
    bool noError;
    bool trim;
    bool retCode;
    bool retAll;
    bool retval;
    bool noRedir;

} ExecOpts;

static Jsi_OptionSpec ExecOptions[] = {
    JSI_OPT(BOOL,   ExecOpts, bg,       .help="Run command in background using system() and return OS code" ),
    JSI_OPT(STRING, ExecOpts, chdir,    .help="Change to directory" ),
    JSI_OPT(STRING, ExecOpts, inputStr, .help="Use string as input and return OS code" ),
    JSI_OPT(BOOL,   ExecOpts, noError,  .help="Suppress all OS errors" ),
    JSI_OPT(BOOL,   ExecOpts, noRedir,  .help="Disable redirect and shell escapes in command" ),

    JSI_OPT(BOOL,   ExecOpts, trim,     .help="Trim trailing whitespace from output" ),
    JSI_OPT(BOOL,   ExecOpts, retAll,   .help="Return the OS return code and data as an object" ),
    JSI_OPT(BOOL,   ExecOpts, retCode,  .help="Return only the OS return code" ),
    JSI_OPT_END(ExecOpts, .help="Exec options")
};

#define FN_exec JSI_INFO("\
................................................................................
By default, returns the string output, unless the 'bg', 'inputStr', 'retCode' or 'retAll' options are used")
Jsi_RC jsi_SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr, bool restricted)
{
    int n, exitCode = 0, hasopts = 0, sLen, sLen2 = 0;
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 0);
    const char *cp2=NULL, *cp = Jsi_ValueString(interp, arg, &sLen);

    if (restricted && Jsi_ValueGetLength(interp, args)>1)
        return Jsi_LogError("restricted may not have args");
    if (interp->isSafe) {
        int rc =0, no = 1;
        if (restricted)
            no = 0;
        else if (interp->safeExecPattern && cp) {
................................................................................
            restricted = 1;
        }
        if (no)
            return Jsi_LogError("no exec in safe mode");
    }
    Jsi_RC rc = JSI_OK;
    Jsi_Value *opt = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_DString dStr = {};
    Jsi_DString cStr = {};
    ExecOpts edata = {};
    edata.retval = 1;
    
    if (opt != NULL) {
        if (opt->vt == JSI_VT_OBJECT && opt->d.obj->ot == JSI_OT_OBJECT) {
            hasopts = 1;
            if (Jsi_OptionsProcess(interp, ExecOptions, &edata, opt, 0) < 0) {
................................................................................
        }
    }
    if (edata.bg || (isbg=((sLen>1 && cp[sLen-1] == '&')))) {
        if (edata.inputStr) {
            rc = Jsi_LogError("input string may not used with bg");
            goto done;
        }




        if (!isbg) {
            Jsi_DSAppend(&dStr, cp, " &", NULL);
            cp = Jsi_DSValue(&dStr);
        }
        edata.bg = 1;
        edata.retCode = 1;
        edata.retval = 0;
        exitCode = ((ec=system(cp))>>8);
        Jsi_DSSetLength(&dStr, 0);
    } else if (edata.inputStr) {
        edata.retCode = 1;
        edata.retval = 0;
        cp2 = Jsi_ValueString(interp, edata.inputStr, &sLen2);
        FILE *fp = popen(cp, "w");
        if (!fp) 
            exitCode = errno;
        else {
            while ((n=fwrite(cp2, 1, sLen2, fp))>0) {
                sLen2 -= n;
            }
            exitCode = ((ec=pclose(fp))>>8);
        }
    } else {
        FILE *fp = popen(cp, "r");





        if (!fp) 
            exitCode = errno;
        else {








            char buf[JSI_BUFSIZ];;
            while ((n=fread(buf, 1, sizeof(buf), fp))>0)
                Jsi_DSAppendLen(&dStr, buf, n);


            exitCode = ((ec=pclose(fp))>>8);


        }
    }
    if (exitCode && edata.noError==0 && edata.retCode==0 && edata.retAll==0) {
        if (exitCode==ENOENT)
            Jsi_LogError("command not found: %s", cp);
        else
            Jsi_LogError("program exit code (%x)", exitCode);
................................................................................
}

static Jsi_RC SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    return jsi_SysExecCmd(interp, args, _this, ret, funcPtr, false);
}


static Jsi_RC SysPutsCmd_(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this, Jsi_Value **ret,
    Jsi_Func *funcPtr, bool stdErr, jsi_LogOptions *popts, const char *argStr)
{
    int i, cnt = 0, quote = (popts->file);
    const char *fn = NULL;
    Jsi_DString dStr, oStr;
................................................................................
static Jsi_RC
B64EncodeDStr(const char *ib, int ilen, Jsi_DString *dStr)
{
    int i=0, pos=0;
    char c[74];
    
    while (pos<ilen) {
#define P(n,s) ((pos+n)>ilen?'=':b64ev[s])
        c[i++]=b64ev[(ib[pos]>>2)&0x3f];
        c[i++]=P(1,((ib[pos]<<4)&0x30)|((ib[pos+1]>>4)&0x0f));
        c[i++]=P(2,((ib[pos+1]<<2)&0x3c)|((ib[pos+2]>>6)&0x03));
        c[i++]=P(3,ib[pos+2]&0x3f);
        if (i>=72) {
            c[i++]='\n';
            c[i]=0;
            Jsi_DSAppendLen(dStr, c, i);
            i=0;
        }
        pos+=3;
................................................................................
        vargs[n++] = Jsi_ValueNewObj(interp, obj);
        vargs[n++] = Jsi_ValueNewObj(interp, obj=Jsi_ObjNew(interp));
    } else {
        arr = v2->d.obj->arr;
        siz = v2->d.obj->arrCnt;
        for (i=0; i<siz; i+=2) {
            anam = Jsi_ValueToString(interp, arr[i], NULL);
            if (i==0 && siz==1 && (!Jsi_Strcmp(anam, "-h") || !Jsi_Strcmp(anam, "--help"))) { anum=1; break; }
            if (anam[0] != '-') break;
            if (anam[0] == '-' && anam[1] == '-' && !anam[2]) {acnt++; break;}
            anum += 2;
        }
        if (anum != 1 && (anum>siz)) {
            rc = Jsi_LogError("odd length");
            goto done;
................................................................................
        obj = Jsi_ObjNewArray(interp, arr+anum+acnt, siz-anum-acnt, 0);
        vargs[n++] = Jsi_ValueNewObj(interp, obj);
        vargs[n++] = Jsi_ValueNewObj(interp, obj=Jsi_ObjNew(interp));
        bool isLong = 0;
        for (i=0; i<anum; i+=2) {
            int anLen;
            const char *astr, *anam = Jsi_ValueToString(interp, arr[i], &anLen);
            if (anum<=1 && (!Jsi_Strcmp(anam,"-h") || (isLong=!Jsi_Strcmp(anam,"--help")))) anam = "help";
            else if (anam && anam[0] == '-') anam++;
            else {
                rc = Jsi_LogError("bad option: %d", i);
                goto done;
            }
                
            Jsi_Value *aval;
................................................................................

static const char *jsi_FindHelpStr(const char *fstr, const char *key, Jsi_DString *dPtr) {
    if (!fstr) return "";
    Jsi_DSSetLength(dPtr, 0);
    const char *cp, *ce;
    int len = Jsi_Strlen(key);
    while (fstr) {
        cp = Jsi_Strstr(fstr, key);
        if (!cp) break;
        if (isspace(cp[-1]) && (cp[len]==':' || isspace(cp[len]))) {
            ce = NULL;
            cp = Jsi_Strstr(cp, "// ");
            if (cp)
                ce = Jsi_Strchr(cp, '\n');
            if (cp && ce)
                return Jsi_DSAppendLen(dPtr, cp, ce-cp);
            fstr = ce;
        } else fstr = cp+len;



    }
    return "";
}

static bool jsi_ModLogEnabled(Jsi_Interp *interp, Jsi_Value *v1, const char *name) {
    jsi_Frame *fptr = interp->framePtr;
    Jsi_Value *v2 = Jsi_ValueObjLookup(interp, v1, name, 0);
................................................................................
            if (!val || !key) continue;
            if (Jsi_ValueIsUndef(interp, val)) {
                rc = Jsi_LogError("value undefined for arg: '%s'", key);
                break;
            }

            if (cnt == 1 && !Jsi_Strcmp(key, "help") && v3->d.obj->tree->numEntries==1) {
                int isLong = Jsi_ValueIsTrue(interp, val);
                const char *help = "", *es = NULL, *fstr = NULL, *fname = interp->framePtr->ip->fname;
                Jsi_TreeSearchDone(&search);
                if (fname) {
                    jsi_FileInfo  *fi = (typeof(fi))Jsi_HashGet(interp->fileTbl, fname, 0);
                    fstr = fi->str;
                }
                if (!fstr)
................................................................................
                        Jsi_DSPrintf(&dStr, " -%s", key);
                    else
                        Jsi_DSPrintf(&dStr, "\t-%s%s\t%s\t%s%s\n", key, (klen<7?"\t":""), vstr, (vlen<7?"\t":""), help);
                }
                if (isLong)
                    Jsi_DSAppend(&dStr, "\nAccepted by all .jsi modules: -Debug, -Trace, -Test.", NULL);
                else
                    Jsi_DSAppend(&dStr, "\nUse --help for long help.", NULL);
                rc = JSI_BREAK;
                Jsi_ValueFromDS(interp, &dStr, ret);
                Jsi_DSFree(&hStr);
                Jsi_DSFree(&vStr);
                break;
            }
            Jsi_vtype oTyp, vTyp = jsi_getValType(val);
................................................................................
#ifndef JSI_OMIT_ENCRYPT
    { "decrypt",    SysDecryptCmd,   2,  2, "val:string, key:string", .help="Decrypt data using BTEA encryption", .retType=(uint)JSI_TT_STRING, .flags=0, .info=FN_encrypt },
    { "encrypt",    SysEncryptCmd,   2,  2, "val:string, key:string", .help="Encrypt data using BTEA encryption", .retType=(uint)JSI_TT_STRING, .flags=0, .info=FN_encrypt },
#endif
    { "fromCharCode",SysFromCharCodeCmd, 1, 1, "code:number", .help="Return char with given character code", .retType=(uint)JSI_TT_STRING, .flags=JSI_CMDSPEC_NONTHIS},
    { "getenv",     SysGetEnvCmd,    0,  1, "name:string=void", .help="Get one or all environment", .retType=(uint)JSI_TT_STRING|JSI_TT_OBJECT|JSI_TT_VOID  },
    { "getpid",     SysGetPidCmd,    0,  1, "parent:boolean=false", .help="Get process/parent id", .retType=(uint)JSI_TT_NUMBER },
    { "setenv",     SysSetEnvCmd,    1,  2, "name:string, value:string=void", .help="Set/get an environment var"  },
    { "hash",       SysHashCmd,      1,  2, "val:string, options|object=void", .help="Return hash (default SHA256) of string/file", .retType=(uint)JSI_TT_STRING, .flags=0, .info=0, .opts=HashOptions},

    { "times",      SysTimesCmd,     1,  2, "callback:function, count:number=1", .help="Call function count times and return execution time in microseconds", .retType=(uint)JSI_TT_NUMBER },
    { "verConvert", SysVerConvertCmd,1,  2, "ver:string|number, zeroTrim:number=0", .help="Convert a version to/from a string/number, or return null if not a version. For string output zeroTrim says how many trailing .0 to trim (0-2)", .retType=(uint)JSI_TT_NUMBER|JSI_TT_STRING|JSI_TT_NULL },
    { NULL, 0,0,0,0, .help="Utilities commands"  }
};

static Jsi_CmdSpec sysCmds[] = {
    { "assert", jsi_AssertCmd,       1,  3, "expr:boolean|number|function, msg:string=void, options:object=void",  .help="Throw or output msg if expr is false", .retType=(uint)JSI_TT_VOID, .flags=0, .info=FN_assert, .opts=AssertOptions },
................................................................................
    char *spath =  Jsi_ValueString(interp, path,0);
    int rc, force = 0; 

    if (!spath) 
        return Jsi_LogError("expected string");
    if (vf && !Jsi_ValueIsBoolean(interp, vf)) 
        return Jsi_LogError("expected boolean");

    if (vf)
        force = vf->d.val;
    if (force==0)
        rc = MKDIR_DEFAULT(spath);
    else {
        Jsi_Value *npath = Jsi_ValueNewStringDup(interp, spath);
        rc = mkdir_all(interp, npath);
        Jsi_ValueFree(interp, npath);
    }
    if (rc != 0) 
        return Jsi_LogError("can't create directory \"%s\": %s", spath, strerror(errno));    
    return JSI_OK;
}







static Jsi_RC FileTempfileCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char *filename;
#ifndef __WIN32
    Jsi_Value *vt = Jsi_ValueArrayIndex(interp, args, 0);
    const char *tp, *templ = "/tmp/jsiXXXXXX";

    if (vt && (tp = Jsi_ValueString(interp, vt, NULL))) {
        templ = tp;
    }
    filename = Jsi_Strdup(templ);








    int fd = mkstemp(filename);
    if (fd < 0)
        goto fail;
    close(fd);
#else
#ifndef MAX_PATH
#define MAX_PATH 1024
................................................................................
    Jsi_Channel ch = Jsi_Open(interp, fname, "rb+");
    if (!ch)
        return JSI_ERROR;
    rc = (Jsi_Truncate(interp, ch, (unsigned int)siz) == 0 ? JSI_OK : JSI_ERROR);
    Jsi_Close(interp, ch);
    return rc;
}






















static Jsi_RC FileChmodCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Value *fname = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_Value *modv = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_Number fmod;
................................................................................
    { "isdir",      FileIsdirCmd,       1,  1, "file:string",  .help="Return true if file is a directory", .retType=(uint)JSI_TT_BOOLEAN },
    { "isfile",     FileIsfileCmd,      1,  1, "file:string",  .help="Return true if file is a normal file", .retType=(uint)JSI_TT_BOOLEAN },
    { "isrelative", FileIsRelativeCmd,  1,  1, "file:string",  .help="Return true if file path is relative", .retType=(uint)JSI_TT_BOOLEAN },
    { "glob",       FileGlobCmd,        0,  2, "pattern:regexp|string|null='*', options:function|object|null=void", .help="Return list of files in dir with optional pattern match", .retType=(uint)JSI_TT_ARRAY, .flags=0, .info=FN_glob, .opts=GlobOptions },
    { "link",       FileLinkCmd,        2,  3, "src:string, dest:string, ishard:boolean=false",  .help="Link a file", .retType=0, .flags=0, .info=FN_link },
    { "lstat",      FileLstatCmd,       1,  1, "file:string",  .help="Return status info for file", .retType=(uint)JSI_TT_OBJECT },
    { "mkdir",      FileMkdirCmd,       1,  1, "file:string",  .help="Create a directory" },

    { "mtime",      FileMtimeCmd,       1,  1, "file:string",  .help="Return file modified time", .retType=(uint)JSI_TT_NUMBER },
    { "owned",      FileOwnedCmd,       1,  1, "file:string",  .help="Return true if file is owned by user", .retType=(uint)JSI_TT_BOOLEAN },
    { "pwd",        FilePwdCmd,         0,  0, "",  .help="Return current directory", .retType=(uint)JSI_TT_STRING },
    { "remove",     FileRemoveCmd,      1,  2, "file:string, force:boolean=false",  .help="Delete a file or direcotry" },
    { "rename",     FileRenameCmd,      2,  3, "src:string, dest:string, force:boolean=false",  .help="Rename a file, with possible overwrite" },
    { "read",       FileReadCmd,        1,  2, "file:string, mode:string='rb'",  .help="Read a file", .retType=(uint)JSI_TT_STRING },
    { "readable",   FileReadableCmd,    1,  1, "file:string",  .help="Return true if file is readable", .retType=(uint)JSI_TT_BOOLEAN },
................................................................................
                Jsi_DecrRefCount(interp, obj->arr[i]);
        Jsi_Free(obj->arr);
        obj->arr = NULL;
    }
    obj->tree = NULL;
    if (obj->clearProto)
        Jsi_DecrRefCount(interp, obj->__proto__);
    _JSI_MEMCLEAR(obj);


    Jsi_Free(obj);
}


/**************************** ARRAY ******************************/

Jsi_Value *jsi_ObjArrayLookup(Jsi_Interp *interp, Jsi_Obj *obj, const char *key) {
................................................................................
    return ++obj->refcnt;
}

int Jsi_ObjDecrRefCount(Jsi_Interp *interp, Jsi_Obj *obj)  {
    SIGASSERT(obj,OBJ);
    if (obj->refcnt<=0) {
#ifdef JSI_MEM_DEBUG
        Jsi_LogBug("Obj double free: %p", obj);
#endif
        return -2;
    }
    jsi_DebugObj(obj,"Decr", jsi_DebugValueCallIdx(), interp);
    int nref;
    if ((nref = --obj->refcnt) <= 0) {
        obj->refcnt = -1;
................................................................................
    if (!n && sb.st_size>0) {
        char fdir[PATH_MAX];
        const char* cr = cmdPtr->curRoot, *fpath;
        if (!Jsi_FSNative(interp, name) || ((fpath= Jsi_Realpath(interp, name, fdir))
            && cr && !Jsi_Strncmp(fpath, cr, Jsi_Strlen(cr))))
            rc = Jsi_FileRead(interp, name, dStr);
        else
            fprintf(stderr, "Skip read file %s\n", Jsi_ValueString(interp, name, NULL));
    }
    if (cmdPtr->noWarn)
        return JSI_OK;
    return rc;
}

static Jsi_RC jsi_wsTemplateFill(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, Jsi_Value *fn, Jsi_DString *dStr,
................................................................................
    } else {
        // Need to read data for non-native files.
        Jsi_DString dStr = {}, fStr = {};
        if (isMdi)
            rc = jsi_wsTemplateFill(interp, cmdPtr, fname, &fStr, (essi?1:0));
        else
            rc = jsi_wsFileRead(interp, fname, &fStr, cmdPtr);
        if (rc != JSI_OK)

            goto nofile;

        int hrc = jsi_wsServeHeader(pss, wsi, (int)Jsi_DSLength(&fStr), 200, Jsi_DSValue(&hStr), mime, &dStr);
        if (hrc>=0) {
            Jsi_DSAppendLen(&dStr, Jsi_DSValue(&fStr), Jsi_DSLength(&fStr));
            char *strVal = Jsi_DSValue(&dStr);
            int strLen = Jsi_DSLength(&dStr);
            hrc = jsi_wswrite(pss, wsi, (unsigned char*)strVal, strLen, LWS_WRITE_HTTP);
        }
................................................................................
    cmdPtr->includeFile = "include.shtml";
    if ((arg != NULL && !Jsi_ValueIsNull(interp,arg))
        && Jsi_OptionsProcess(interp, WSOptions, cmdPtr, arg, 0) < 0) {
bail:
        jsi_wswebsocketObjFree(interp, cmdPtr);
        return JSI_ERROR;
    }


    if (cmdPtr->headers && (Jsi_ValueGetLength(interp, cmdPtr->headers)%2)) {
        Jsi_LogError("Odd header length");
        goto bail;
    }
    const char *up = cmdPtr->urlPrefix, *ur = cmdPtr->urlRedirect;
    if (up && ur && Jsi_Strncmp(ur, up, Jsi_Strlen(up))) {
        Jsi_LogError("urlRedirect does not start with urlPrefix");
................................................................................

    if (cmdPtr->client) {
        struct lws_client_connect_info lci = {};
        lci.context = cmdPtr->context;
        lci.address = cmdPtr->address ? Jsi_ValueString(cmdPtr->interp, cmdPtr->address, NULL) : "127.0.0.1";
        lci.port = cmdPtr->port;
        lci.ssl_connection = cmdPtr->use_ssl;
        lci.path = cmdPtr->rootdir?Jsi_ValueString(cmdPtr->interp, cmdPtr->rootdir, NULL):"/";
        lci.host = cmdPtr->clientHost?cmdPtr->clientHost:lws_canonical_hostname( cmdPtr->context );
        lci.origin = cmdPtr->clientOrigin?cmdPtr->clientOrigin:"origin";
        lci.protocol = cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].name;
        lci.ietf_version_or_minus_one = cmdPtr->ietf_version;
#if (LWS_LIBRARY_VERSION_MAJOR>1)
        if (cmdPtr->post)
            lci.method = "POST";
................................................................................
                    }
                } else {
                    if (flags&JSI_EVAL_ARGV0) {
                        interp->argv0 = Jsi_ValueNewStringDup(interp, fname);
                        Jsi_IncrRefCount(interp, interp->argv0);
                    }
                }






















                



                tinput = input = Jsi_Open(interp, npath, (exists?"-r":"r"));

                if (!input) {
                    if (exists)
                        rc = JSI_OK;
                    //Jsi_LogError("Can not open '%s'", fname);
                    goto bail;
                }
            }
................................................................................
#undef _jsi_STACK
#undef _jsi_STACKIDX
#undef _jsi_THISIDX
#undef _jsi_TOP
#undef _jsi_TOQ

#endif
#if JSI__MAIN==1
/* JSI main program */
#ifndef JSI_AMALGAMATION
#include "jsi.h"
#endif
#include <string.h>
#include <stdlib.h>


int main(int argc, char **argv)
{
     // A replacement for shebang "#!/usr/bin/env".
    Jsi_DString sStr = {};
    FILE *fp = NULL;
    if (argc >= 3 && Jsi_Strchr(argv[1], ' ') && Jsi_Strstr(argv[1], "%s")) {
        Jsi_DString tStr = {};
        int i;
................................................................................
    Jsi_InterpOpts opts = {.argc=argc, .argv=argv};
    Jsi_Interp *interp = Jsi_Main(&opts);
    if (!interp) return opts.exitCode;
    Jsi_InterpDelete(interp);
    Jsi_DSFree(&sStr);
    exit(0);
}

#endif //JSI__MAIN==1



#endif //JSI_IN_AMALGAMATION






|







 







|







 







>







 







>
>
>
>
>
>
>




|
>
>







 







|
>
>
>

>







 







>

<







 







>







 







|
>







 







>
>







 







|





|
<



<
<
<
<
<
<
<
<
>
|
>
>
>
>
>
>
>
>
|
|
>
|
|
<
|
|
<
|
>
|
<
<
<
<
|
<
<
<
<
<
<
>
>
>
|
<
<
<
>
>
>
|
|
|
|
>
>
>
>
>
>
>
|
|
|
|
|
|
|
<
>
|
|
|
<
>
|
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
>
|
>
>
|
>
>
|
|
|
|
<
<
<
<
<
<
>
>
|
<
>
>
|
<
<
>
>
|
<
>
>
>
>
>
>
>
>
>
>
|
<
<
<
>
>
|
<
<
<
<
<
<
<
>
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

>
>
>
>
|
<
<
<
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

<







 







>







 







|
>


<
|



|
|
>
|
<
>
|
>
>
>







 







|
|

|
>


>


|



>


|




>


<
>




>


<
>
>
>
>
|
>
>
>
>
>
|
|
|
<
|
|
<
<
|
|
>
>
|
|
|
|
|
|
|
>
>
>
>







 







>
>

>
>







 







|







 







|







 







>
>
>

>







 







>
>
>
>
>







 







<
<
<
<







>
>
>
>
|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







>
>
>
>
>
>
>







 







>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>








>







 







>







 







|
<







 







>
>
>
>









|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>



>
>
>
>
>
>
>
>
|
|
|
>
>
|
>
>







 







>







 







|

|
|
|







 







|







 







|







 







|
|
|







|
>
>
>







 







|







 







|







 







<

>







 







>













>
>
>
>
>
>













>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







 







|
>
>







 







|







 







|







 







|
>

>







 







>
>







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>

>







 







<







>
|







 







>
|
>
>
>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
...
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
.....
13374
13375
13376
13377
13378
13379
13380
13381
13382
13383
13384
13385
13386
13387
13388
13389
13390
13391
13392
13393
13394
13395
13396
13397
13398
13399
13400
13401
.....
16144
16145
16146
16147
16148
16149
16150
16151
16152
16153
16154
16155
16156
16157
16158
16159
16160
16161
16162
16163
.....
16307
16308
16309
16310
16311
16312
16313
16314
16315

16316
16317
16318
16319
16320
16321
16322
.....
18507
18508
18509
18510
18511
18512
18513
18514
18515
18516
18517
18518
18519
18520
18521
.....
18549
18550
18551
18552
18553
18554
18555
18556
18557
18558
18559
18560
18561
18562
18563
18564
.....
19082
19083
19084
19085
19086
19087
19088
19089
19090
19091
19092
19093
19094
19095
19096
19097
.....
19108
19109
19110
19111
19112
19113
19114
19115
19116
19117
19118
19119
19120
19121

19122
19123
19124








19125
19126
19127
19128
19129
19130
19131
19132
19133
19134
19135
19136
19137
19138
19139

19140
19141

19142
19143
19144




19145






19146
19147
19148
19149



19150
19151
19152
19153
19154
19155
19156
19157
19158
19159
19160
19161
19162
19163
19164
19165
19166
19167
19168
19169
19170

19171
19172
19173
19174

19175
19176
19177
19178
19179
19180
19181
19182
19183
19184
19185
19186
19187
19188
19189
19190
19191
19192
19193
19194
19195
19196
19197
19198
19199
19200
19201
19202
19203






19204
19205
19206

19207
19208
19209


19210
19211
19212

19213
19214
19215
19216
19217
19218
19219
19220
19221
19222
19223



19224
19225
19226







19227
19228
19229


















19230
19231
19232
19233
19234
19235



19236
19237
19238
19239
19240
19241
19242
19243
19244
19245
19246
19247
19248
19249
19250
19251
19252
19253
19254
19255
19256
19257
19258
19259
19260
19261
19262
19263
19264
19265
19266
19267
19268
19269
19270
19271
19272
19273
19274
19275
19276
19277
19278
19279

19280
19281
19282
19283
19284
19285
19286
.....
19485
19486
19487
19488
19489
19490
19491
19492
19493
19494
19495
19496
19497
19498
19499
.....
19530
19531
19532
19533
19534
19535
19536
19537
19538
19539
19540

19541
19542
19543
19544
19545
19546
19547
19548

19549
19550
19551
19552
19553
19554
19555
19556
19557
19558
19559
19560
.....
19593
19594
19595
19596
19597
19598
19599
19600
19601
19602
19603
19604
19605
19606
19607
19608
19609
19610
19611
19612
19613
19614
19615
19616
19617
19618
19619
19620
19621
19622
19623
19624

19625
19626
19627
19628
19629
19630
19631
19632

19633
19634
19635
19636
19637
19638
19639
19640
19641
19642
19643
19644
19645

19646
19647


19648
19649
19650
19651
19652
19653
19654
19655
19656
19657
19658
19659
19660
19661
19662
19663
19664
19665
19666
19667
19668
19669
.....
19751
19752
19753
19754
19755
19756
19757
19758
19759
19760
19761
19762
19763
19764
19765
19766
19767
19768
19769
.....
19874
19875
19876
19877
19878
19879
19880
19881
19882
19883
19884
19885
19886
19887
19888
.....
19910
19911
19912
19913
19914
19915
19916
19917
19918
19919
19920
19921
19922
19923
19924
.....
20926
20927
20928
20929
20930
20931
20932
20933
20934
20935
20936
20937
20938
20939
20940
20941
20942
20943
20944
.....
24785
24786
24787
24788
24789
24790
24791
24792
24793
24794
24795
24796
24797
24798
24799
24800
24801
24802
24803
.....
24912
24913
24914
24915
24916
24917
24918




24919
24920
24921
24922
24923
24924
24925
24926
24927
24928
24929
24930
24931
24932
24933
24934
24935
24936
24937
.....
25044
25045
25046
25047
25048
25049
25050
25051
25052
25053
25054
25055
25056
25057
25058
25059
25060
25061
25062
25063
25064
25065
25066
25067
25068
25069
25070
25071
25072
25073
25074
.....
28572
28573
28574
28575
28576
28577
28578
28579
28580
28581
28582
28583
28584
28585
28586
.....
28609
28610
28611
28612
28613
28614
28615
28616
28617
28618
28619
28620
28621
28622
28623
28624
28625
28626
28627
28628
28629
.....
32366
32367
32368
32369
32370
32371
32372
32373
32374
32375
32376
32377
32378
32379
32380
32381
32382
.....
33530
33531
33532
33533
33534
33535
33536
33537
33538
33539
33540
33541
33542
33543
33544
33545
33546
33547
33548
33549
33550
33551
33552
33553
33554
33555
33556
33557
33558
33559
33560
33561
33562
33563
33564
33565
33566
33567
33568
33569
33570
33571
33572
33573
33574
33575
33576
33577
33578
33579
33580
33581
33582
33583
33584
33585
33586
33587
33588
33589
33590
33591
33592
33593
33594
33595
33596
33597
33598
33599
33600
33601
33602
33603
33604
33605
33606
33607
33608
33609
33610
33611
33612
.....
33615
33616
33617
33618
33619
33620
33621
33622
33623
33624
33625
33626
33627
33628
33629
.....
33639
33640
33641
33642
33643
33644
33645
33646

33647
33648
33649
33650
33651
33652
33653
.....
33681
33682
33683
33684
33685
33686
33687
33688
33689
33690
33691
33692
33693
33694
33695
33696
33697
33698
33699
33700
33701
33702
33703
33704
33705
33706
33707
33708
33709
33710
33711
33712
33713
33714
33715
33716
33717
33718
33719
33720
33721
33722
33723
33724
33725
33726
33727
33728
33729
33730
33731
33732
33733
33734
33735
33736
33737
33738
33739
33740
33741
33742
33743
33744
33745
33746
.....
33784
33785
33786
33787
33788
33789
33790
33791
33792
33793
33794
33795
33796
33797
33798
.....
36085
36086
36087
36088
36089
36090
36091
36092
36093
36094
36095
36096
36097
36098
36099
36100
36101
36102
36103
.....
36394
36395
36396
36397
36398
36399
36400
36401
36402
36403
36404
36405
36406
36407
36408
.....
36410
36411
36412
36413
36414
36415
36416
36417
36418
36419
36420
36421
36422
36423
36424
.....
36464
36465
36466
36467
36468
36469
36470
36471
36472
36473
36474
36475
36476
36477
36478
36479
36480
36481
36482
36483
36484
36485
36486
36487
36488
36489
36490
36491
.....
36554
36555
36556
36557
36558
36559
36560
36561
36562
36563
36564
36565
36566
36567
36568
.....
36599
36600
36601
36602
36603
36604
36605
36606
36607
36608
36609
36610
36611
36612
36613
.....
36735
36736
36737
36738
36739
36740
36741

36742
36743
36744
36745
36746
36747
36748
36749
36750
.....
37073
37074
37075
37076
37077
37078
37079
37080
37081
37082
37083
37084
37085
37086
37087
37088
37089
37090
37091
37092
37093
37094
37095
37096
37097
37098
37099
37100
37101
37102
37103
37104
37105
37106
37107
37108
37109
37110
37111
37112
37113
37114
37115
37116
37117
37118
37119
37120
37121
37122
37123
37124
37125
37126
37127
.....
37156
37157
37158
37159
37160
37161
37162
37163
37164
37165
37166
37167
37168
37169
37170
37171
37172
37173
37174
37175
37176
37177
37178
37179
37180
37181
37182
37183
37184
37185
37186
37187
37188
37189
37190
.....
37994
37995
37996
37997
37998
37999
38000
38001
38002
38003
38004
38005
38006
38007
38008
.....
38274
38275
38276
38277
38278
38279
38280
38281
38282
38283
38284
38285
38286
38287
38288
38289
38290
.....
38361
38362
38363
38364
38365
38366
38367
38368
38369
38370
38371
38372
38373
38374
38375
.....
51317
51318
51319
51320
51321
51322
51323
51324
51325
51326
51327
51328
51329
51330
51331
.....
51837
51838
51839
51840
51841
51842
51843
51844
51845
51846
51847
51848
51849
51850
51851
51852
51853
51854
.....
53128
53129
53130
53131
53132
53133
53134
53135
53136
53137
53138
53139
53140
53141
53142
53143
.....
53216
53217
53218
53219
53220
53221
53222
53223
53224
53225
53226
53227
53228
53229
53230
.....
65518
65519
65520
65521
65522
65523
65524
65525
65526
65527
65528
65529
65530
65531
65532
65533
65534
65535
65536
65537
65538
65539
65540
65541
65542
65543
65544
65545
65546
65547
65548
65549
65550
65551
65552
65553
65554
65555
65556
65557
65558
65559
.....
65730
65731
65732
65733
65734
65735
65736

65737
65738
65739
65740
65741
65742
65743
65744
65745
65746
65747
65748
65749
65750
65751
65752
.....
65779
65780
65781
65782
65783
65784
65785
65786
65787
65788
65789
65790
65791
/* jsi.h : External API header file for Jsi. */
#ifndef __JSI_H__
#define __JSI_H__

#define JSI_VERSION_MAJOR   2
#define JSI_VERSION_MINOR   7
#define JSI_VERSION_RELEASE 2

#define JSI_VERSION (JSI_VERSION_MAJOR + ((Jsi_Number)JSI_VERSION_MINOR/100.0) + ((Jsi_Number)JSI_VERSION_RELEASE/10000.0))

#ifndef JSI_EXTERN
#define JSI_EXTERN extern
#endif

................................................................................

JSI_EXTERN Jsi_Value* Jsi_ValueNewNull(Jsi_Interp *interp); /*STUB = 100*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewBoolean(Jsi_Interp *interp, int bval); /*STUB = 101*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewNumber(Jsi_Interp *interp, Jsi_Number n); /*STUB = 102*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewBlob(Jsi_Interp *interp, uchar *s, uint len); /*STUB = 103*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewString(Jsi_Interp *interp, const char *s, int len); /*STUB = 104*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringKey(Jsi_Interp *interp, const char *s); /*STUB = 105*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringConst(Jsi_Interp *interp, const char *s, int len); /*STUB = 409*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringDup(Jsi_Interp *interp, const char *s); /*STUB = 106*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewArray(Jsi_Interp *interp, const char **items, int count); /*STUB = 107*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewObj(Jsi_Interp *interp, Jsi_Obj *o) ; /*STUB = 108*/
#define Jsi_ValueNewBlobString(interp, s) Jsi_ValueNewBlob(interp, (uchar*)s, Jsi_Strlen(s))

JSI_EXTERN Jsi_RC Jsi_GetStringFromValue(Jsi_Interp* interp, Jsi_Value *value, const char **s); /*STUB = 109*/
JSI_EXTERN Jsi_RC Jsi_GetNumberFromValue(Jsi_Interp* interp, Jsi_Value *value, Jsi_Number *n); /*STUB = 110*/
................................................................................
JSI_EXTERN Jsi_RC Jsi_RegExpMatch( Jsi_Interp *interp,  Jsi_Value *pattern, const char *str, int *rc, Jsi_DString *dStr); /*STUB = 189*/
JSI_EXTERN Jsi_RC Jsi_RegExpMatches(Jsi_Interp *interp, Jsi_Value *pattern, const char *str, Jsi_Value *ret); /*STUB = 190*/
JSI_EXTERN bool Jsi_GlobMatch(const char *pattern, const char *string, int nocase); /*STUB = 191*/
JSI_EXTERN char* Jsi_FileRealpath(Jsi_Interp *interp, Jsi_Value *path, char *newpath); /*STUB = 192*/
JSI_EXTERN char* Jsi_FileRealpathStr(Jsi_Interp *interp, const char *path, char *newpath); /*STUB = 193*/
JSI_EXTERN char* Jsi_NormalPath(Jsi_Interp *interp, const char *path, Jsi_DString *dStr); /*STUB = 194*/
JSI_EXTERN char* Jsi_ValueNormalPath(Jsi_Interp *interp, Jsi_Value *path, Jsi_DString *dStr); /*STUB = 195*/
JSI_EXTERN Jsi_RC Jsi_PathNormalize(Jsi_Interp *interp, Jsi_Value **pathPtr); /*STUB = 410*/ /*LAST*/
JSI_EXTERN Jsi_RC Jsi_JSONParse(Jsi_Interp *interp, const char *js, Jsi_Value **ret, int flags); /*STUB = 196*/
JSI_EXTERN Jsi_RC Jsi_JSONParseFmt(Jsi_Interp *interp, Jsi_Value **ret, const char *fmt, ...) /*STUB = 197*/ __attribute__((format (printf,3,4)));
JSI_EXTERN char* Jsi_JSONQuote(Jsi_Interp *interp, const char *str, int len, Jsi_DString *dStr); /*STUB = 198*/
JSI_EXTERN Jsi_RC Jsi_EvalString(Jsi_Interp* interp, const char *str, int flags); /*STUB = 199*/
JSI_EXTERN Jsi_RC Jsi_EvalFile(Jsi_Interp* interp, Jsi_Value *fname, int flags); /*STUB = 200*/
JSI_EXTERN Jsi_RC Jsi_EvalCmdJSON(Jsi_Interp *interp, const char *cmd, const char *jsonArgs, Jsi_DString *dStr, int flags); /*STUB = 201*/
JSI_EXTERN Jsi_RC Jsi_EvalZip(Jsi_Interp *interp, const char *exeFile, const char *mntDir, int *jsFound); /*STUB = 202*/
................................................................................
    jsi_ScopeChain *scope_save;         /* saved scope (used in catch block/with block)*/
    Jsi_Value *curscope_save;           /* saved current scope */
    struct jsi_TryList *next;
    bool inCatch;
    bool inFinal;
} jsi_TryList;

typedef enum {
    jsi_safe_None,
    jsi_safe_Read,
    jsi_safe_Write,
    jsi_safe_Write2
} jsi_safe_mode;

struct Jsi_Interp {
#ifdef JSI_HAS_SIG
    jsi_Sig sig;
#endif
    bool isSafe, startSafe;
    jsi_safe_mode safeMode;
    int iskips;
    Jsi_Value *safeReadDirs;
    Jsi_Value *safeWriteDirs;
    const char *safeExecPattern;
    Jsi_DebugInterp debugOpts;
    struct jsi_TryList *tryList;
    bool deleting;
    bool destroying;
................................................................................
    assert(v->refCnt>=0);
    jsi_DebugValue(v,"Incr", jsi_DebugValueCallIdx(), interp);
    return ++(v->refCnt);
}

int Jsi_DecrRefCount(Jsi_Interp* interp, Jsi_Value *v) {
    SIGASSERT(v,VALUE);
    if (v->refCnt<=0) {
#ifdef JSI_MEM_DEBUG
        fprintf(stderr, "Value decr with ref %d: VD.Idx=%d\n", v->refCnt, v->VD.Idx);
#endif
        return -2;
    }
    int ref;
    jsi_DebugValue(v,"Decr", jsi_DebugValueCallIdx(), interp);
    if ((ref = --(v->refCnt)) <= 0) {
        v->refCnt = -1;
        Jsi_ValueFree(interp, v);
    }
    return ref;
................................................................................
#ifdef JSI_MEM_DEBUG
    //if (v->VD.interp != interp)  //TODO: InterpAliasCmd leaking Values.
     //   fprintf(stderr, "cross interp delete: %p\n", v);
    if (v->VD.hPtr) {
        if (!Jsi_HashEntryDelete(v->VD.hPtr))
            fprintf(stderr, "Value not in hash\n");
    }
    memset(v, 0, (sizeof(*v)-sizeof(v->VD)));
#endif

    Jsi_Free(v);
}

/* Reset a value back to undefined, releasing string/obj if necessary. */
void Jsi_ValueReset(Jsi_Interp *interp, Jsi_Value **vPtr) {
    Jsi_Value *v = *vPtr;
    SIGASSERTV(v,VALUE);
................................................................................
    JSI_OPT(BOOL,  jsi_SubOptions, mutexUnlock, .help="Unlock own mutex when evaling in other interps (true)", jsi_IIOF),
    JSI_OPT(BOOL,  jsi_SubOptions, noproto,     .help="Disable support of the OOP symbols:  __proto__, prototype, constructor, etc"),
    JSI_OPT(BOOL,  jsi_SubOptions, noReadline,  .help="In interactive mode disable use of readline" ),
    JSI_OPT(BOOL,  jsi_SubOptions, outUndef,     .help="In interactive mode output result values that are undefined"),
    JSI_OPT_END(jsi_SubOptions, .help="Lesser sub-feature options")
};

static const char *jsi_SafeModeStrs[] = { "none", "read", "write", "write2", NULL };
static const char *jsi_TypeChkStrs[] = { "parse", "run", "all", "error", "strict", "noundef", "nowith", "funcsig", NULL };
static const char *jsi_callTraceStrs[] = { "funcs", "cmds", "new", "return", "args", "notrunc", "noparent", "full", "before", NULL};
const char *jsi_AssertModeStrs[] = { "throw", "log", "puts", NULL};

static Jsi_OptionSpec InterpOptions[] = {
    JSI_OPT(ARRAY, Jsi_Interp, args,        .help="The console.arguments for interp", jsi_IIOF),
    JSI_OPT(BOOL,  Jsi_Interp, asserts,     .help="Enable assert" ),
................................................................................
    JSI_OPT(FUNC,  Jsi_Interp, onComplete,  .help="Function to return commands completions for interactive mode.  Default uses Info.completions ", .flags=0, .custom=0, .data=(void*)"prefix:string, start:number, end:number" ),
    JSI_OPT(FUNC,  Jsi_Interp, onEval,      .help="Function to get control for interactive evals", .flags=0, .custom=0, .data=(void*)"cmd:string" ),
    JSI_OPT(FUNC,  Jsi_Interp, onExit,      .help="Command to call in parent on exit, returns true to continue", jsi_IIOF , .custom=0, .data=(void*)""),
    JSI_OPT(INT,   Jsi_Interp, opTrace,     .help="Set debugging level for OPCODE execution"),
    JSI_OPT(ARRAY, Jsi_Interp, pkgDirs,     .help="list of library directories for require() to search" ),
    JSI_OPT(BOOL,  Jsi_Interp, profile,     .help="On exit generate profile of function calls"),
    JSI_OPT(CUSTOM,Jsi_Interp, recvCallback,.help="Command to recv 'send' msgs from parent interp", .flags=0, .custom=Jsi_Opt_SwitchParentFunc, .data=(void*)"msg:string"),
    JSI_OPT(VALUE, Jsi_Interp, retValue,    .help="Return value from last eval", jsi_IIRO),
    JSI_OPT(CUSTOM,Jsi_Interp, safeMode,    .help="Set isSafe mode and setup safeReadDirs and/or safeWriteDirs for pwd and script-dir", jsi_IIOF, .custom=Jsi_Opt_SwitchEnum, .data=jsi_SafeModeStrs ),
    JSI_OPT(ARRAY, Jsi_Interp, safeReadDirs,.help="In safe mode, files/dirs to allow reads from", jsi_IIOF),
    JSI_OPT(ARRAY, Jsi_Interp, safeWriteDirs,.help="In safe mode, files/dirs to allow writes to", jsi_IIOF),
    JSI_OPT(STRKEY, Jsi_Interp,safeExecPattern,.help="In safe mode, regexp pattern allow exec of commands", jsi_IIOF),
    JSI_OPT(STRKEY,Jsi_Interp, scriptStr,   .help="Interp init script string", jsi_IIOF),
    JSI_OPT(STRING,Jsi_Interp, scriptFile,  .help="Interp init script file"),
    JSI_OPT(STRING,Jsi_Interp, stdinStr,    .help="String to use as stdin for console.input()"),
    JSI_OPT(STRING,Jsi_Interp, stdoutStr,   .help="String to collect stdout for puts()"),
................................................................................
        argv = opts->argv;
    }
    if (!interp)
        interp = Jsi_InterpNew(opts);
    if (!interp)
        return NULL;
    Jsi_InterpOnDelete(interp, &jsi_InterpDelete, (void*)&jsi_InterpDelete);
    argc -= interp->iskips;
    argv += interp->iskips;

#ifndef NO_JAZ
    /* Mount zip at end of executable */
    Jsi_Value *v = Jsi_Executable(interp);
    const char *exeFile = (v?Jsi_ValueString(interp, v, NULL):NULL);
    int jsFound = 0;
    if (v && (argc != 2 || Jsi_Strcmp(argv[1], "--nozvfs"))) {
................................................................................
            else if (rc != 0) {
                fprintf(stderr, "Error\n");
                return jsi_DoExit(interp, 1);
            }
        }
    }
#endif
    const char *ai1, *iext = (argc<=1?NULL:Jsi_Strrchr(argv[1], '.'));
    if (interp->selfZvfs && iext && Jsi_Strcmp(iext,".fossil")==0) {
        rc = Jsi_EvalString(interp, "runModule('Jsi_Archive');", JSI_EVAL_ISMAIN);
        goto done;
    }
    Jsi_ShiftArgs(interp, NULL);
    if (argc <= 1) {

        if (interp->opts.no_interactive)
            return interp;
        rc = Jsi_Interactive(interp, JSI_OUTPUT_QUOTE|JSI_OUTPUT_NEWLINES);








        goto done;
    }
    ai1 = argv[1];
    if (!Jsi_Strcmp(ai1, "-help")) {
        dohelp:
        puts("USAGE:\n  jsish [PREFIX-OPTS] [COMMAND-OPTS|FILE] ...\n"
          "\nPREFIX-OPTS:\n"
          "  --F\t\tTrace all function calls/returns.\n"
          "  --I OPT:VAL\tInterp option: equivalent to Interp.conf({OPT:VAL}).\n"
          "  --T OPT\tTypecheck option: equivalent to \"use OPT\".\n"
          "  --U\t\tDisplay unittest output, minus pass/fail compare.\n"
          "  --V\t\tSame as --U, but adds file and line number to output.\n"
          "\nCOMMAND-OPTS:\n"
          "  -a\t\tArchive: mount an archive (zip, sqlar or fossil repo) and run module.\n"
          "  -c\t\tCData: generate .c or JSON output from a .jsc description.\n"

          "  -d\t\tDebug: console script debugger.\n"
          "  -e STRING\tEval: run javascript in STRING and exit.\n"

          "  -g\t\tGendeep: generate html output from markdeep source.\n"
          "  -h\t\tHelp: show this help.\n"
          "  -m MOD\tModule: invoke runModule, after source if file.\n"




          "  -s\t\tSafe: runs script in safe sub-interp.\n"






          "  -u\t\tUnitTest: test script file(s) or directories .js/.jsi files.\n"
          "  -w\t\tWget: web client to download file from url.\n"
          "  -v\t\tVersion: show version info.\n"
          "  -z\t\tZip: append/manage zip files at end of executable.\n"



          "  -D\t\tDebugUI: web-gui script debugger.\n"
          "  -S\t\tSqliteUI: web-gui for sqlite database file.\n"
          "  -W\t\tWebsrv: web server to serve out content.\n"
          "\nInterp options may also be set via the environment eg. JSI_INTERP_OPTS='{coverage:true}'\n"
           );
        return jsi_DoExit(interp, 1);
    }
    if (!Jsi_Strcmp(ai1, "-version"))
        ai1 = "-v";
    if (ai1[0] == '-') {
        switch (ai1[1]) {
            case 'a':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Archive');", JSI_EVAL_ISMAIN);
                break;
            case 'c':
                rc = Jsi_EvalString(interp, "runModule('Jsi_CData');", JSI_EVAL_ISMAIN);
                break;
            case 'd':
                interp->debugOpts.isDebugger = 1;
                rc = Jsi_EvalString(interp, "runModule('Jsi_Debug');", JSI_EVAL_ISMAIN);
                break;

            case 'D':
                interp->debugOpts.isDebugger = 1;
                rc = Jsi_EvalString(interp, "runModule('Jsi_DebugUI');", JSI_EVAL_ISMAIN);
                break;

            case 'e':
                if (argc != 3)
                    rc = Jsi_LogError("missing argument");
                else
                    rc = Jsi_EvalString(interp, argv[2], JSI_EVAL_NOSKIPBANG);
                break;
            case 'g':
                rc = Jsi_EvalString(interp, "runModule('Jsi_GenDeep');", JSI_EVAL_ISMAIN);
                break;
            case 'h':
                goto dohelp;
            case 'm':
                if (argc <= 2)
                    rc = Jsi_LogError("missing argument");
                else if (argv[2][0] == '-')
                    rc = Jsi_EvalString(interp, "runModule('Jsi_Module');", JSI_EVAL_ISMAIN);
                else {
                    Jsi_DString dStr = {};
                    const char *cps, *cpe;
                    cps = Jsi_Strrchr(argv[2], '/');
                    if (cps) cps++; else cps = argv[2];
                    cpe = Jsi_Strrchr(cps, '.');
                    int len = (cpe?cpe-cps:(int)Jsi_Strlen(cps));
                    if (cpe)
                        Jsi_DSPrintf(&dStr, "source(\"%s\");", argv[2]);
                    Jsi_DSPrintf(&dStr, "puts(runModule(\"%.*s\",console.args.slice(1)));", len, cps);
                    rc = Jsi_EvalString(interp, Jsi_DSValue(&dStr), JSI_EVAL_NOSKIPBANG);
                    Jsi_DSFree(&dStr);
                }






                break;
            case 's':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Safe');", JSI_EVAL_ISMAIN);

                break;
            case 'S':
                rc = Jsi_EvalString(interp, "runModule('Jsi_SqliteUI');", JSI_EVAL_ISMAIN);


                break;
            case 'u':
                rc = Jsi_EvalString(interp, "exit(runModule('Jsi_UnitTest'));", JSI_EVAL_ISMAIN);

                break;
            case 'v': {
                char str[200] = "\n";
                Jsi_Channel chan = Jsi_Open(interp, Jsi_ValueNewStringKey(interp, "/zvfs/lib/sourceid.txt"), "r");
                if (chan)
                    Jsi_Read(interp, chan, str, sizeof(str));
                printf("%u.%u.%u %.4" JSI_NUMGFMT " %s", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE, Jsi_Version(), str);
                return jsi_DoExit(interp, 1);
            }
            case 'w':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Wget');", JSI_EVAL_ISMAIN);



                break;
            case 'W':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Websrv');", JSI_EVAL_ISMAIN);







                break;
            case 'z':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Zip');", JSI_EVAL_ISMAIN);


















                break;
            default:
                puts("usage: jsish [ --I OPT:VAL | --T OPT | --U | --V | --F ] | -e STRING |\n\t"
                "| -a | -c | -d | -D | -h | -m | -s | -S | -u  | -v |-w | -W | -z | FILE ...\nUse -help for long help.");
                return jsi_DoExit(interp, 1);
        }



    } else {
        const char *ext = Jsi_Strrchr(argv[1], '.');

        /* Support running "main.jsi" from a zip file. */
        if (ext && (Jsi_Strcmp(ext,".zip")==0 ||Jsi_Strcmp(ext,".jsz")==0 ) ) {
            rc = Jsi_EvalZip(interp, argv[1], NULL, &jsFound);
            if (rc<0) {
                fprintf(stderr, "zip mount failed\n");
                return jsi_DoExit(interp, 1);
            }
            if (!(jsFound&JSI_ZIP_MAIN)) {
                fprintf(stderr, "main.jsi not found\n");
                return jsi_DoExit(interp, 1);
            }
        } else {
            if (argc>1) {
                jsi_vf = Jsi_ValueNewStringKey(interp, argv[1]);
                Jsi_IncrRefCount(interp, jsi_vf);
            }
            rc = Jsi_EvalFile(interp, jsi_vf, JSI_EVAL_ARGV0|JSI_EVAL_AUTOINDEX);
            if (jsi_vf) {
                Jsi_DecrRefCount(interp, jsi_vf);
                jsi_vf = NULL;
            }

        }
    }
    if (jsi_deleted) //TODO: rationalize jsi_deleted, jsi_exitCode, etc
        return jsi_DoExit(rc==JSI_EXIT?NULL:interp, jsi_exitCode);
    if (rc == 0) {
        /* Skip output from an ending semicolon which evaluates to undefined */
        Jsi_Value *ret = Jsi_ReturnValue(interp);
        if (!Jsi_ValueIsType(interp, ret, JSI_VT_UNDEF)) {
            Jsi_DString dStr = {};
            fputs(Jsi_ValueGetDString(interp, ret, &dStr, 0), stdout);
            Jsi_DSFree(&dStr);
            fputs("\n", stdout);
        }
    } else {
        if (!interp->parent && !interp->isHelp)
            fputs("ERROR\n", stderr);
        return jsi_DoExit(interp, 1);
    }


done:
    if (rc == JSI_EXIT) {
        if (opts)
            opts->exitCode = jsi_exitCode;
        return NULL;
    }
    if (jsi_deleted == 0 && interp->opts.auto_delete) {
................................................................................
        iopts->interp = interp;
        interp->opts = *iopts;
    }
    interp->logOpts.file = 1;
    interp->logOpts.func = 1;
    int argc = interp->opts.argc;
    char **argv = interp->opts.argv;
    char *argv0 = (argv?argv[0]:NULL);
    interp->parent = parent;
    interp->topInterp = (parent == NULL ? interp: parent->topInterp);
    if (jsiIntData.mainInterp == NULL)
        jsiIntData.mainInterp = interp->topInterp;
    interp->mainInterp = jsiIntData.mainInterp; // The first interps handles exit.
    interp->memDebug = interp->opts.mem_debug;
    if (parent) {
................................................................................
    const char *ocp2;
    if (ocp && ((ocp2=Jsi_Strstr(ocp,"memDebug:"))))
        interp->memDebug=strtol(ocp+sizeof("memDebug:"), NULL, 0);
    if (ocp && ((ocp2=Jsi_Strstr(ocp,"compat:"))))
        interp->subOpts.compat=(ocp[sizeof("compat:")]=='t');
    for (iocnt = 1; (iocnt+1)<argc; iocnt+=2)
    {
        const char *aio = argv[iocnt];
        if (Jsi_Strcmp(aio, "--T") == 0) {
            continue;
        }

        if (Jsi_Strcmp(aio, "--F") == 0 || Jsi_Strcmp(aio, "--U") == 0 || Jsi_Strcmp(aio, "--V") == 0) {
            iocnt--;
            continue;
        }
        if (!Jsi_Strcmp(aio, "--I")) {
            const char *aio2 = argv[iocnt+1];
            if (!Jsi_Strncmp("memDebug:", aio2, sizeof("memDebug")))
                interp->memDebug=strtol(aio2+sizeof("memDebug"), NULL, 0);

            else if (!Jsi_Strncmp("compat", aio2, sizeof("compat")))
                interp->subOpts.compat=strtol(aio2+sizeof("compat"), NULL, 0);
            continue;
        }
        break;
    }
    SIGINIT(interp,INTERP);
    interp->NullValue = Jsi_ValueNewNull(interp);
    Jsi_IncrRefCount(interp, interp->NullValue);
#ifdef __WIN32
    Jsi_DString cwdStr;
    Jsi_DSInit(&cwdStr);
................................................................................
    mapType = JSI_MAP_TREE;
#endif

    if (interp == jsiIntData.mainInterp || interp->threadId != jsiIntData.mainInterp->threadId) {
        interp->strKeyTbl = Jsi_MapNew(interp,  mapType, JSI_KEYS_STRING, NULL);
        interp->subOpts.privKeys = 1;
    }
    // Handle interp options: -T value and -Ixxx value
    for (iocnt = 1; (iocnt+1)<argc && !interp->parent; iocnt+=2)
    {
        const char *aio = argv[iocnt];
        if (Jsi_Strcmp(aio, "--F") == 0) {
            interp->traceCall |= (jsi_callTraceFuncs |jsi_callTraceArgs |jsi_callTraceReturn | jsi_callTraceBefore | jsi_callTraceFullPath);
            iocnt--;
            interp->iskips++;
            continue;
        }
        if (Jsi_Strcmp(aio, "--U") == 0) {
            interp->asserts = 1;
            interp->unitTest = 1;
            iocnt--;
            interp->iskips++;
            continue;
        }
        if (Jsi_Strcmp(aio, "--V") == 0) {
            interp->asserts = 1;
            interp->unitTest = 5;
            interp->tracePuts = 1;
            iocnt--;
            interp->iskips++;
            continue;
        }

        if (Jsi_Strcmp(aio, "--T") == 0) {
            if (jsi_ParseTypeCheckStr(interp, argv[iocnt+1]) != JSI_OK) {
                Jsi_InterpDelete(interp);
                return NULL;
            }
            interp->iskips+=2;
            continue;
        }

        if (!Jsi_Strcmp(aio, "--I"))  {
            bool bv = 1;
            char *aio2 = argv[iocnt+1], *aioc = Jsi_Strchr(aio2, ':'),
                argNamS[50], *argNam = aio2;
            const char *argVal;
            if (aioc) {
                argNam = argNamS;
                argVal = aioc+1;
                snprintf(argNamS, sizeof(argNamS), "%.*s", (int)(aioc-aio2), aio2);
            }
            DECL_VALINIT(argV);
            Jsi_Value *argValue = &argV;
            Jsi_Number dv;

            if (!aioc || Jsi_GetBool(interp, argVal, &bv) == JSI_OK) {
                Jsi_ValueMakeBool(interp, &argValue, bv);


            } else if (!Jsi_Strcmp("null", argVal)) {
                Jsi_ValueMakeNull(interp, &argValue);
            } else if (Jsi_GetDouble(interp, argVal, &dv) == JSI_OK) {
                Jsi_ValueMakeNumber(interp, &argValue, dv);
            } else {
                Jsi_ValueMakeStringKey(interp, &argValue, argVal);
            }
            if (JSI_OK != Jsi_OptionsSet(interp, InterpOptions, interp, argNam, argValue, 0)) {
                Jsi_InterpDelete(interp);
                return NULL;
            }
            interp->iskips+=2;
            continue;
        }
        break;
    }
    if (!interp->strKeyTbl)
        interp->strKeyTbl = jsiIntData.mainInterp->strKeyTbl;
    if (opts) {
        interp->inopts = opts = Jsi_ValueDupJSON(interp, opts);
        if (Jsi_OptionsProcess(interp, InterpOptions, interp, opts, 0) < 0) {
            Jsi_DecrRefCount(interp, opts);
................................................................................
    interp->threadId = Jsi_CurrentThread();
    if (interp->parent && interp->subthread==0 && interp->threadId != interp->parent->threadId) {
        interp->threadId = interp->parent->threadId;
#ifndef JSI_MEM_DEBUG
        Jsi_LogWarn("non-threaded sub-interp created by different thread than parent");
#endif
    }
    if (interp->safeMode != jsi_safe_None)
        interp->isSafe = interp->startSafe = 1;
    if (!interp->parent) {
        if (interp->isSafe)
            interp->startSafe = 1;
        if (interp->debugOpts.msgCallback)
            Jsi_LogWarn("ignoring msgCallback");
        if (interp->debugOpts.putsCallback)
            Jsi_LogWarn("ignoring putsCallback");
        if (interp->busyCallback)
            Jsi_LogWarn("ignoring busyCallback");
        if (interp->recvCallback)
................................................................................

    if (!interp->isSafe) {
        JSIDOINIT(Load);
#if JSI__SIGNAL==1
        JSIDOINIT(Signal);
#endif
    }
    if (interp->isSafe == 0 || interp->startSafe || interp->safeWriteDirs!=NULL || interp->safeReadDirs!=NULL) {
#if JSI__FILESYS==1
        JSIDOINIT(FileCmds);
        JSIDOINIT(Filesys);
#endif
    }
#if JSI__SQLITE==1
    JSIDOINIT2(Sqlite);
................................................................................
    if (JSI_USER_EXTENSION (interp, 0) != JSI_OK) {
        fprintf(stderr, "extension load failed");
        return jsi_DoExit(interp, 1);
    }
#endif
    Jsi_PkgProvide(interp, "Jsi", JSI_VERSION, NULL);
    if (argc > 0) {
        char *ss = argv0;
        char epath[PATH_MAX] = ""; // Path of executable
#ifdef __WIN32

        if (GetModuleFileName(NULL, epath, sizeof(epath))>0)
            ss = epath;
#else
#ifndef PROC_SELF_DIR
................................................................................
    if (!mntDir)
        ret = Jsi_ValueNew(interp);
    else {
        vmnt = Jsi_ValueNewStringKey(interp, mntDir);
        Jsi_IncrRefCount(interp, vmnt);
        Jsi_HashSet(interp->genValueTbl, vmnt, vmnt);
    }
    bool osafe = interp->isSafe;
    if (interp->startSafe)
        interp->isSafe = 0;
    rc =Jsi_Mount(interp, vexe, vmnt, &ret);
    interp->isSafe = osafe;
    if (rc != JSI_OK)
        return rc;
    Jsi_DString dStr, bStr;
    Jsi_DSInit(&dStr);
    Jsi_DSInit(&bStr);
    if (!mntDir) {
        mntDir = Jsi_KeyAdd(interp, Jsi_ValueString(interp, ret, NULL));
................................................................................
    if (fsPtr == NULL || !fsPtr->chmodProc) return -1;
    return fsPtr->chmodProc(interp, path, mode);
}
int Jsi_Access(Jsi_Interp *interp, Jsi_Value* path, int mode) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->accessProc) return -1;
    if (interp->isSafe && fsPtr && fsPtr == &jsiFilesystem) {
        int aflag = (mode&W_OK ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);
        if (Jsi_InterpAccess(interp, path, aflag) != JSI_OK)
            return -1;
    }
    return fsPtr->accessProc(interp, path, mode);
}
int Jsi_Remove(Jsi_Interp *interp, Jsi_Value* path, int flags) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->removeProc) return -1;
    return fsPtr->removeProc(interp, path, flags);
................................................................................
    {
        Jsi_LogError("can not open directory: %s", fileName);
        goto done;
    }
    fsPtr = Jsi_FilesystemForPath(interp, file, &data);
    writ = (Jsi_Strchr(s,'w') || Jsi_Strchr(s,'a') || Jsi_Strchr(s,'+'));
    aflag = (writ ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);




    if (fsPtr && fsPtr != &jsiFilesystem) {
        ch = fsPtr->openProc(interp, file, Mode);
        if (ch)
            ch->isNative = 0;
        else
            Jsi_LogError("File open failed '%s'", fileName);
    } else {
        if (interp->isSafe && Jsi_InterpAccess(interp, file, aflag) != JSI_OK) {
            Jsi_LogError("%s access denied: %s", writ?"write":"read", fileName);
            goto done;
        }
            FILE *fp = fopen(fileName, Mode);
        fsPtr = &jsiFilesystem;
        if (!fp) {
            if (!quiet)
                Jsi_LogError("File open failed '%s'", fileName);
            goto done;
        }
        ch = (Jsi_Chan *)Jsi_Calloc(1,sizeof(*ch));
................................................................................
    if (!path) return NULL;
    return Jsi_FileRealpathStr(interp, path, newname);
}

static char* jsi_FSRealPathProc(Jsi_Interp *interp, Jsi_Value *src, char *newPath) {
    return Jsi_FileRealpath(interp, src, newPath);
}

Jsi_RC Jsi_PathNormalize(Jsi_Interp *interp, Jsi_Value **pathPtr) {
    Jsi_Value *path = *pathPtr;
    if (!path) {
        Jsi_DString dStr = {};
        *pathPtr = Jsi_ValueNewStringDup(interp, Jsi_GetCwd(interp, &dStr));
        Jsi_IncrRefCount(interp, *pathPtr);
        Jsi_DSFree(&dStr);
    } else {
        const char *rn = Jsi_Realpath(interp, path, NULL);
        if (!rn) return JSI_ERROR;
        Jsi_DecrRefCount(interp, *pathPtr);
        *pathPtr = Jsi_ValueNewString(interp, rn, -1);
        Jsi_IncrRefCount(interp, *pathPtr);
    }
    return JSI_OK;
}

char *Jsi_Realpath(Jsi_Interp *interp, Jsi_Value *src, char *newname)
{
    /* TODO: resolve pwd first. */
    void *data;
    const char *cp = NULL;
    Jsi_Filesystem *fsPtr;
................................................................................

#ifdef __WIN32
#define dlsym(l,s) GetProcAddress(l,s)
#define dlclose(l) FreeLibrary(l)
#include <windows.h>
#else
#include <dlfcn.h>
#include <sys/statvfs.h>
#endif

#ifndef RTLD_NOW
    #define RTLD_NOW 0
#endif
#ifndef RTLD_LOCAL
    #define RTLD_LOCAL 0
................................................................................
Jsi_RC Jsi_LoadLibrary(Jsi_Interp *interp, const char *pathName, bool noInit)
{
    if (interp->noLoad)
        return Jsi_LogError("shared lib load is disabled");
#ifdef __WIN32
    HMODULE handle = LoadLibrary(pathName);
#else
#if JSI__LOADNOEXEC
#warning "NOTE: Allowing load from noexec FS"
#else
    struct statvfs vfs;
    if (statvfs(pathName, &vfs) == 0 && vfs.f_flag&ST_NOEXEC)
        return Jsi_LogError("shared libs may not be loaded from a noexec FS");
#endif
    void *handle = dlopen(pathName, RTLD_NOW | RTLD_LOCAL);
#endif
    if (handle == NULL) {
        // FYI: Valgrind shows a mem-leak here.
        Jsi_LogError("loading extension \"%s\": %s", pathName, dlerror());
        return JSI_ERROR;
    }
................................................................................
char *strptime(const char *buf, const char *fmt, struct tm *tm);
#endif
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <signal.h>
#include <limits.h>
#ifndef __WIN32
#include <sys/wait.h>
#endif

static Jsi_RC consoleInputCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char buf[1024];
    char *cp, *p = buf;
    buf[0] = 0;
................................................................................
        pid = (Jsi_Number)getppid();
    else
        pid = (Jsi_Number)getpid();
    Jsi_ValueMakeNumber(interp, ret, pid);
    return JSI_OK;
#endif
}

// Replacement for popen when there is no /bin/sh.  It uses execvp.
static FILE *jsi_popen(char **cmdv, const char *type, int *cpid)
{
#ifdef __WIN32
    return NULL;
#else
    int fd[2], pid, find = (*type != 'w' ? 1 : 0);
    if (!cmdv[0] || pipe(fd)<0)
        return NULL;
    pid = fork();
    if (pid<0) {
        close(fd[0]);
        close(fd[1]);
        return NULL;
    }

    if (pid) {
        *cpid = pid;
        close(fd[find]);
        return fdopen(fd[1-find], type);
    }
    if (fd[find] != find) {
        dup2(fd[find], find);
        close(fd[find]);
    }
    close(fd[1-find]);
    execvp(cmdv[0], cmdv);
    //execlp("sh", "sh", "-c", cmd, NULL);
    _exit(127);
#endif
}

static int jsi_pclose(FILE *fp, int cpid)
{
#ifdef __WIN32
    return -1;
#else
    pid_t pid;
    int pstat;
    fclose(fp);
    do {
        pid = waitpid(cpid, &pstat, 0);
    } while (pid == -1 && errno == EINTR);
    return (pid == -1 ? -1 : pstat);
#endif
}


typedef struct {
    Jsi_Value* inputStr;
    Jsi_Value* chdir;
    bool bg;
    bool noError;
    bool trim;
    bool retCode;
    bool retAll;
    bool retval;
    bool noRedir;
    bool noShell;
} ExecOpts;

static Jsi_OptionSpec ExecOptions[] = {
    JSI_OPT(BOOL,   ExecOpts, bg,       .help="Run command in background using system() and return OS code" ),
    JSI_OPT(STRING, ExecOpts, chdir,    .help="Change to directory" ),
    JSI_OPT(STRING, ExecOpts, inputStr, .help="Use string as input and return OS code" ),
    JSI_OPT(BOOL,   ExecOpts, noError,  .help="Suppress all OS errors" ),
    JSI_OPT(BOOL,   ExecOpts, noRedir,  .help="Disable redirect and shell escapes in command" ),
    JSI_OPT(BOOL,   ExecOpts, noShell,  .help="Do not use native popen which invokes via /bin/sh" ),
    JSI_OPT(BOOL,   ExecOpts, trim,     .help="Trim trailing whitespace from output" ),
    JSI_OPT(BOOL,   ExecOpts, retAll,   .help="Return the OS return code and data as an object" ),
    JSI_OPT(BOOL,   ExecOpts, retCode,  .help="Return only the OS return code" ),
    JSI_OPT_END(ExecOpts, .help="Exec options")
};

#define FN_exec JSI_INFO("\
................................................................................
By default, returns the string output, unless the 'bg', 'inputStr', 'retCode' or 'retAll' options are used")
Jsi_RC jsi_SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr, bool restricted)
{
    int n, exitCode = 0, hasopts = 0, sLen, sLen2 = 0;
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 0);
    const char *cp2=NULL, *cp = Jsi_ValueString(interp, arg, &sLen);
    FILE *fp = NULL;
    if (restricted && Jsi_ValueGetLength(interp, args)>1)
        return Jsi_LogError("restricted may not have args");
    if (interp->isSafe) {
        int rc =0, no = 1;
        if (restricted)
            no = 0;
        else if (interp->safeExecPattern && cp) {
................................................................................
            restricted = 1;
        }
        if (no)
            return Jsi_LogError("no exec in safe mode");
    }
    Jsi_RC rc = JSI_OK;
    Jsi_Value *opt = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_DString dStr = {}, cStr = {};

    ExecOpts edata = {};
    edata.retval = 1;
    
    if (opt != NULL) {
        if (opt->vt == JSI_VT_OBJECT && opt->d.obj->ot == JSI_OT_OBJECT) {
            hasopts = 1;
            if (Jsi_OptionsProcess(interp, ExecOptions, &edata, opt, 0) < 0) {
................................................................................
        }
    }
    if (edata.bg || (isbg=((sLen>1 && cp[sLen-1] == '&')))) {
        if (edata.inputStr) {
            rc = Jsi_LogError("input string may not used with bg");
            goto done;
        }
        if (edata.noShell) {
            rc = Jsi_LogError("noShell may not used with bg");
            goto done;
        }
        if (!isbg) {
            Jsi_DSAppend(&dStr, cp, " &", NULL);
            cp = Jsi_DSValue(&dStr);
        }
        edata.bg = 1;
        edata.retCode = 1;
        edata.retval = 0;
        exitCode = ((ec=system(cp))>>8);
        Jsi_DSSetLength(&dStr, 0);
    } else {
        const char *type = (edata.inputStr?"w":"r");
        bool native = !edata.noShell;
#ifdef __WIN32
        native = 1;
#else
        if (native)
            native = (access("/bin/sh", X_OK)==0);
#endif
        int cpid;
        if (native)
            fp = popen(cp, type);
        else {
            int argc;
            char **argv;
            Jsi_DString pStr = {};
            Jsi_SplitStr(cp, &argc, &argv, NULL, &pStr);
            fp = jsi_popen(argv, type, &cpid);
            Jsi_DSFree(&pStr);
        }
        if (!fp) 
            exitCode = errno;
        else {
            if (edata.inputStr) {
                edata.retCode = 1;
                edata.retval = 0;
                cp2 = Jsi_ValueString(interp, edata.inputStr, &sLen2);
                while ((n=fwrite(cp2, 1, sLen2, fp))>0) {
                    sLen2 -= n;
                }
            } else {
                char buf[JSI_BUFSIZ];;
                while ((n=fread(buf, 1, sizeof(buf), fp))>0)
                    Jsi_DSAppendLen(&dStr, buf, n);
            }
            if (native)
                exitCode = ((ec=pclose(fp))>>8);
            else
                exitCode = ((ec=jsi_pclose(fp, cpid))>>8);
        }
    }
    if (exitCode && edata.noError==0 && edata.retCode==0 && edata.retAll==0) {
        if (exitCode==ENOENT)
            Jsi_LogError("command not found: %s", cp);
        else
            Jsi_LogError("program exit code (%x)", exitCode);
................................................................................
}

static Jsi_RC SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    return jsi_SysExecCmd(interp, args, _this, ret, funcPtr, false);
}


static Jsi_RC SysPutsCmd_(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this, Jsi_Value **ret,
    Jsi_Func *funcPtr, bool stdErr, jsi_LogOptions *popts, const char *argStr)
{
    int i, cnt = 0, quote = (popts->file);
    const char *fn = NULL;
    Jsi_DString dStr, oStr;
................................................................................
static Jsi_RC
B64EncodeDStr(const char *ib, int ilen, Jsi_DString *dStr)
{
    int i=0, pos=0;
    char c[74];
    
    while (pos<ilen) {
#define PPDCS(n,s) ((pos+n)>ilen?'=':b64ev[s])
        c[i++]=b64ev[(ib[pos]>>2)&0x3f];
        c[i++]=PPDCS(1,((ib[pos]<<4)&0x30)|((ib[pos+1]>>4)&0x0f));
        c[i++]=PPDCS(2,((ib[pos+1]<<2)&0x3c)|((ib[pos+2]>>6)&0x03));
        c[i++]=PPDCS(3,ib[pos+2]&0x3f);
        if (i>=72) {
            c[i++]='\n';
            c[i]=0;
            Jsi_DSAppendLen(dStr, c, i);
            i=0;
        }
        pos+=3;
................................................................................
        vargs[n++] = Jsi_ValueNewObj(interp, obj);
        vargs[n++] = Jsi_ValueNewObj(interp, obj=Jsi_ObjNew(interp));
    } else {
        arr = v2->d.obj->arr;
        siz = v2->d.obj->arrCnt;
        for (i=0; i<siz; i+=2) {
            anam = Jsi_ValueToString(interp, arr[i], NULL);
            if (i==0 && siz==1 && !Jsi_Strcmp(anam, "-h")) { anum=1; break; }
            if (anam[0] != '-') break;
            if (anam[0] == '-' && anam[1] == '-' && !anam[2]) {acnt++; break;}
            anum += 2;
        }
        if (anum != 1 && (anum>siz)) {
            rc = Jsi_LogError("odd length");
            goto done;
................................................................................
        obj = Jsi_ObjNewArray(interp, arr+anum+acnt, siz-anum-acnt, 0);
        vargs[n++] = Jsi_ValueNewObj(interp, obj);
        vargs[n++] = Jsi_ValueNewObj(interp, obj=Jsi_ObjNew(interp));
        bool isLong = 0;
        for (i=0; i<anum; i+=2) {
            int anLen;
            const char *astr, *anam = Jsi_ValueToString(interp, arr[i], &anLen);
            if (anum<=1 && !Jsi_Strcmp(anam,"-h") ) anam = "help";
            else if (anam && anam[0] == '-') anam++;
            else {
                rc = Jsi_LogError("bad option: %d", i);
                goto done;
            }
                
            Jsi_Value *aval;
................................................................................

static const char *jsi_FindHelpStr(const char *fstr, const char *key, Jsi_DString *dPtr) {
    if (!fstr) return "";
    Jsi_DSSetLength(dPtr, 0);
    const char *cp, *ce;
    int len = Jsi_Strlen(key);
    while (fstr) {
        while (isspace(*fstr)) fstr++;
        cp = fstr;
        if (!Jsi_Strncmp(cp, key, len) && isspace(cp[-1]) && (cp[len]==':' || isspace(cp[len]))) {
            ce = NULL;
            cp = Jsi_Strstr(cp, "// ");
            if (cp)
                ce = Jsi_Strchr(cp, '\n');
            if (cp && ce)
                return Jsi_DSAppendLen(dPtr, cp, ce-cp);
            fstr = ce;
        } else {
            fstr = Jsi_Strchr(cp, '\n');
            if (fstr == cp) break;
        }
    }
    return "";
}

static bool jsi_ModLogEnabled(Jsi_Interp *interp, Jsi_Value *v1, const char *name) {
    jsi_Frame *fptr = interp->framePtr;
    Jsi_Value *v2 = Jsi_ValueObjLookup(interp, v1, name, 0);
................................................................................
            if (!val || !key) continue;
            if (Jsi_ValueIsUndef(interp, val)) {
                rc = Jsi_LogError("value undefined for arg: '%s'", key);
                break;
            }

            if (cnt == 1 && !Jsi_Strcmp(key, "help") && v3->d.obj->tree->numEntries==1) {
                int isLong = 1;//Jsi_ValueIsTrue(interp, val);
                const char *help = "", *es = NULL, *fstr = NULL, *fname = interp->framePtr->ip->fname;
                Jsi_TreeSearchDone(&search);
                if (fname) {
                    jsi_FileInfo  *fi = (typeof(fi))Jsi_HashGet(interp->fileTbl, fname, 0);
                    fstr = fi->str;
                }
                if (!fstr)
................................................................................
                        Jsi_DSPrintf(&dStr, " -%s", key);
                    else
                        Jsi_DSPrintf(&dStr, "\t-%s%s\t%s\t%s%s\n", key, (klen<7?"\t":""), vstr, (vlen<7?"\t":""), help);
                }
                if (isLong)
                    Jsi_DSAppend(&dStr, "\nAccepted by all .jsi modules: -Debug, -Trace, -Test.", NULL);
                else
                    Jsi_DSAppend(&dStr, "\nUse -help for long help.", NULL);
                rc = JSI_BREAK;
                Jsi_ValueFromDS(interp, &dStr, ret);
                Jsi_DSFree(&hStr);
                Jsi_DSFree(&vStr);
                break;
            }
            Jsi_vtype oTyp, vTyp = jsi_getValType(val);
................................................................................
#ifndef JSI_OMIT_ENCRYPT
    { "decrypt",    SysDecryptCmd,   2,  2, "val:string, key:string", .help="Decrypt data using BTEA encryption", .retType=(uint)JSI_TT_STRING, .flags=0, .info=FN_encrypt },
    { "encrypt",    SysEncryptCmd,   2,  2, "val:string, key:string", .help="Encrypt data using BTEA encryption", .retType=(uint)JSI_TT_STRING, .flags=0, .info=FN_encrypt },
#endif
    { "fromCharCode",SysFromCharCodeCmd, 1, 1, "code:number", .help="Return char with given character code", .retType=(uint)JSI_TT_STRING, .flags=JSI_CMDSPEC_NONTHIS},
    { "getenv",     SysGetEnvCmd,    0,  1, "name:string=void", .help="Get one or all environment", .retType=(uint)JSI_TT_STRING|JSI_TT_OBJECT|JSI_TT_VOID  },
    { "getpid",     SysGetPidCmd,    0,  1, "parent:boolean=false", .help="Get process/parent id", .retType=(uint)JSI_TT_NUMBER },

    { "hash",       SysHashCmd,      1,  2, "val:string, options|object=void", .help="Return hash (default SHA256) of string/file", .retType=(uint)JSI_TT_STRING, .flags=0, .info=0, .opts=HashOptions},
    { "setenv",     SysSetEnvCmd,    1,  2, "name:string, value:string=void", .help="Set/get an environment var"  },
    { "times",      SysTimesCmd,     1,  2, "callback:function, count:number=1", .help="Call function count times and return execution time in microseconds", .retType=(uint)JSI_TT_NUMBER },
    { "verConvert", SysVerConvertCmd,1,  2, "ver:string|number, zeroTrim:number=0", .help="Convert a version to/from a string/number, or return null if not a version. For string output zeroTrim says how many trailing .0 to trim (0-2)", .retType=(uint)JSI_TT_NUMBER|JSI_TT_STRING|JSI_TT_NULL },
    { NULL, 0,0,0,0, .help="Utilities commands"  }
};

static Jsi_CmdSpec sysCmds[] = {
    { "assert", jsi_AssertCmd,       1,  3, "expr:boolean|number|function, msg:string=void, options:object=void",  .help="Throw or output msg if expr is false", .retType=(uint)JSI_TT_VOID, .flags=0, .info=FN_assert, .opts=AssertOptions },
................................................................................
    char *spath =  Jsi_ValueString(interp, path,0);
    int rc, force = 0; 

    if (!spath) 
        return Jsi_LogError("expected string");
    if (vf && !Jsi_ValueIsBoolean(interp, vf)) 
        return Jsi_LogError("expected boolean");
    SAFEACCESS(path, 1)
    if (vf)
        force = vf->d.val;
    if (force==0)
        rc = MKDIR_DEFAULT(spath);
    else {
        Jsi_Value *npath = Jsi_ValueNewStringDup(interp, spath);
        rc = mkdir_all(interp, npath);
        Jsi_ValueFree(interp, npath);
    }
    if (rc != 0) 
        return Jsi_LogError("can't create directory \"%s\": %s", spath, strerror(errno));    
    return JSI_OK;
}

static Jsi_RC jsi_SAFEACCESS(Jsi_Interp *interp, Jsi_Value *fname, bool write)
{
    SAFEACCESS(fname, write);
    return JSI_OK;
}

static Jsi_RC FileTempfileCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char *filename;
#ifndef __WIN32
    Jsi_Value *vt = Jsi_ValueArrayIndex(interp, args, 0);
    const char *tp, *templ = "/tmp/jsiXXXXXX";

    if (vt && (tp = Jsi_ValueString(interp, vt, NULL))) {
        templ = tp;
    }
    filename = Jsi_Strdup(templ);
    if (Jsi_InterpSafe(interp)) {
        Jsi_Value *fname = Jsi_ValueNewStringConst(interp, templ, -1);
        Jsi_IncrRefCount(interp, fname);
        Jsi_RC rc = jsi_SAFEACCESS(interp, fname, 1);
        Jsi_DecrRefCount(interp, fname);
        if (rc != JSI_OK)
            return rc;
    }
    int fd = mkstemp(filename);
    if (fd < 0)
        goto fail;
    close(fd);
#else
#ifndef MAX_PATH
#define MAX_PATH 1024
................................................................................
    Jsi_Channel ch = Jsi_Open(interp, fname, "rb+");
    if (!ch)
        return JSI_ERROR;
    rc = (Jsi_Truncate(interp, ch, (unsigned int)siz) == 0 ? JSI_OK : JSI_ERROR);
    Jsi_Close(interp, ch);
    return rc;
}

static Jsi_RC FileMknodCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
#ifdef __WIN32
    return Jsi_LogError("mknod unsupported");
#else
    Jsi_Number m, d;
    Jsi_Value *fname = Jsi_ValueArrayIndex(interp, args, 0);
    const char *nam = Jsi_ValueString(interp, fname, NULL);
    if (Jsi_GetNumberFromValue(interp, Jsi_ValueArrayIndex(interp, args, 1), &m) != JSI_OK
        || Jsi_GetNumberFromValue(interp, Jsi_ValueArrayIndex(interp, args, 2), &d) != JSI_OK)
        return Jsi_LogError("expected numbers for arg 2 & 3");
    SAFEACCESS(fname, 1)
    mode_t mode = (mode_t)m;
    dev_t dev = (dev_t)m;
    if (mknod(nam, mode, dev))
        return Jsi_LogError("mknod failed: %s", strerror(errno));
    return JSI_OK;
#endif
}

static Jsi_RC FileChmodCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Value *fname = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_Value *modv = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_Number fmod;
................................................................................
    { "isdir",      FileIsdirCmd,       1,  1, "file:string",  .help="Return true if file is a directory", .retType=(uint)JSI_TT_BOOLEAN },
    { "isfile",     FileIsfileCmd,      1,  1, "file:string",  .help="Return true if file is a normal file", .retType=(uint)JSI_TT_BOOLEAN },
    { "isrelative", FileIsRelativeCmd,  1,  1, "file:string",  .help="Return true if file path is relative", .retType=(uint)JSI_TT_BOOLEAN },
    { "glob",       FileGlobCmd,        0,  2, "pattern:regexp|string|null='*', options:function|object|null=void", .help="Return list of files in dir with optional pattern match", .retType=(uint)JSI_TT_ARRAY, .flags=0, .info=FN_glob, .opts=GlobOptions },
    { "link",       FileLinkCmd,        2,  3, "src:string, dest:string, ishard:boolean=false",  .help="Link a file", .retType=0, .flags=0, .info=FN_link },
    { "lstat",      FileLstatCmd,       1,  1, "file:string",  .help="Return status info for file", .retType=(uint)JSI_TT_OBJECT },
    { "mkdir",      FileMkdirCmd,       1,  1, "file:string",  .help="Create a directory" },
    { "mknod",      FileMknodCmd,       3,  3, "file:string, mode:number, dev:number", .help="Create unix device file using mknod"  },
    { "mtime",      FileMtimeCmd,       1,  1, "file:string",  .help="Return file modified time", .retType=(uint)JSI_TT_NUMBER },
    { "owned",      FileOwnedCmd,       1,  1, "file:string",  .help="Return true if file is owned by user", .retType=(uint)JSI_TT_BOOLEAN },
    { "pwd",        FilePwdCmd,         0,  0, "",  .help="Return current directory", .retType=(uint)JSI_TT_STRING },
    { "remove",     FileRemoveCmd,      1,  2, "file:string, force:boolean=false",  .help="Delete a file or direcotry" },
    { "rename",     FileRenameCmd,      2,  3, "src:string, dest:string, force:boolean=false",  .help="Rename a file, with possible overwrite" },
    { "read",       FileReadCmd,        1,  2, "file:string, mode:string='rb'",  .help="Read a file", .retType=(uint)JSI_TT_STRING },
    { "readable",   FileReadableCmd,    1,  1, "file:string",  .help="Return true if file is readable", .retType=(uint)JSI_TT_BOOLEAN },
................................................................................
                Jsi_DecrRefCount(interp, obj->arr[i]);
        Jsi_Free(obj->arr);
        obj->arr = NULL;
    }
    obj->tree = NULL;
    if (obj->clearProto)
        Jsi_DecrRefCount(interp, obj->__proto__);
#ifdef JSI_MEM_DEBUG
    memset(obj, 0, (sizeof(*obj)-sizeof(obj->VD)));
#endif
    Jsi_Free(obj);
}


/**************************** ARRAY ******************************/

Jsi_Value *jsi_ObjArrayLookup(Jsi_Interp *interp, Jsi_Obj *obj, const char *key) {
................................................................................
    return ++obj->refcnt;
}

int Jsi_ObjDecrRefCount(Jsi_Interp *interp, Jsi_Obj *obj)  {
    SIGASSERT(obj,OBJ);
    if (obj->refcnt<=0) {
#ifdef JSI_MEM_DEBUG
        fprintf(stderr, "Obj decr with ref %d: VD.Idx=%d\n", obj->refcnt, obj->VD.Idx);
#endif
        return -2;
    }
    jsi_DebugObj(obj,"Decr", jsi_DebugValueCallIdx(), interp);
    int nref;
    if ((nref = --obj->refcnt) <= 0) {
        obj->refcnt = -1;
................................................................................
    if (!n && sb.st_size>0) {
        char fdir[PATH_MAX];
        const char* cr = cmdPtr->curRoot, *fpath;
        if (!Jsi_FSNative(interp, name) || ((fpath= Jsi_Realpath(interp, name, fdir))
            && cr && !Jsi_Strncmp(fpath, cr, Jsi_Strlen(cr))))
            rc = Jsi_FileRead(interp, name, dStr);
        else
            fprintf(stderr, "Skip read file %s in %s\n", Jsi_ValueString(interp, name, NULL), (cr?cr:""));
    }
    if (cmdPtr->noWarn)
        return JSI_OK;
    return rc;
}

static Jsi_RC jsi_wsTemplateFill(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, Jsi_Value *fn, Jsi_DString *dStr,
................................................................................
    } else {
        // Need to read data for non-native files.
        Jsi_DString dStr = {}, fStr = {};
        if (isMdi)
            rc = jsi_wsTemplateFill(interp, cmdPtr, fname, &fStr, (essi?1:0));
        else
            rc = jsi_wsFileRead(interp, fname, &fStr, cmdPtr);
        if (rc != JSI_OK) {
            Jsi_DSFree(&fStr);
            goto nofile;
        }
        int hrc = jsi_wsServeHeader(pss, wsi, (int)Jsi_DSLength(&fStr), 200, Jsi_DSValue(&hStr), mime, &dStr);
        if (hrc>=0) {
            Jsi_DSAppendLen(&dStr, Jsi_DSValue(&fStr), Jsi_DSLength(&fStr));
            char *strVal = Jsi_DSValue(&dStr);
            int strLen = Jsi_DSLength(&dStr);
            hrc = jsi_wswrite(pss, wsi, (unsigned char*)strVal, strLen, LWS_WRITE_HTTP);
        }
................................................................................
    cmdPtr->includeFile = "include.shtml";
    if ((arg != NULL && !Jsi_ValueIsNull(interp,arg))
        && Jsi_OptionsProcess(interp, WSOptions, cmdPtr, arg, 0) < 0) {
bail:
        jsi_wswebsocketObjFree(interp, cmdPtr);
        return JSI_ERROR;
    }
    Jsi_PathNormalize(interp, &cmdPtr->rootdir);

    if (cmdPtr->headers && (Jsi_ValueGetLength(interp, cmdPtr->headers)%2)) {
        Jsi_LogError("Odd header length");
        goto bail;
    }
    const char *up = cmdPtr->urlPrefix, *ur = cmdPtr->urlRedirect;
    if (up && ur && Jsi_Strncmp(ur, up, Jsi_Strlen(up))) {
        Jsi_LogError("urlRedirect does not start with urlPrefix");
................................................................................

    if (cmdPtr->client) {
        struct lws_client_connect_info lci = {};
        lci.context = cmdPtr->context;
        lci.address = cmdPtr->address ? Jsi_ValueString(cmdPtr->interp, cmdPtr->address, NULL) : "127.0.0.1";
        lci.port = cmdPtr->port;
        lci.ssl_connection = cmdPtr->use_ssl;
        lci.path = Jsi_ValueString(cmdPtr->interp, cmdPtr->rootdir, NULL);
        lci.host = cmdPtr->clientHost?cmdPtr->clientHost:lws_canonical_hostname( cmdPtr->context );
        lci.origin = cmdPtr->clientOrigin?cmdPtr->clientOrigin:"origin";
        lci.protocol = cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].name;
        lci.ietf_version_or_minus_one = cmdPtr->ietf_version;
#if (LWS_LIBRARY_VERSION_MAJOR>1)
        if (cmdPtr->post)
            lci.method = "POST";
................................................................................
                    }
                } else {
                    if (flags&JSI_EVAL_ARGV0) {
                        interp->argv0 = Jsi_ValueNewStringDup(interp, fname);
                        Jsi_IncrRefCount(interp, interp->argv0);
                    }
                }
                bool osafe = interp->isSafe;
                if (interp->startSafe  && flags&JSI_EVAL_ARGV0) {
                    if (interp->safeReadDirs || interp->safeMode == jsi_safe_None)
                        interp->isSafe = 0;
                    else {
                        char vds[PATH_MAX], *cp;
                        const char *vda[2] = {vds};
                        Jsi_Strncpy(vds, Jsi_ValueString(interp, npath, NULL), sizeof(vds)-1);
                        vds[sizeof(vds)-1] = 0;
                        cp = Jsi_Strrchr(vds, '/');
                        if (cp)
                            cp[1] = 0;
                        Jsi_DString pStr = {};
                        vda[1] = Jsi_GetCwd(interp, &pStr);
                        interp->safeReadDirs = Jsi_ValueNewArray(interp, vda, 2);
                        Jsi_IncrRefCount(interp, interp->safeReadDirs);
                        if (interp->safeMode == jsi_safe_Write2) {
                            interp->safeWriteDirs = interp->safeReadDirs;
                            Jsi_IncrRefCount(interp, interp->safeWriteDirs);
                        } else if (interp->safeMode == jsi_safe_Write) {
                            interp->safeWriteDirs = Jsi_ValueNewArray(interp, vda+1, 1);
                            Jsi_IncrRefCount(interp, interp->safeWriteDirs);
                        }
                        Jsi_DSFree(&pStr);
                    }
                }
                tinput = input = Jsi_Open(interp, npath, (exists?"-r":"r"));
                interp->isSafe = osafe;
                if (!input) {
                    if (exists)
                        rc = JSI_OK;
                    //Jsi_LogError("Can not open '%s'", fname);
                    goto bail;
                }
            }
................................................................................
#undef _jsi_STACK
#undef _jsi_STACKIDX
#undef _jsi_THISIDX
#undef _jsi_TOP
#undef _jsi_TOQ

#endif

/* JSI main program */
#ifndef JSI_AMALGAMATION
#include "jsi.h"
#endif
#include <string.h>
#include <stdlib.h>

#ifndef JSI_LITE_ONLY
int jsi_main(int argc, char **argv)
{
     // A replacement for shebang "#!/usr/bin/env".
    Jsi_DString sStr = {};
    FILE *fp = NULL;
    if (argc >= 3 && Jsi_Strchr(argv[1], ' ') && Jsi_Strstr(argv[1], "%s")) {
        Jsi_DString tStr = {};
        int i;
................................................................................
    Jsi_InterpOpts opts = {.argc=argc, .argv=argv};
    Jsi_Interp *interp = Jsi_Main(&opts);
    if (!interp) return opts.exitCode;
    Jsi_InterpDelete(interp);
    Jsi_DSFree(&sStr);
    exit(0);
}

#if JSI__MAIN
int main(int argc, char **argv) { return jsi_main(argc, argv); }
#endif
#endif
#endif //JSI_IN_AMALGAMATION

Changes to src/jsi.h.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
...
583
584
585
586
587
588
589

590
591
592
593
594
595
596
/* jsi.h : External API header file for Jsi. */
#ifndef __JSI_H__
#define __JSI_H__

#define JSI_VERSION_MAJOR   2
#define JSI_VERSION_MINOR   7
#define JSI_VERSION_RELEASE 1

#define JSI_VERSION (JSI_VERSION_MAJOR + ((Jsi_Number)JSI_VERSION_MINOR/100.0) + ((Jsi_Number)JSI_VERSION_RELEASE/10000.0))

#ifndef JSI_EXTERN
#define JSI_EXTERN extern
#endif

................................................................................

JSI_EXTERN Jsi_Value* Jsi_ValueNewNull(Jsi_Interp *interp); /*STUB = 100*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewBoolean(Jsi_Interp *interp, int bval); /*STUB = 101*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewNumber(Jsi_Interp *interp, Jsi_Number n); /*STUB = 102*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewBlob(Jsi_Interp *interp, uchar *s, uint len); /*STUB = 103*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewString(Jsi_Interp *interp, const char *s, int len); /*STUB = 104*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringKey(Jsi_Interp *interp, const char *s); /*STUB = 105*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringConst(Jsi_Interp *interp, const char *s, int len); /*STUB = 409*/ /*LAST*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringDup(Jsi_Interp *interp, const char *s); /*STUB = 106*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewArray(Jsi_Interp *interp, const char **items, int count); /*STUB = 107*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewObj(Jsi_Interp *interp, Jsi_Obj *o) ; /*STUB = 108*/
#define Jsi_ValueNewBlobString(interp, s) Jsi_ValueNewBlob(interp, (uchar*)s, Jsi_Strlen(s))

JSI_EXTERN Jsi_RC Jsi_GetStringFromValue(Jsi_Interp* interp, Jsi_Value *value, const char **s); /*STUB = 109*/
JSI_EXTERN Jsi_RC Jsi_GetNumberFromValue(Jsi_Interp* interp, Jsi_Value *value, Jsi_Number *n); /*STUB = 110*/
................................................................................
JSI_EXTERN Jsi_RC Jsi_RegExpMatch( Jsi_Interp *interp,  Jsi_Value *pattern, const char *str, int *rc, Jsi_DString *dStr); /*STUB = 189*/
JSI_EXTERN Jsi_RC Jsi_RegExpMatches(Jsi_Interp *interp, Jsi_Value *pattern, const char *str, Jsi_Value *ret); /*STUB = 190*/
JSI_EXTERN bool Jsi_GlobMatch(const char *pattern, const char *string, int nocase); /*STUB = 191*/
JSI_EXTERN char* Jsi_FileRealpath(Jsi_Interp *interp, Jsi_Value *path, char *newpath); /*STUB = 192*/
JSI_EXTERN char* Jsi_FileRealpathStr(Jsi_Interp *interp, const char *path, char *newpath); /*STUB = 193*/
JSI_EXTERN char* Jsi_NormalPath(Jsi_Interp *interp, const char *path, Jsi_DString *dStr); /*STUB = 194*/
JSI_EXTERN char* Jsi_ValueNormalPath(Jsi_Interp *interp, Jsi_Value *path, Jsi_DString *dStr); /*STUB = 195*/

JSI_EXTERN Jsi_RC Jsi_JSONParse(Jsi_Interp *interp, const char *js, Jsi_Value **ret, int flags); /*STUB = 196*/
JSI_EXTERN Jsi_RC Jsi_JSONParseFmt(Jsi_Interp *interp, Jsi_Value **ret, const char *fmt, ...) /*STUB = 197*/ __attribute__((format (printf,3,4)));
JSI_EXTERN char* Jsi_JSONQuote(Jsi_Interp *interp, const char *str, int len, Jsi_DString *dStr); /*STUB = 198*/
JSI_EXTERN Jsi_RC Jsi_EvalString(Jsi_Interp* interp, const char *str, int flags); /*STUB = 199*/
JSI_EXTERN Jsi_RC Jsi_EvalFile(Jsi_Interp* interp, Jsi_Value *fname, int flags); /*STUB = 200*/
JSI_EXTERN Jsi_RC Jsi_EvalCmdJSON(Jsi_Interp *interp, const char *cmd, const char *jsonArgs, Jsi_DString *dStr, int flags); /*STUB = 201*/
JSI_EXTERN Jsi_RC Jsi_EvalZip(Jsi_Interp *interp, const char *exeFile, const char *mntDir, int *jsFound); /*STUB = 202*/






|







 







|







 







>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
...
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
/* jsi.h : External API header file for Jsi. */
#ifndef __JSI_H__
#define __JSI_H__

#define JSI_VERSION_MAJOR   2
#define JSI_VERSION_MINOR   7
#define JSI_VERSION_RELEASE 2

#define JSI_VERSION (JSI_VERSION_MAJOR + ((Jsi_Number)JSI_VERSION_MINOR/100.0) + ((Jsi_Number)JSI_VERSION_RELEASE/10000.0))

#ifndef JSI_EXTERN
#define JSI_EXTERN extern
#endif

................................................................................

JSI_EXTERN Jsi_Value* Jsi_ValueNewNull(Jsi_Interp *interp); /*STUB = 100*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewBoolean(Jsi_Interp *interp, int bval); /*STUB = 101*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewNumber(Jsi_Interp *interp, Jsi_Number n); /*STUB = 102*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewBlob(Jsi_Interp *interp, uchar *s, uint len); /*STUB = 103*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewString(Jsi_Interp *interp, const char *s, int len); /*STUB = 104*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringKey(Jsi_Interp *interp, const char *s); /*STUB = 105*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringConst(Jsi_Interp *interp, const char *s, int len); /*STUB = 409*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewStringDup(Jsi_Interp *interp, const char *s); /*STUB = 106*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewArray(Jsi_Interp *interp, const char **items, int count); /*STUB = 107*/
JSI_EXTERN Jsi_Value* Jsi_ValueNewObj(Jsi_Interp *interp, Jsi_Obj *o) ; /*STUB = 108*/
#define Jsi_ValueNewBlobString(interp, s) Jsi_ValueNewBlob(interp, (uchar*)s, Jsi_Strlen(s))

JSI_EXTERN Jsi_RC Jsi_GetStringFromValue(Jsi_Interp* interp, Jsi_Value *value, const char **s); /*STUB = 109*/
JSI_EXTERN Jsi_RC Jsi_GetNumberFromValue(Jsi_Interp* interp, Jsi_Value *value, Jsi_Number *n); /*STUB = 110*/
................................................................................
JSI_EXTERN Jsi_RC Jsi_RegExpMatch( Jsi_Interp *interp,  Jsi_Value *pattern, const char *str, int *rc, Jsi_DString *dStr); /*STUB = 189*/
JSI_EXTERN Jsi_RC Jsi_RegExpMatches(Jsi_Interp *interp, Jsi_Value *pattern, const char *str, Jsi_Value *ret); /*STUB = 190*/
JSI_EXTERN bool Jsi_GlobMatch(const char *pattern, const char *string, int nocase); /*STUB = 191*/
JSI_EXTERN char* Jsi_FileRealpath(Jsi_Interp *interp, Jsi_Value *path, char *newpath); /*STUB = 192*/
JSI_EXTERN char* Jsi_FileRealpathStr(Jsi_Interp *interp, const char *path, char *newpath); /*STUB = 193*/
JSI_EXTERN char* Jsi_NormalPath(Jsi_Interp *interp, const char *path, Jsi_DString *dStr); /*STUB = 194*/
JSI_EXTERN char* Jsi_ValueNormalPath(Jsi_Interp *interp, Jsi_Value *path, Jsi_DString *dStr); /*STUB = 195*/
JSI_EXTERN Jsi_RC Jsi_PathNormalize(Jsi_Interp *interp, Jsi_Value **pathPtr); /*STUB = 410*/ /*LAST*/
JSI_EXTERN Jsi_RC Jsi_JSONParse(Jsi_Interp *interp, const char *js, Jsi_Value **ret, int flags); /*STUB = 196*/
JSI_EXTERN Jsi_RC Jsi_JSONParseFmt(Jsi_Interp *interp, Jsi_Value **ret, const char *fmt, ...) /*STUB = 197*/ __attribute__((format (printf,3,4)));
JSI_EXTERN char* Jsi_JSONQuote(Jsi_Interp *interp, const char *str, int len, Jsi_DString *dStr); /*STUB = 198*/
JSI_EXTERN Jsi_RC Jsi_EvalString(Jsi_Interp* interp, const char *str, int flags); /*STUB = 199*/
JSI_EXTERN Jsi_RC Jsi_EvalFile(Jsi_Interp* interp, Jsi_Value *fname, int flags); /*STUB = 200*/
JSI_EXTERN Jsi_RC Jsi_EvalCmdJSON(Jsi_Interp *interp, const char *cmd, const char *jsonArgs, Jsi_DString *dStr, int flags); /*STUB = 201*/
JSI_EXTERN Jsi_RC Jsi_EvalZip(Jsi_Interp *interp, const char *exeFile, const char *mntDir, int *jsFound); /*STUB = 202*/

Changes to src/jsiCmds.c.

5
6
7
8
9
10
11



12
13
14
15
16
17
18
....
1166
1167
1168
1169
1170
1171
1172
















































1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183

1184
1185
1186
1187
1188
1189
1190
1191

1192
1193
1194
1195
1196
1197
1198
....
1201
1202
1203
1204
1205
1206
1207

1208
1209
1210
1211
1212
1213
1214
....
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
....
1267
1268
1269
1270
1271
1272
1273




1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297





1298
1299
1300








1301
1302
1303


1304


1305
1306
1307
1308
1309
1310
1311
....
1349
1350
1351
1352
1353
1354
1355

1356
1357
1358
1359
1360
1361
1362
....
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
....
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
....
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
....
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045



4046
4047
4048
4049
4050
4051
4052
....
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
....
4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
....
4296
4297
4298
4299
4300
4301
4302
4303
4304

4305
4306
4307
4308
4309
4310
4311
char *strptime(const char *buf, const char *fmt, struct tm *tm);
#endif
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <signal.h>
#include <limits.h>




static Jsi_RC consoleInputCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char buf[1024];
    char *cp, *p = buf;
    buf[0] = 0;
................................................................................
        pid = (Jsi_Number)getppid();
    else
        pid = (Jsi_Number)getpid();
    Jsi_ValueMakeNumber(interp, ret, pid);
    return JSI_OK;
#endif
}

















































typedef struct {
    Jsi_Value* inputStr;
    Jsi_Value* chdir;
    bool bg;
    bool noError;
    bool trim;
    bool retCode;
    bool retAll;
    bool retval;
    bool noRedir;

} ExecOpts;

static Jsi_OptionSpec ExecOptions[] = {
    JSI_OPT(BOOL,   ExecOpts, bg,       .help="Run command in background using system() and return OS code" ),
    JSI_OPT(STRING, ExecOpts, chdir,    .help="Change to directory" ),
    JSI_OPT(STRING, ExecOpts, inputStr, .help="Use string as input and return OS code" ),
    JSI_OPT(BOOL,   ExecOpts, noError,  .help="Suppress all OS errors" ),
    JSI_OPT(BOOL,   ExecOpts, noRedir,  .help="Disable redirect and shell escapes in command" ),

    JSI_OPT(BOOL,   ExecOpts, trim,     .help="Trim trailing whitespace from output" ),
    JSI_OPT(BOOL,   ExecOpts, retAll,   .help="Return the OS return code and data as an object" ),
    JSI_OPT(BOOL,   ExecOpts, retCode,  .help="Return only the OS return code" ),
    JSI_OPT_END(ExecOpts, .help="Exec options")
};

#define FN_exec JSI_INFO("\
................................................................................
By default, returns the string output, unless the 'bg', 'inputStr', 'retCode' or 'retAll' options are used")
Jsi_RC jsi_SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr, bool restricted)
{
    int n, exitCode = 0, hasopts = 0, sLen, sLen2 = 0;
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 0);
    const char *cp2=NULL, *cp = Jsi_ValueString(interp, arg, &sLen);

    if (restricted && Jsi_ValueGetLength(interp, args)>1)
        return Jsi_LogError("restricted may not have args");
    if (interp->isSafe) {
        int rc =0, no = 1;
        if (restricted)
            no = 0;
        else if (interp->safeExecPattern && cp) {
................................................................................
            restricted = 1;
        }
        if (no)
            return Jsi_LogError("no exec in safe mode");
    }
    Jsi_RC rc = JSI_OK;
    Jsi_Value *opt = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_DString dStr = {};
    Jsi_DString cStr = {};
    ExecOpts edata = {};
    edata.retval = 1;
    
    if (opt != NULL) {
        if (opt->vt == JSI_VT_OBJECT && opt->d.obj->ot == JSI_OT_OBJECT) {
            hasopts = 1;
            if (Jsi_OptionsProcess(interp, ExecOptions, &edata, opt, 0) < 0) {
................................................................................
        }
    }
    if (edata.bg || (isbg=((sLen>1 && cp[sLen-1] == '&')))) {
        if (edata.inputStr) {
            rc = Jsi_LogError("input string may not used with bg");
            goto done;
        }




        if (!isbg) {
            Jsi_DSAppend(&dStr, cp, " &", NULL);
            cp = Jsi_DSValue(&dStr);
        }
        edata.bg = 1;
        edata.retCode = 1;
        edata.retval = 0;
        exitCode = ((ec=system(cp))>>8);
        Jsi_DSSetLength(&dStr, 0);
    } else if (edata.inputStr) {
        edata.retCode = 1;
        edata.retval = 0;
        cp2 = Jsi_ValueString(interp, edata.inputStr, &sLen2);
        FILE *fp = popen(cp, "w");
        if (!fp) 
            exitCode = errno;
        else {
            while ((n=fwrite(cp2, 1, sLen2, fp))>0) {
                sLen2 -= n;
            }
            exitCode = ((ec=pclose(fp))>>8);
        }
    } else {
        FILE *fp = popen(cp, "r");





        if (!fp) 
            exitCode = errno;
        else {








            char buf[JSI_BUFSIZ];;
            while ((n=fread(buf, 1, sizeof(buf), fp))>0)
                Jsi_DSAppendLen(&dStr, buf, n);


            exitCode = ((ec=pclose(fp))>>8);


        }
    }
    if (exitCode && edata.noError==0 && edata.retCode==0 && edata.retAll==0) {
        if (exitCode==ENOENT)
            Jsi_LogError("command not found: %s", cp);
        else
            Jsi_LogError("program exit code (%x)", exitCode);
................................................................................
}

static Jsi_RC SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    return jsi_SysExecCmd(interp, args, _this, ret, funcPtr, false);
}


static Jsi_RC SysPutsCmd_(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this, Jsi_Value **ret,
    Jsi_Func *funcPtr, bool stdErr, jsi_LogOptions *popts, const char *argStr)
{
    int i, cnt = 0, quote = (popts->file);
    const char *fn = NULL;
    Jsi_DString dStr, oStr;
................................................................................
static Jsi_RC
B64EncodeDStr(const char *ib, int ilen, Jsi_DString *dStr)
{
    int i=0, pos=0;
    char c[74];
    
    while (pos<ilen) {
#define P(n,s) ((pos+n)>ilen?'=':b64ev[s])
        c[i++]=b64ev[(ib[pos]>>2)&0x3f];
        c[i++]=P(1,((ib[pos]<<4)&0x30)|((ib[pos+1]>>4)&0x0f));
        c[i++]=P(2,((ib[pos+1]<<2)&0x3c)|((ib[pos+2]>>6)&0x03));
        c[i++]=P(3,ib[pos+2]&0x3f);
        if (i>=72) {
            c[i++]='\n';
            c[i]=0;
            Jsi_DSAppendLen(dStr, c, i);
            i=0;
        }
        pos+=3;
................................................................................
        vargs[n++] = Jsi_ValueNewObj(interp, obj);
        vargs[n++] = Jsi_ValueNewObj(interp, obj=Jsi_ObjNew(interp));
    } else {
        arr = v2->d.obj->arr;
        siz = v2->d.obj->arrCnt;
        for (i=0; i<siz; i+=2) {
            anam = Jsi_ValueToString(interp, arr[i], NULL);
            if (i==0 && siz==1 && (!Jsi_Strcmp(anam, "-h") || !Jsi_Strcmp(anam, "--help"))) { anum=1; break; }
            if (anam[0] != '-') break;
            if (anam[0] == '-' && anam[1] == '-' && !anam[2]) {acnt++; break;}
            anum += 2;
        }
        if (anum != 1 && (anum>siz)) {
            rc = Jsi_LogError("odd length");
            goto done;
................................................................................
        obj = Jsi_ObjNewArray(interp, arr+anum+acnt, siz-anum-acnt, 0);
        vargs[n++] = Jsi_ValueNewObj(interp, obj);
        vargs[n++] = Jsi_ValueNewObj(interp, obj=Jsi_ObjNew(interp));
        bool isLong = 0;
        for (i=0; i<anum; i+=2) {
            int anLen;
            const char *astr, *anam = Jsi_ValueToString(interp, arr[i], &anLen);
            if (anum<=1 && (!Jsi_Strcmp(anam,"-h") || (isLong=!Jsi_Strcmp(anam,"--help")))) anam = "help";
            else if (anam && anam[0] == '-') anam++;
            else {
                rc = Jsi_LogError("bad option: %d", i);
                goto done;
            }
                
            Jsi_Value *aval;
................................................................................

static const char *jsi_FindHelpStr(const char *fstr, const char *key, Jsi_DString *dPtr) {
    if (!fstr) return "";
    Jsi_DSSetLength(dPtr, 0);
    const char *cp, *ce;
    int len = Jsi_Strlen(key);
    while (fstr) {
        cp = Jsi_Strstr(fstr, key);
        if (!cp) break;
        if (isspace(cp[-1]) && (cp[len]==':' || isspace(cp[len]))) {
            ce = NULL;
            cp = Jsi_Strstr(cp, "// ");
            if (cp)
                ce = Jsi_Strchr(cp, '\n');
            if (cp && ce)
                return Jsi_DSAppendLen(dPtr, cp, ce-cp);
            fstr = ce;
        } else fstr = cp+len;



    }
    return "";
}

static bool jsi_ModLogEnabled(Jsi_Interp *interp, Jsi_Value *v1, const char *name) {
    jsi_Frame *fptr = interp->framePtr;
    Jsi_Value *v2 = Jsi_ValueObjLookup(interp, v1, name, 0);
................................................................................
            if (!val || !key) continue;
            if (Jsi_ValueIsUndef(interp, val)) {
                rc = Jsi_LogError("value undefined for arg: '%s'", key);
                break;
            }

            if (cnt == 1 && !Jsi_Strcmp(key, "help") && v3->d.obj->tree->numEntries==1) {
                int isLong = Jsi_ValueIsTrue(interp, val);
                const char *help = "", *es = NULL, *fstr = NULL, *fname = interp->framePtr->ip->fname;
                Jsi_TreeSearchDone(&search);
                if (fname) {
                    jsi_FileInfo  *fi = (typeof(fi))Jsi_HashGet(interp->fileTbl, fname, 0);
                    fstr = fi->str;
                }
                if (!fstr)
................................................................................
                        Jsi_DSPrintf(&dStr, " -%s", key);
                    else
                        Jsi_DSPrintf(&dStr, "\t-%s%s\t%s\t%s%s\n", key, (klen<7?"\t":""), vstr, (vlen<7?"\t":""), help);
                }
                if (isLong)
                    Jsi_DSAppend(&dStr, "\nAccepted by all .jsi modules: -Debug, -Trace, -Test.", NULL);
                else
                    Jsi_DSAppend(&dStr, "\nUse --help for long help.", NULL);
                rc = JSI_BREAK;
                Jsi_ValueFromDS(interp, &dStr, ret);
                Jsi_DSFree(&hStr);
                Jsi_DSFree(&vStr);
                break;
            }
            Jsi_vtype oTyp, vTyp = jsi_getValType(val);
................................................................................
#ifndef JSI_OMIT_ENCRYPT
    { "decrypt",    SysDecryptCmd,   2,  2, "val:string, key:string", .help="Decrypt data using BTEA encryption", .retType=(uint)JSI_TT_STRING, .flags=0, .info=FN_encrypt },
    { "encrypt",    SysEncryptCmd,   2,  2, "val:string, key:string", .help="Encrypt data using BTEA encryption", .retType=(uint)JSI_TT_STRING, .flags=0, .info=FN_encrypt },
#endif
    { "fromCharCode",SysFromCharCodeCmd, 1, 1, "code:number", .help="Return char with given character code", .retType=(uint)JSI_TT_STRING, .flags=JSI_CMDSPEC_NONTHIS},
    { "getenv",     SysGetEnvCmd,    0,  1, "name:string=void", .help="Get one or all environment", .retType=(uint)JSI_TT_STRING|JSI_TT_OBJECT|JSI_TT_VOID  },
    { "getpid",     SysGetPidCmd,    0,  1, "parent:boolean=false", .help="Get process/parent id", .retType=(uint)JSI_TT_NUMBER },
    { "setenv",     SysSetEnvCmd,    1,  2, "name:string, value:string=void", .help="Set/get an environment var"  },
    { "hash",       SysHashCmd,      1,  2, "val:string, options|object=void", .help="Return hash (default SHA256) of string/file", .retType=(uint)JSI_TT_STRING, .flags=0, .info=0, .opts=HashOptions},

    { "times",      SysTimesCmd,     1,  2, "callback:function, count:number=1", .help="Call function count times and return execution time in microseconds", .retType=(uint)JSI_TT_NUMBER },
    { "verConvert", SysVerConvertCmd,1,  2, "ver:string|number, zeroTrim:number=0", .help="Convert a version to/from a string/number, or return null if not a version. For string output zeroTrim says how many trailing .0 to trim (0-2)", .retType=(uint)JSI_TT_NUMBER|JSI_TT_STRING|JSI_TT_NULL },
    { NULL, 0,0,0,0, .help="Utilities commands"  }
};

static Jsi_CmdSpec sysCmds[] = {
    { "assert", jsi_AssertCmd,       1,  3, "expr:boolean|number|function, msg:string=void, options:object=void",  .help="Throw or output msg if expr is false", .retType=(uint)JSI_TT_VOID, .flags=0, .info=FN_assert, .opts=AssertOptions },







>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>








>







 







>







 







|
<







 







>
>
>
>









|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>



>
>
>
>
>
>
>
>
|
|
|
>
>
|
>
>







 







>







 







|

|
|
|







 







|







 







|







 







|
|
|







|
>
>
>







 







|







 







|







 







<

>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
....
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
....
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
....
1278
1279
1280
1281
1282
1283
1284
1285

1286
1287
1288
1289
1290
1291
1292
....
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
....
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
....
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
....
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
4043
4044
4045
4046
4047
....
4049
4050
4051
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
....
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
....
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
....
4238
4239
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
....
4374
4375
4376
4377
4378
4379
4380

4381
4382
4383
4384
4385
4386
4387
4388
4389
char *strptime(const char *buf, const char *fmt, struct tm *tm);
#endif
#include <errno.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <signal.h>
#include <limits.h>
#ifndef __WIN32
#include <sys/wait.h>
#endif

static Jsi_RC consoleInputCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char buf[1024];
    char *cp, *p = buf;
    buf[0] = 0;
................................................................................
        pid = (Jsi_Number)getppid();
    else
        pid = (Jsi_Number)getpid();
    Jsi_ValueMakeNumber(interp, ret, pid);
    return JSI_OK;
#endif
}

// Replacement for popen when there is no /bin/sh.  It uses execvp.
static FILE *jsi_popen(char **cmdv, const char *type, int *cpid)
{
#ifdef __WIN32
    return NULL;
#else
    int fd[2], pid, find = (*type != 'w' ? 1 : 0);
    if (!cmdv[0] || pipe(fd)<0)
        return NULL;
    pid = fork();
    if (pid<0) {
        close(fd[0]);
        close(fd[1]);
        return NULL;
    }

    if (pid) {
        *cpid = pid;
        close(fd[find]);
        return fdopen(fd[1-find], type);
    }
    if (fd[find] != find) {
        dup2(fd[find], find);
        close(fd[find]);
    }
    close(fd[1-find]);
    execvp(cmdv[0], cmdv);
    //execlp("sh", "sh", "-c", cmd, NULL);
    _exit(127);
#endif
}

static int jsi_pclose(FILE *fp, int cpid)
{
#ifdef __WIN32
    return -1;
#else
    pid_t pid;
    int pstat;
    fclose(fp);
    do {
        pid = waitpid(cpid, &pstat, 0);
    } while (pid == -1 && errno == EINTR);
    return (pid == -1 ? -1 : pstat);
#endif
}


typedef struct {
    Jsi_Value* inputStr;
    Jsi_Value* chdir;
    bool bg;
    bool noError;
    bool trim;
    bool retCode;
    bool retAll;
    bool retval;
    bool noRedir;
    bool noShell;
} ExecOpts;

static Jsi_OptionSpec ExecOptions[] = {
    JSI_OPT(BOOL,   ExecOpts, bg,       .help="Run command in background using system() and return OS code" ),
    JSI_OPT(STRING, ExecOpts, chdir,    .help="Change to directory" ),
    JSI_OPT(STRING, ExecOpts, inputStr, .help="Use string as input and return OS code" ),
    JSI_OPT(BOOL,   ExecOpts, noError,  .help="Suppress all OS errors" ),
    JSI_OPT(BOOL,   ExecOpts, noRedir,  .help="Disable redirect and shell escapes in command" ),
    JSI_OPT(BOOL,   ExecOpts, noShell,  .help="Do not use native popen which invokes via /bin/sh" ),
    JSI_OPT(BOOL,   ExecOpts, trim,     .help="Trim trailing whitespace from output" ),
    JSI_OPT(BOOL,   ExecOpts, retAll,   .help="Return the OS return code and data as an object" ),
    JSI_OPT(BOOL,   ExecOpts, retCode,  .help="Return only the OS return code" ),
    JSI_OPT_END(ExecOpts, .help="Exec options")
};

#define FN_exec JSI_INFO("\
................................................................................
By default, returns the string output, unless the 'bg', 'inputStr', 'retCode' or 'retAll' options are used")
Jsi_RC jsi_SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr, bool restricted)
{
    int n, exitCode = 0, hasopts = 0, sLen, sLen2 = 0;
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 0);
    const char *cp2=NULL, *cp = Jsi_ValueString(interp, arg, &sLen);
    FILE *fp = NULL;
    if (restricted && Jsi_ValueGetLength(interp, args)>1)
        return Jsi_LogError("restricted may not have args");
    if (interp->isSafe) {
        int rc =0, no = 1;
        if (restricted)
            no = 0;
        else if (interp->safeExecPattern && cp) {
................................................................................
            restricted = 1;
        }
        if (no)
            return Jsi_LogError("no exec in safe mode");
    }
    Jsi_RC rc = JSI_OK;
    Jsi_Value *opt = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_DString dStr = {}, cStr = {};

    ExecOpts edata = {};
    edata.retval = 1;
    
    if (opt != NULL) {
        if (opt->vt == JSI_VT_OBJECT && opt->d.obj->ot == JSI_OT_OBJECT) {
            hasopts = 1;
            if (Jsi_OptionsProcess(interp, ExecOptions, &edata, opt, 0) < 0) {
................................................................................
        }
    }
    if (edata.bg || (isbg=((sLen>1 && cp[sLen-1] == '&')))) {
        if (edata.inputStr) {
            rc = Jsi_LogError("input string may not used with bg");
            goto done;
        }
        if (edata.noShell) {
            rc = Jsi_LogError("noShell may not used with bg");
            goto done;
        }
        if (!isbg) {
            Jsi_DSAppend(&dStr, cp, " &", NULL);
            cp = Jsi_DSValue(&dStr);
        }
        edata.bg = 1;
        edata.retCode = 1;
        edata.retval = 0;
        exitCode = ((ec=system(cp))>>8);
        Jsi_DSSetLength(&dStr, 0);
    } else {
        const char *type = (edata.inputStr?"w":"r");
        bool native = !edata.noShell;
#ifdef __WIN32
        native = 1;
#else
        if (native)
            native = (access("/bin/sh", X_OK)==0);
#endif
        int cpid;
        if (native)
            fp = popen(cp, type);
        else {
            int argc;
            char **argv;
            Jsi_DString pStr = {};
            Jsi_SplitStr(cp, &argc, &argv, NULL, &pStr);
            fp = jsi_popen(argv, type, &cpid);
            Jsi_DSFree(&pStr);
        }
        if (!fp) 
            exitCode = errno;
        else {
            if (edata.inputStr) {
                edata.retCode = 1;
                edata.retval = 0;
                cp2 = Jsi_ValueString(interp, edata.inputStr, &sLen2);
                while ((n=fwrite(cp2, 1, sLen2, fp))>0) {
                    sLen2 -= n;
                }
            } else {
                char buf[JSI_BUFSIZ];;
                while ((n=fread(buf, 1, sizeof(buf), fp))>0)
                    Jsi_DSAppendLen(&dStr, buf, n);
            }
            if (native)
                exitCode = ((ec=pclose(fp))>>8);
            else
                exitCode = ((ec=jsi_pclose(fp, cpid))>>8);
        }
    }
    if (exitCode && edata.noError==0 && edata.retCode==0 && edata.retAll==0) {
        if (exitCode==ENOENT)
            Jsi_LogError("command not found: %s", cp);
        else
            Jsi_LogError("program exit code (%x)", exitCode);
................................................................................
}

static Jsi_RC SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    return jsi_SysExecCmd(interp, args, _this, ret, funcPtr, false);
}


static Jsi_RC SysPutsCmd_(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this, Jsi_Value **ret,
    Jsi_Func *funcPtr, bool stdErr, jsi_LogOptions *popts, const char *argStr)
{
    int i, cnt = 0, quote = (popts->file);
    const char *fn = NULL;
    Jsi_DString dStr, oStr;
................................................................................
static Jsi_RC
B64EncodeDStr(const char *ib, int ilen, Jsi_DString *dStr)
{
    int i=0, pos=0;
    char c[74];
    
    while (pos<ilen) {
#define PPDCS(n,s) ((pos+n)>ilen?'=':b64ev[s])
        c[i++]=b64ev[(ib[pos]>>2)&0x3f];
        c[i++]=PPDCS(1,((ib[pos]<<4)&0x30)|((ib[pos+1]>>4)&0x0f));
        c[i++]=PPDCS(2,((ib[pos+1]<<2)&0x3c)|((ib[pos+2]>>6)&0x03));
        c[i++]=PPDCS(3,ib[pos+2]&0x3f);
        if (i>=72) {
            c[i++]='\n';
            c[i]=0;
            Jsi_DSAppendLen(dStr, c, i);
            i=0;
        }
        pos+=3;
................................................................................
        vargs[n++] = Jsi_ValueNewObj(interp, obj);
        vargs[n++] = Jsi_ValueNewObj(interp, obj=Jsi_ObjNew(interp));
    } else {
        arr = v2->d.obj->arr;
        siz = v2->d.obj->arrCnt;
        for (i=0; i<siz; i+=2) {
            anam = Jsi_ValueToString(interp, arr[i], NULL);
            if (i==0 && siz==1 && !Jsi_Strcmp(anam, "-h")) { anum=1; break; }
            if (anam[0] != '-') break;
            if (anam[0] == '-' && anam[1] == '-' && !anam[2]) {acnt++; break;}
            anum += 2;
        }
        if (anum != 1 && (anum>siz)) {
            rc = Jsi_LogError("odd length");
            goto done;
................................................................................
        obj = Jsi_ObjNewArray(interp, arr+anum+acnt, siz-anum-acnt, 0);
        vargs[n++] = Jsi_ValueNewObj(interp, obj);
        vargs[n++] = Jsi_ValueNewObj(interp, obj=Jsi_ObjNew(interp));
        bool isLong = 0;
        for (i=0; i<anum; i+=2) {
            int anLen;
            const char *astr, *anam = Jsi_ValueToString(interp, arr[i], &anLen);
            if (anum<=1 && !Jsi_Strcmp(anam,"-h") ) anam = "help";
            else if (anam && anam[0] == '-') anam++;
            else {
                rc = Jsi_LogError("bad option: %d", i);
                goto done;
            }
                
            Jsi_Value *aval;
................................................................................

static const char *jsi_FindHelpStr(const char *fstr, const char *key, Jsi_DString *dPtr) {
    if (!fstr) return "";
    Jsi_DSSetLength(dPtr, 0);
    const char *cp, *ce;
    int len = Jsi_Strlen(key);
    while (fstr) {
        while (isspace(*fstr)) fstr++;
        cp = fstr;
        if (!Jsi_Strncmp(cp, key, len) && isspace(cp[-1]) && (cp[len]==':' || isspace(cp[len]))) {
            ce = NULL;
            cp = Jsi_Strstr(cp, "// ");
            if (cp)
                ce = Jsi_Strchr(cp, '\n');
            if (cp && ce)
                return Jsi_DSAppendLen(dPtr, cp, ce-cp);
            fstr = ce;
        } else {
            fstr = Jsi_Strchr(cp, '\n');
            if (fstr == cp) break;
        }
    }
    return "";
}

static bool jsi_ModLogEnabled(Jsi_Interp *interp, Jsi_Value *v1, const char *name) {
    jsi_Frame *fptr = interp->framePtr;
    Jsi_Value *v2 = Jsi_ValueObjLookup(interp, v1, name, 0);
................................................................................
            if (!val || !key) continue;
            if (Jsi_ValueIsUndef(interp, val)) {
                rc = Jsi_LogError("value undefined for arg: '%s'", key);
                break;
            }

            if (cnt == 1 && !Jsi_Strcmp(key, "help") && v3->d.obj->tree->numEntries==1) {
                int isLong = 1;//Jsi_ValueIsTrue(interp, val);
                const char *help = "", *es = NULL, *fstr = NULL, *fname = interp->framePtr->ip->fname;
                Jsi_TreeSearchDone(&search);
                if (fname) {
                    jsi_FileInfo  *fi = (typeof(fi))Jsi_HashGet(interp->fileTbl, fname, 0);
                    fstr = fi->str;
                }
                if (!fstr)
................................................................................
                        Jsi_DSPrintf(&dStr, " -%s", key);
                    else
                        Jsi_DSPrintf(&dStr, "\t-%s%s\t%s\t%s%s\n", key, (klen<7?"\t":""), vstr, (vlen<7?"\t":""), help);
                }
                if (isLong)
                    Jsi_DSAppend(&dStr, "\nAccepted by all .jsi modules: -Debug, -Trace, -Test.", NULL);
                else
                    Jsi_DSAppend(&dStr, "\nUse -help for long help.", NULL);
                rc = JSI_BREAK;
                Jsi_ValueFromDS(interp, &dStr, ret);
                Jsi_DSFree(&hStr);
                Jsi_DSFree(&vStr);
                break;
            }
            Jsi_vtype oTyp, vTyp = jsi_getValType(val);
................................................................................
#ifndef JSI_OMIT_ENCRYPT
    { "decrypt",    SysDecryptCmd,   2,  2, "val:string, key:string", .help="Decrypt data using BTEA encryption", .retType=(uint)JSI_TT_STRING, .flags=0, .info=FN_encrypt },
    { "encrypt",    SysEncryptCmd,   2,  2, "val:string, key:string", .help="Encrypt data using BTEA encryption", .retType=(uint)JSI_TT_STRING, .flags=0, .info=FN_encrypt },
#endif
    { "fromCharCode",SysFromCharCodeCmd, 1, 1, "code:number", .help="Return char with given character code", .retType=(uint)JSI_TT_STRING, .flags=JSI_CMDSPEC_NONTHIS},
    { "getenv",     SysGetEnvCmd,    0,  1, "name:string=void", .help="Get one or all environment", .retType=(uint)JSI_TT_STRING|JSI_TT_OBJECT|JSI_TT_VOID  },
    { "getpid",     SysGetPidCmd,    0,  1, "parent:boolean=false", .help="Get process/parent id", .retType=(uint)JSI_TT_NUMBER },

    { "hash",       SysHashCmd,      1,  2, "val:string, options|object=void", .help="Return hash (default SHA256) of string/file", .retType=(uint)JSI_TT_STRING, .flags=0, .info=0, .opts=HashOptions},
    { "setenv",     SysSetEnvCmd,    1,  2, "name:string, value:string=void", .help="Set/get an environment var"  },
    { "times",      SysTimesCmd,     1,  2, "callback:function, count:number=1", .help="Call function count times and return execution time in microseconds", .retType=(uint)JSI_TT_NUMBER },
    { "verConvert", SysVerConvertCmd,1,  2, "ver:string|number, zeroTrim:number=0", .help="Convert a version to/from a string/number, or return null if not a version. For string output zeroTrim says how many trailing .0 to trim (0-2)", .retType=(uint)JSI_TT_NUMBER|JSI_TT_STRING|JSI_TT_NULL },
    { NULL, 0,0,0,0, .help="Utilities commands"  }
};

static Jsi_CmdSpec sysCmds[] = {
    { "assert", jsi_AssertCmd,       1,  3, "expr:boolean|number|function, msg:string=void, options:object=void",  .help="Throw or output msg if expr is false", .retType=(uint)JSI_TT_VOID, .flags=0, .info=FN_assert, .opts=AssertOptions },

Changes to src/jsiEval.c.

2214
2215
2216
2217
2218
2219
2220






















2221



2222

2223
2224
2225
2226
2227
2228
2229
                    }
                } else {
                    if (flags&JSI_EVAL_ARGV0) {
                        interp->argv0 = Jsi_ValueNewStringDup(interp, fname);
                        Jsi_IncrRefCount(interp, interp->argv0);
                    }
                }






















                



                tinput = input = Jsi_Open(interp, npath, (exists?"-r":"r"));

                if (!input) {
                    if (exists)
                        rc = JSI_OK;
                    //Jsi_LogError("Can not open '%s'", fname);
                    goto bail;
                }
            }







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>

>







2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
                    }
                } else {
                    if (flags&JSI_EVAL_ARGV0) {
                        interp->argv0 = Jsi_ValueNewStringDup(interp, fname);
                        Jsi_IncrRefCount(interp, interp->argv0);
                    }
                }
                bool osafe = interp->isSafe;
                if (interp->startSafe  && flags&JSI_EVAL_ARGV0) {
                    if (interp->safeReadDirs || interp->safeMode == jsi_safe_None)
                        interp->isSafe = 0;
                    else {
                        char vds[PATH_MAX], *cp;
                        const char *vda[2] = {vds};
                        Jsi_Strncpy(vds, Jsi_ValueString(interp, npath, NULL), sizeof(vds)-1);
                        vds[sizeof(vds)-1] = 0;
                        cp = Jsi_Strrchr(vds, '/');
                        if (cp)
                            cp[1] = 0;
                        Jsi_DString pStr = {};
                        vda[1] = Jsi_GetCwd(interp, &pStr);
                        interp->safeReadDirs = Jsi_ValueNewArray(interp, vda, 2);
                        Jsi_IncrRefCount(interp, interp->safeReadDirs);
                        if (interp->safeMode == jsi_safe_Write2) {
                            interp->safeWriteDirs = interp->safeReadDirs;
                            Jsi_IncrRefCount(interp, interp->safeWriteDirs);
                        } else if (interp->safeMode == jsi_safe_Write) {
                            interp->safeWriteDirs = Jsi_ValueNewArray(interp, vda+1, 1);
                            Jsi_IncrRefCount(interp, interp->safeWriteDirs);
                        }
                        Jsi_DSFree(&pStr);
                    }
                }
                tinput = input = Jsi_Open(interp, npath, (exists?"-r":"r"));
                interp->isSafe = osafe;
                if (!input) {
                    if (exists)
                        rc = JSI_OK;
                    //Jsi_LogError("Can not open '%s'", fname);
                    goto bail;
                }
            }

Changes to src/jsiFileCmds.c.

261
262
263
264
265
266
267

268
269
270
271
272
273
274
275
276
277
278
279
280






281
282
283
284
285
286
287
288
289
290
291
292
293








294
295
296
297
298
299
300
...
329
330
331
332
333
334
335





















336
337
338
339
340
341
342
....
1146
1147
1148
1149
1150
1151
1152

1153
1154
1155
1156
1157
1158
1159
    char *spath =  Jsi_ValueString(interp, path,0);
    int rc, force = 0; 

    if (!spath) 
        return Jsi_LogError("expected string");
    if (vf && !Jsi_ValueIsBoolean(interp, vf)) 
        return Jsi_LogError("expected boolean");

    if (vf)
        force = vf->d.val;
    if (force==0)
        rc = MKDIR_DEFAULT(spath);
    else {
        Jsi_Value *npath = Jsi_ValueNewStringDup(interp, spath);
        rc = mkdir_all(interp, npath);
        Jsi_ValueFree(interp, npath);
    }
    if (rc != 0) 
        return Jsi_LogError("can't create directory \"%s\": %s", spath, strerror(errno));    
    return JSI_OK;
}







static Jsi_RC FileTempfileCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char *filename;
#ifndef __WIN32
    Jsi_Value *vt = Jsi_ValueArrayIndex(interp, args, 0);
    const char *tp, *templ = "/tmp/jsiXXXXXX";

    if (vt && (tp = Jsi_ValueString(interp, vt, NULL))) {
        templ = tp;
    }
    filename = Jsi_Strdup(templ);








    int fd = mkstemp(filename);
    if (fd < 0)
        goto fail;
    close(fd);
#else
#ifndef MAX_PATH
#define MAX_PATH 1024
................................................................................
    Jsi_Channel ch = Jsi_Open(interp, fname, "rb+");
    if (!ch)
        return JSI_ERROR;
    rc = (Jsi_Truncate(interp, ch, (unsigned int)siz) == 0 ? JSI_OK : JSI_ERROR);
    Jsi_Close(interp, ch);
    return rc;
}






















static Jsi_RC FileChmodCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Value *fname = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_Value *modv = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_Number fmod;
................................................................................
    { "isdir",      FileIsdirCmd,       1,  1, "file:string",  .help="Return true if file is a directory", .retType=(uint)JSI_TT_BOOLEAN },
    { "isfile",     FileIsfileCmd,      1,  1, "file:string",  .help="Return true if file is a normal file", .retType=(uint)JSI_TT_BOOLEAN },
    { "isrelative", FileIsRelativeCmd,  1,  1, "file:string",  .help="Return true if file path is relative", .retType=(uint)JSI_TT_BOOLEAN },
    { "glob",       FileGlobCmd,        0,  2, "pattern:regexp|string|null='*', options:function|object|null=void", .help="Return list of files in dir with optional pattern match", .retType=(uint)JSI_TT_ARRAY, .flags=0, .info=FN_glob, .opts=GlobOptions },
    { "link",       FileLinkCmd,        2,  3, "src:string, dest:string, ishard:boolean=false",  .help="Link a file", .retType=0, .flags=0, .info=FN_link },
    { "lstat",      FileLstatCmd,       1,  1, "file:string",  .help="Return status info for file", .retType=(uint)JSI_TT_OBJECT },
    { "mkdir",      FileMkdirCmd,       1,  1, "file:string",  .help="Create a directory" },

    { "mtime",      FileMtimeCmd,       1,  1, "file:string",  .help="Return file modified time", .retType=(uint)JSI_TT_NUMBER },
    { "owned",      FileOwnedCmd,       1,  1, "file:string",  .help="Return true if file is owned by user", .retType=(uint)JSI_TT_BOOLEAN },
    { "pwd",        FilePwdCmd,         0,  0, "",  .help="Return current directory", .retType=(uint)JSI_TT_STRING },
    { "remove",     FileRemoveCmd,      1,  2, "file:string, force:boolean=false",  .help="Delete a file or direcotry" },
    { "rename",     FileRenameCmd,      2,  3, "src:string, dest:string, force:boolean=false",  .help="Rename a file, with possible overwrite" },
    { "read",       FileReadCmd,        1,  2, "file:string, mode:string='rb'",  .help="Read a file", .retType=(uint)JSI_TT_STRING },
    { "readable",   FileReadableCmd,    1,  1, "file:string",  .help="Return true if file is readable", .retType=(uint)JSI_TT_BOOLEAN },







>













>
>
>
>
>
>













>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
....
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
    char *spath =  Jsi_ValueString(interp, path,0);
    int rc, force = 0; 

    if (!spath) 
        return Jsi_LogError("expected string");
    if (vf && !Jsi_ValueIsBoolean(interp, vf)) 
        return Jsi_LogError("expected boolean");
    SAFEACCESS(path, 1)
    if (vf)
        force = vf->d.val;
    if (force==0)
        rc = MKDIR_DEFAULT(spath);
    else {
        Jsi_Value *npath = Jsi_ValueNewStringDup(interp, spath);
        rc = mkdir_all(interp, npath);
        Jsi_ValueFree(interp, npath);
    }
    if (rc != 0) 
        return Jsi_LogError("can't create directory \"%s\": %s", spath, strerror(errno));    
    return JSI_OK;
}

static Jsi_RC jsi_SAFEACCESS(Jsi_Interp *interp, Jsi_Value *fname, bool write)
{
    SAFEACCESS(fname, write);
    return JSI_OK;
}

static Jsi_RC FileTempfileCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char *filename;
#ifndef __WIN32
    Jsi_Value *vt = Jsi_ValueArrayIndex(interp, args, 0);
    const char *tp, *templ = "/tmp/jsiXXXXXX";

    if (vt && (tp = Jsi_ValueString(interp, vt, NULL))) {
        templ = tp;
    }
    filename = Jsi_Strdup(templ);
    if (Jsi_InterpSafe(interp)) {
        Jsi_Value *fname = Jsi_ValueNewStringConst(interp, templ, -1);
        Jsi_IncrRefCount(interp, fname);
        Jsi_RC rc = jsi_SAFEACCESS(interp, fname, 1);
        Jsi_DecrRefCount(interp, fname);
        if (rc != JSI_OK)
            return rc;
    }
    int fd = mkstemp(filename);
    if (fd < 0)
        goto fail;
    close(fd);
#else
#ifndef MAX_PATH
#define MAX_PATH 1024
................................................................................
    Jsi_Channel ch = Jsi_Open(interp, fname, "rb+");
    if (!ch)
        return JSI_ERROR;
    rc = (Jsi_Truncate(interp, ch, (unsigned int)siz) == 0 ? JSI_OK : JSI_ERROR);
    Jsi_Close(interp, ch);
    return rc;
}

static Jsi_RC FileMknodCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
#ifdef __WIN32
    return Jsi_LogError("mknod unsupported");
#else
    Jsi_Number m, d;
    Jsi_Value *fname = Jsi_ValueArrayIndex(interp, args, 0);
    const char *nam = Jsi_ValueString(interp, fname, NULL);
    if (Jsi_GetNumberFromValue(interp, Jsi_ValueArrayIndex(interp, args, 1), &m) != JSI_OK
        || Jsi_GetNumberFromValue(interp, Jsi_ValueArrayIndex(interp, args, 2), &d) != JSI_OK)
        return Jsi_LogError("expected numbers for arg 2 & 3");
    SAFEACCESS(fname, 1)
    mode_t mode = (mode_t)m;
    dev_t dev = (dev_t)m;
    if (mknod(nam, mode, dev))
        return Jsi_LogError("mknod failed: %s", strerror(errno));
    return JSI_OK;
#endif
}

static Jsi_RC FileChmodCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Value *fname = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_Value *modv = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_Number fmod;
................................................................................
    { "isdir",      FileIsdirCmd,       1,  1, "file:string",  .help="Return true if file is a directory", .retType=(uint)JSI_TT_BOOLEAN },
    { "isfile",     FileIsfileCmd,      1,  1, "file:string",  .help="Return true if file is a normal file", .retType=(uint)JSI_TT_BOOLEAN },
    { "isrelative", FileIsRelativeCmd,  1,  1, "file:string",  .help="Return true if file path is relative", .retType=(uint)JSI_TT_BOOLEAN },
    { "glob",       FileGlobCmd,        0,  2, "pattern:regexp|string|null='*', options:function|object|null=void", .help="Return list of files in dir with optional pattern match", .retType=(uint)JSI_TT_ARRAY, .flags=0, .info=FN_glob, .opts=GlobOptions },
    { "link",       FileLinkCmd,        2,  3, "src:string, dest:string, ishard:boolean=false",  .help="Link a file", .retType=0, .flags=0, .info=FN_link },
    { "lstat",      FileLstatCmd,       1,  1, "file:string",  .help="Return status info for file", .retType=(uint)JSI_TT_OBJECT },
    { "mkdir",      FileMkdirCmd,       1,  1, "file:string",  .help="Create a directory" },
    { "mknod",      FileMknodCmd,       3,  3, "file:string, mode:number, dev:number", .help="Create unix device file using mknod"  },
    { "mtime",      FileMtimeCmd,       1,  1, "file:string",  .help="Return file modified time", .retType=(uint)JSI_TT_NUMBER },
    { "owned",      FileOwnedCmd,       1,  1, "file:string",  .help="Return true if file is owned by user", .retType=(uint)JSI_TT_BOOLEAN },
    { "pwd",        FilePwdCmd,         0,  0, "",  .help="Return current directory", .retType=(uint)JSI_TT_STRING },
    { "remove",     FileRemoveCmd,      1,  2, "file:string, force:boolean=false",  .help="Delete a file or direcotry" },
    { "rename",     FileRenameCmd,      2,  3, "src:string, dest:string, force:boolean=false",  .help="Rename a file, with possible overwrite" },
    { "read",       FileReadCmd,        1,  2, "file:string, mode:string='rb'",  .help="Read a file", .retType=(uint)JSI_TT_STRING },
    { "readable",   FileReadableCmd,    1,  1, "file:string",  .help="Return true if file is readable", .retType=(uint)JSI_TT_BOOLEAN },

Changes to src/jsiFilesys.c.

375
376
377
378
379
380
381





382
383
384
385
386
387
388
...
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514




515
516
517
518
519
520
521
522
...
629
630
631
632
633
634
635

















636
637
638
639
640
641
642
    if (fsPtr == NULL || !fsPtr->chmodProc) return -1;
    return fsPtr->chmodProc(interp, path, mode);
}
int Jsi_Access(Jsi_Interp *interp, Jsi_Value* path, int mode) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->accessProc) return -1;





    return fsPtr->accessProc(interp, path, mode);
}
int Jsi_Remove(Jsi_Interp *interp, Jsi_Value* path, int flags) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->removeProc) return -1;
    return fsPtr->removeProc(interp, path, flags);
................................................................................
    {
        Jsi_LogError("can not open directory: %s", fileName);
        goto done;
    }
    fsPtr = Jsi_FilesystemForPath(interp, file, &data);
    writ = (Jsi_Strchr(s,'w') || Jsi_Strchr(s,'a') || Jsi_Strchr(s,'+'));
    aflag = (writ ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);
    if (interp->isSafe && Jsi_InterpAccess(interp, file, aflag) != JSI_OK) {
        Jsi_LogError("%s access denied", writ?"write":"read");
        goto done;
    }
    if (fsPtr && fsPtr != &jsiFilesystem) {
        ch = fsPtr->openProc(interp, file, Mode);
        if (ch)
            ch->isNative = 0;
        else
            Jsi_LogError("File open failed '%s'", fileName);
    } else {




        FILE *fp = fopen(fileName, Mode);
        fsPtr = &jsiFilesystem;
        if (!fp) {
            if (!quiet)
                Jsi_LogError("File open failed '%s'", fileName);
            goto done;
        }
        ch = (Jsi_Chan *)Jsi_Calloc(1,sizeof(*ch));
................................................................................
    if (!path) return NULL;
    return Jsi_FileRealpathStr(interp, path, newname);
}

static char* jsi_FSRealPathProc(Jsi_Interp *interp, Jsi_Value *src, char *newPath) {
    return Jsi_FileRealpath(interp, src, newPath);
}


















char *Jsi_Realpath(Jsi_Interp *interp, Jsi_Value *src, char *newname)
{
    /* TODO: resolve pwd first. */
    void *data;
    const char *cp = NULL;
    Jsi_Filesystem *fsPtr;







>
>
>
>
>







 







<
<
<
<







>
>
>
>
|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
...
502
503
504
505
506
507
508




509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
...
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
    if (fsPtr == NULL || !fsPtr->chmodProc) return -1;
    return fsPtr->chmodProc(interp, path, mode);
}
int Jsi_Access(Jsi_Interp *interp, Jsi_Value* path, int mode) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->accessProc) return -1;
    if (interp->isSafe && fsPtr && fsPtr == &jsiFilesystem) {
        int aflag = (mode&W_OK ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);
        if (Jsi_InterpAccess(interp, path, aflag) != JSI_OK)
            return -1;
    }
    return fsPtr->accessProc(interp, path, mode);
}
int Jsi_Remove(Jsi_Interp *interp, Jsi_Value* path, int flags) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->removeProc) return -1;
    return fsPtr->removeProc(interp, path, flags);
................................................................................
    {
        Jsi_LogError("can not open directory: %s", fileName);
        goto done;
    }
    fsPtr = Jsi_FilesystemForPath(interp, file, &data);
    writ = (Jsi_Strchr(s,'w') || Jsi_Strchr(s,'a') || Jsi_Strchr(s,'+'));
    aflag = (writ ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);




    if (fsPtr && fsPtr != &jsiFilesystem) {
        ch = fsPtr->openProc(interp, file, Mode);
        if (ch)
            ch->isNative = 0;
        else
            Jsi_LogError("File open failed '%s'", fileName);
    } else {
        if (interp->isSafe && Jsi_InterpAccess(interp, file, aflag) != JSI_OK) {
            Jsi_LogError("%s access denied: %s", writ?"write":"read", fileName);
            goto done;
        }
            FILE *fp = fopen(fileName, Mode);
        fsPtr = &jsiFilesystem;
        if (!fp) {
            if (!quiet)
                Jsi_LogError("File open failed '%s'", fileName);
            goto done;
        }
        ch = (Jsi_Chan *)Jsi_Calloc(1,sizeof(*ch));
................................................................................
    if (!path) return NULL;
    return Jsi_FileRealpathStr(interp, path, newname);
}

static char* jsi_FSRealPathProc(Jsi_Interp *interp, Jsi_Value *src, char *newPath) {
    return Jsi_FileRealpath(interp, src, newPath);
}

Jsi_RC Jsi_PathNormalize(Jsi_Interp *interp, Jsi_Value **pathPtr) {
    Jsi_Value *path = *pathPtr;
    if (!path) {
        Jsi_DString dStr = {};
        *pathPtr = Jsi_ValueNewStringDup(interp, Jsi_GetCwd(interp, &dStr));
        Jsi_IncrRefCount(interp, *pathPtr);
        Jsi_DSFree(&dStr);
    } else {
        const char *rn = Jsi_Realpath(interp, path, NULL);
        if (!rn) return JSI_ERROR;
        Jsi_DecrRefCount(interp, *pathPtr);
        *pathPtr = Jsi_ValueNewString(interp, rn, -1);
        Jsi_IncrRefCount(interp, *pathPtr);
    }
    return JSI_OK;
}

char *Jsi_Realpath(Jsi_Interp *interp, Jsi_Value *src, char *newname)
{
    /* TODO: resolve pwd first. */
    void *data;
    const char *cp = NULL;
    Jsi_Filesystem *fsPtr;

Changes to src/jsiInt.h.

1050
1051
1052
1053
1054
1055
1056







1057
1058
1059
1060
1061


1062
1063
1064
1065
1066
1067
1068
    jsi_ScopeChain *scope_save;         /* saved scope (used in catch block/with block)*/
    Jsi_Value *curscope_save;           /* saved current scope */
    struct jsi_TryList *next;
    bool inCatch;
    bool inFinal;
} jsi_TryList;








struct Jsi_Interp {
#ifdef JSI_HAS_SIG
    jsi_Sig sig;
#endif
    bool isSafe;


    Jsi_Value *safeReadDirs;
    Jsi_Value *safeWriteDirs;
    const char *safeExecPattern;
    Jsi_DebugInterp debugOpts;
    struct jsi_TryList *tryList;
    bool deleting;
    bool destroying;







>
>
>
>
>
>
>




|
>
>







1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
    jsi_ScopeChain *scope_save;         /* saved scope (used in catch block/with block)*/
    Jsi_Value *curscope_save;           /* saved current scope */
    struct jsi_TryList *next;
    bool inCatch;
    bool inFinal;
} jsi_TryList;

typedef enum {
    jsi_safe_None,
    jsi_safe_Read,
    jsi_safe_Write,
    jsi_safe_Write2
} jsi_safe_mode;

struct Jsi_Interp {
#ifdef JSI_HAS_SIG
    jsi_Sig sig;
#endif
    bool isSafe, startSafe;
    jsi_safe_mode safeMode;
    int iskips;
    Jsi_Value *safeReadDirs;
    Jsi_Value *safeWriteDirs;
    const char *safeExecPattern;
    Jsi_DebugInterp debugOpts;
    struct jsi_TryList *tryList;
    bool deleting;
    bool destroying;

Changes to src/jsiInterp.c.

63
64
65
66
67
68
69

70
71
72
73
74
75
76
...
104
105
106
107
108
109
110
111

112
113
114
115
116
117
118
...
636
637
638
639
640
641
642


643
644
645
646
647
648
649
...
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685

686








687
688

689
690
691
692
693
694
695

696
697
698
699
700
701
702
703
704
705
706
707



708
709
710
711



712
713
714
715







716
717
718
719
720
721
722
723

724
725
726
727

728












729
730
731
732

733


734


735
736
737
738
739
740
741
742
743
744


745
746


747
748
749


750
751










752
753
754
755


756
757
758
759
760
761
762
763


764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783




784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
....
1038
1039
1040
1041
1042
1043
1044

1045
1046
1047
1048
1049
1050
1051
....
1082
1083
1084
1085
1086
1087
1088
1089

1090
1091
1092
1093
1094
1095
1096
1097
1098

1099
1100

1101



1102
1103
1104
1105
1106
1107
1108
....
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151

1152
1153

1154
1155
1156
1157
1158
1159

1160
1161
1162
1163
1164
1165
1166

1167
1168
1169

1170
1171
1172
1173

1174
1175
1176




1177





1178
1179
1180
1181
1182
1183
1184
1185
1186
1187


1188
1189
1190
1191
1192
1193
1194




1195
1196
1197
1198
1199
1200
1201
....
1283
1284
1285
1286
1287
1288
1289


1290


1291
1292
1293
1294
1295
1296
1297
....
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
....
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
....
2454
2455
2456
2457
2458
2459
2460



2461

2462
2463
2464
2465
2466
2467
2468
    JSI_OPT(BOOL,  jsi_SubOptions, mutexUnlock, .help="Unlock own mutex when evaling in other interps (true)", jsi_IIOF),
    JSI_OPT(BOOL,  jsi_SubOptions, noproto,     .help="Disable support of the OOP symbols:  __proto__, prototype, constructor, etc"),
    JSI_OPT(BOOL,  jsi_SubOptions, noReadline,  .help="In interactive mode disable use of readline" ),
    JSI_OPT(BOOL,  jsi_SubOptions, outUndef,     .help="In interactive mode output result values that are undefined"),
    JSI_OPT_END(jsi_SubOptions, .help="Lesser sub-feature options")
};


static const char *jsi_TypeChkStrs[] = { "parse", "run", "all", "error", "strict", "noundef", "nowith", "funcsig", NULL };
static const char *jsi_callTraceStrs[] = { "funcs", "cmds", "new", "return", "args", "notrunc", "noparent", "full", "before", NULL};
const char *jsi_AssertModeStrs[] = { "throw", "log", "puts", NULL};

static Jsi_OptionSpec InterpOptions[] = {
    JSI_OPT(ARRAY, Jsi_Interp, args,        .help="The console.arguments for interp", jsi_IIOF),
    JSI_OPT(BOOL,  Jsi_Interp, asserts,     .help="Enable assert" ),
................................................................................
    JSI_OPT(FUNC,  Jsi_Interp, onComplete,  .help="Function to return commands completions for interactive mode.  Default uses Info.completions ", .flags=0, .custom=0, .data=(void*)"prefix:string, start:number, end:number" ),
    JSI_OPT(FUNC,  Jsi_Interp, onEval,      .help="Function to get control for interactive evals", .flags=0, .custom=0, .data=(void*)"cmd:string" ),
    JSI_OPT(FUNC,  Jsi_Interp, onExit,      .help="Command to call in parent on exit, returns true to continue", jsi_IIOF , .custom=0, .data=(void*)""),
    JSI_OPT(INT,   Jsi_Interp, opTrace,     .help="Set debugging level for OPCODE execution"),
    JSI_OPT(ARRAY, Jsi_Interp, pkgDirs,     .help="list of library directories for require() to search" ),
    JSI_OPT(BOOL,  Jsi_Interp, profile,     .help="On exit generate profile of function calls"),
    JSI_OPT(CUSTOM,Jsi_Interp, recvCallback,.help="Command to recv 'send' msgs from parent interp", .flags=0, .custom=Jsi_Opt_SwitchParentFunc, .data=(void*)"msg:string"),
    JSI_OPT(VALUE, Jsi_Interp, retValue,      .help="Return value from last eval", jsi_IIRO),

    JSI_OPT(ARRAY, Jsi_Interp, safeReadDirs,.help="In safe mode, files/dirs to allow reads from", jsi_IIOF),
    JSI_OPT(ARRAY, Jsi_Interp, safeWriteDirs,.help="In safe mode, files/dirs to allow writes to", jsi_IIOF),
    JSI_OPT(STRKEY, Jsi_Interp,safeExecPattern,.help="In safe mode, regexp pattern allow exec of commands", jsi_IIOF),
    JSI_OPT(STRKEY,Jsi_Interp, scriptStr,   .help="Interp init script string", jsi_IIOF),
    JSI_OPT(STRING,Jsi_Interp, scriptFile,  .help="Interp init script file"),
    JSI_OPT(STRING,Jsi_Interp, stdinStr,    .help="String to use as stdin for console.input()"),
    JSI_OPT(STRING,Jsi_Interp, stdoutStr,   .help="String to collect stdout for puts()"),
................................................................................
        argv = opts->argv;
    }
    if (!interp)
        interp = Jsi_InterpNew(opts);
    if (!interp)
        return NULL;
    Jsi_InterpOnDelete(interp, &jsi_InterpDelete, (void*)&jsi_InterpDelete);



#ifndef NO_JAZ
    /* Mount zip at end of executable */
    Jsi_Value *v = Jsi_Executable(interp);
    const char *exeFile = (v?Jsi_ValueString(interp, v, NULL):NULL);
    int jsFound = 0;
    if (v && (argc != 2 || Jsi_Strcmp(argv[1], "--nozvfs"))) {
................................................................................
            else if (rc != 0) {
                fprintf(stderr, "Error\n");
                return jsi_DoExit(interp, 1);
            }
        }
    }
#endif
    const char *iext = (argc<=1?NULL:Jsi_Strrchr(argv[1], '.'));
    if (interp->selfZvfs && iext && Jsi_Strcmp(iext,".fossil")==0) {
        rc = Jsi_EvalString(interp, "runModule('Jsi_Archive');", JSI_EVAL_ISMAIN);
        goto done;
    }
    Jsi_ShiftArgs(interp, NULL);
    if (argc == 1) {
iact:
        if (interp->opts.no_interactive)
            return interp;
        rc = Jsi_Interactive(interp, JSI_OUTPUT_QUOTE|JSI_OUTPUT_NEWLINES);
    } else {
        if (argc == 2 && !Jsi_Strcmp(argv[1], "-h" )) {
            puts("usage: jsish -h/--help | -v/--version | -d/--debug | -D/--debugui\n\t"
            "| -u/--unittest | -U/-UU | -s/--safe | -Z/--zip | -S/--sqliteui\n\t"
            "| -w/--wget | -W/--websrv | -H/--htmli | -J/--jsi | -C/--cssi\n\t"
            "| -c/--cdata | -m/--module | -M/--make | -e/--eval | -t/--tracecall\n\t"
            "| -a/--archive | -T/--typecheck OPT | -IOPT VAL | FILE arg arg ...\nUse --help for long help.");
            return jsi_DoExit(interp, 1);

        }








        if (argc == 2 && !Jsi_Strcmp(argv[1], "--help")) {
            puts("jsish arguments:\n"

              "  -a/--archive FILE\tMount an archive (zip, sqlar or fossil repo) and run module.\n"
              "  -c/--cdata FILE\tGenerate .c or JSON output from a .jsc description.\n"
              "  -C/--cssi FILE\tPreprocess embedded CSS in .css file.\n"
              "  -d/--debug FILE\tRun console debugger on script.\n"
              "  -D/--debugui FILE\tRun web-gui debugger on script.\n"
              "  -e/--eval STRING\tEvaluate a javascript string and exit.\n"
              "  -g/--gendeep FILES\tGenerate html output from markdeep source.\n"

              "  -h/--help\t\tPrint help in short or long form.\n"
              "  -H/--htmli FILE\tPreprocess embedded jsi in .htmli file.\n"
              "  -J/--jsi FILE\t\tPreprocess a .jsi file to typecheck in standard javascript.\n"
              "  -m/--module FILE\tSource file and invoke runModule.\n"
              "  -M/--make FILE\tPreprocess script as a Jsi Makefile.\n"
              "  -s/--safe FILE\tRun script in safe sub-interp.\n"
              "  -S/--sqliteui DBFILE\tRun Sqlite web-gui.\n"
              "  -t/--tracecall\tTrace all function calls.\n"
              "  -T/--typecheck OPT\tEnable typechecking.\n"
              "  -u/--unittest FILE\tRun unit-tests on a script file, or a dir containing .js/.jsi files.\n"
              "  -U/-UU SCRIPT\t\tShow output from unit-test mode, omitting pass/fail compare.\n"
              "  -v/--version\t\tPrint short/long version info and exit.\n"



              "  -Z/--zip\t\tUsed to append/manage zip files at end of executable.\n"
              "  -w/--wget URL\t\tWeb client to download file from url.\n"
              "  -W/--websrv FILE\tServe out file in web server, with preprocessing.\n"
              "  -IOPT VAL\t\tSet an internal interp option value.\n"



              "\nInterp options may also be set via the environment eg. JSI_INTERP_OPTS='{coverage:true}'\n"
               );
            return jsi_DoExit(interp, 1);
        }







        if (argc == 2 && !Jsi_Strcmp(argv[1], "--version")) {
            char str[200] = "\n";
            Jsi_Channel chan = Jsi_Open(interp, Jsi_ValueNewStringKey(interp, "/zvfs/lib/sourceid.txt"), "r");
            if (chan)
                Jsi_Read(interp, chan, str, sizeof(str));
            printf("%u.%u.%u %.4" JSI_NUMGFMT " %s", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE, Jsi_Version(), str);
            return jsi_DoExit(interp, 1);
        }

        if (argc == 2 && !Jsi_Strcmp(argv[1], "-v" )) {
            printf("%u.%u.%u\n", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE);
            return jsi_DoExit(interp, 1);
        }

        if (argc > 2 && (Jsi_Strcmp(argv[1], "--module")==0 || Jsi_Strcmp(argv[1], "-m" )==0)) {












            if (argv[2][0] == '-')
                rc = Jsi_EvalString(interp, "runModule('Jsi_Module');", JSI_EVAL_ISMAIN);
            else {
                Jsi_DString dStr = {};

                const char* cpe = Jsi_Strrchr(argv[2], '.');


                int len = (cpe?cpe-argv[2]:(int)Jsi_Strlen(argv[2]));


                Jsi_DSPrintf(&dStr, "source(\"%s\"); puts(runModule(\"%.*s\",console.args.slice(1)));", argv[2], len, argv[2]);
                rc = Jsi_EvalString(interp, Jsi_DSValue(&dStr), JSI_EVAL_NOSKIPBANG);
                Jsi_DSFree(&dStr);
            }
        }
        else if (argc == 3 && (Jsi_Strcmp(argv[1], "--eval")==0 || Jsi_Strcmp(argv[1], "-e" )==0))
            rc = Jsi_EvalString(interp, argv[2], JSI_EVAL_NOSKIPBANG);

        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--debug")==0 || Jsi_Strcmp(argv[1], "-d" )==0)) {
            interp->debugOpts.isDebugger = 1;


            rc = Jsi_EvalString(interp, "runModule('Jsi_Debug');", JSI_EVAL_ISMAIN);
        } else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--safe")==0 || Jsi_Strcmp(argv[1], "-s" )==0))


            rc = Jsi_EvalString(interp, "runModule('Jsi_Safe');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--debugui")==0 || Jsi_Strcmp(argv[1], "-D" )==0)) {
            interp->debugOpts.isDebugger = 1;


            rc = Jsi_EvalString(interp, "runModule('Jsi_DebugUI');", JSI_EVAL_ISMAIN);
        } else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--wget")==0 || Jsi_Strcmp(argv[1], "-w" )==0))










            rc = Jsi_EvalString(interp, "runModule('Jsi_Wget');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--gendeep")==0 || Jsi_Strcmp(argv[1], "-g" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_GenDeep');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--websrv")==0 || Jsi_Strcmp(argv[1], "-W" )==0))


            rc = Jsi_EvalString(interp, "runModule('Jsi_Websrv');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--archive")==0 || Jsi_Strcmp(argv[1], "-a" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_Archive');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--sqliteui")==0 || Jsi_Strcmp(argv[1], "-S" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_SqliteUI');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--cdata")==0 || Jsi_Strcmp(argv[1], "-c" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_CData');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--zip")==0 || Jsi_Strcmp(argv[1], "-Z" )==0))


            rc = Jsi_EvalString(interp, "runModule('Jsi_Zip');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--cssi")==0 || Jsi_Strcmp(argv[1], "-C" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_Csspp');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--jsi")==0 || Jsi_Strcmp(argv[1], "-J" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_Jspp');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--htmli")==0 || Jsi_Strcmp(argv[1], "-H" )==0))
            rc = Jsi_EvalString(interp, "runModule('Jsi_Htmlpp');", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--unittest")==0 || Jsi_Strcmp(argv[1], "-u" )==0))
            rc = Jsi_EvalString(interp, "exit(runModule('Jsi_UnitTest'));", JSI_EVAL_ISMAIN);
        else if (interp->selfZvfs && argc > 1 && (Jsi_Strcmp(argv[1], "--make")==0 || Jsi_Strcmp(argv[1], "-M" )==0))
            rc = Jsi_EvalString(interp, "exit(runModule('Jsi_Make'));", JSI_EVAL_ISMAIN);
        else {
            int iocnt;
            for (iocnt = 1; (iocnt+1)<argc; iocnt+=2) {
                if (Jsi_Strcmp(argv[iocnt], "-t") == 0 || Jsi_Strcmp(argv[iocnt], "--tracecall") == 0) {iocnt--; continue; }
                if (Jsi_Strcmp(argv[iocnt], "-T") == 0 || Jsi_Strcmp(argv[iocnt], "--typecheck") == 0) {continue; }
                if (Jsi_Strcmp(argv[iocnt], "-U") == 0) {iocnt--; continue; }
                if (Jsi_Strcmp(argv[iocnt], "-UU") == 0) {iocnt--; continue; }
                if (Jsi_Strncmp(argv[iocnt], "-I", 2) == 0) continue;
                break;




            }
            argc -= (iocnt-1);
            argv += (iocnt-1);
            if (argc<2)
                goto iact;
            const char *ext = Jsi_Strrchr(argv[1], '.');

            /* Support running "main.jsi" from a zip file. */
            if (ext && (Jsi_Strcmp(ext,".zip")==0 ||Jsi_Strcmp(ext,".jsz")==0 ) ) {
                rc = Jsi_EvalZip(interp, argv[1], NULL, &jsFound);
                if (rc<0) {
                    fprintf(stderr, "zip mount failed\n");
                    return jsi_DoExit(interp, 1);
                }
                if (!(jsFound&JSI_ZIP_MAIN)) {
                    fprintf(stderr, "main.jsi not found\n");
                    return jsi_DoExit(interp, 1);
                }
            } else {
                if (argc>1) {
                    jsi_vf = Jsi_ValueNewStringKey(interp, argv[1]);
                    Jsi_IncrRefCount(interp, jsi_vf);
                }
                rc = Jsi_EvalFile(interp, jsi_vf, JSI_EVAL_ARGV0|JSI_EVAL_AUTOINDEX);
                if (jsi_vf) {
                    Jsi_DecrRefCount(interp, jsi_vf);
                    jsi_vf = NULL;
                }

            }
        }
        if (jsi_deleted) //TODO: rationalize jsi_deleted, jsi_exitCode, etc
            return jsi_DoExit(rc==JSI_EXIT?NULL:interp, jsi_exitCode);
        if (rc == 0) {
            /* Skip output from an ending semicolon which evaluates to undefined */
            Jsi_Value *ret = Jsi_ReturnValue(interp);
            if (!Jsi_ValueIsType(interp, ret, JSI_VT_UNDEF)) {
                Jsi_DString dStr = {};
                fputs(Jsi_ValueGetDString(interp, ret, &dStr, 0), stdout);
                Jsi_DSFree(&dStr);
                fputs("\n", stdout);
            }
        } else {
            if (!interp->parent && !interp->isHelp)
                fputs("ERROR\n", stderr);
            return jsi_DoExit(interp, 1);
        }

    }
done:
    if (rc == JSI_EXIT) {
        if (opts)
            opts->exitCode = jsi_exitCode;
        return NULL;
    }
    if (jsi_deleted == 0 && interp->opts.auto_delete) {
................................................................................
        iopts->interp = interp;
        interp->opts = *iopts;
    }
    interp->logOpts.file = 1;
    interp->logOpts.func = 1;
    int argc = interp->opts.argc;
    char **argv = interp->opts.argv;

    interp->parent = parent;
    interp->topInterp = (parent == NULL ? interp: parent->topInterp);
    if (jsiIntData.mainInterp == NULL)
        jsiIntData.mainInterp = interp->topInterp;
    interp->mainInterp = jsiIntData.mainInterp; // The first interps handles exit.
    interp->memDebug = interp->opts.mem_debug;
    if (parent) {
................................................................................
    const char *ocp2;
    if (ocp && ((ocp2=Jsi_Strstr(ocp,"memDebug:"))))
        interp->memDebug=strtol(ocp+sizeof("memDebug:"), NULL, 0);
    if (ocp && ((ocp2=Jsi_Strstr(ocp,"compat:"))))
        interp->subOpts.compat=(ocp[sizeof("compat:")]=='t');
    for (iocnt = 1; (iocnt+1)<argc; iocnt+=2)
    {
        if (Jsi_Strcmp(argv[iocnt], "-T") == 0 || Jsi_Strcmp(argv[iocnt], "--typecheck") == 0) {

            continue;
        }
        if (Jsi_Strcmp(argv[iocnt], "-t") == 0 || Jsi_Strcmp(argv[iocnt], "--tracecall") == 0
            || Jsi_Strcmp(argv[iocnt], "-U") == 0 || Jsi_Strcmp(argv[iocnt], "-UU") == 0) {
            iocnt--;
            continue;
        }
        if (Jsi_Strncmp(argv[iocnt], "-I", 2)) break;
        if (!Jsi_Strcmp("memDebug", argv[iocnt]+2))

            interp->memDebug=strtol(argv[iocnt+1], NULL, 0);
        else if (!Jsi_Strcmp("compat", argv[iocnt]+2))

            interp->subOpts.compat=strtol(argv[iocnt+1], NULL, 0);



    }
    SIGINIT(interp,INTERP);
    interp->NullValue = Jsi_ValueNewNull(interp);
    Jsi_IncrRefCount(interp, interp->NullValue);
#ifdef __WIN32
    Jsi_DString cwdStr;
    Jsi_DSInit(&cwdStr);
................................................................................
    mapType = JSI_MAP_TREE;
#endif

    if (interp == jsiIntData.mainInterp || interp->threadId != jsiIntData.mainInterp->threadId) {
        interp->strKeyTbl = Jsi_MapNew(interp,  mapType, JSI_KEYS_STRING, NULL);
        interp->subOpts.privKeys = 1;
    }
    // Handle interp options: -T|--typecheck value and -Ixxx value
    for (iocnt = 1; (iocnt+1)<argc; iocnt+=2)
    {
        if (Jsi_Strcmp(argv[iocnt], "-t") == 0 || Jsi_Strcmp(argv[iocnt], "--tracecall") == 0) {

            interp->traceCall |= (jsi_callTraceFuncs |jsi_callTraceArgs |jsi_callTraceReturn | jsi_callTraceBefore | jsi_callTraceFullPath);
            iocnt--;

            continue;
        }
        if (Jsi_Strcmp(argv[iocnt], "-U") == 0) {
            interp->asserts = 1;
            interp->unitTest = 1;
            iocnt--;

            continue;
        }
        if (Jsi_Strcmp(argv[iocnt], "-UU") == 0) {
            interp->asserts = 1;
            interp->unitTest = 5;
            interp->tracePuts = 1;
            iocnt--;

            continue;
        }
        if (Jsi_Strcmp(argv[iocnt], "-T") == 0 || Jsi_Strcmp(argv[iocnt], "--typecheck") == 0) {

            if (jsi_ParseTypeCheckStr(interp, argv[iocnt+1]) != JSI_OK) {
                Jsi_InterpDelete(interp);
                return NULL;
            }

            continue;
        }
        if (Jsi_Strncmp(argv[iocnt], "-I", 2)) break;




        const char *argStr = argv[iocnt+1];





        DECL_VALINIT(argV);
        Jsi_Value *argValue = &argV;
        Jsi_Number dv;
        bool bv;
        if (Jsi_GetBool(interp, argStr, &bv) == JSI_OK) {
            Jsi_ValueMakeBool(interp, &argValue, bv);
        } else if (Jsi_GetDouble(interp, argStr, &dv) == JSI_OK) {
            Jsi_ValueMakeNumber(interp, &argValue, dv);
        } else if (!Jsi_Strcmp("null", argStr)) {
            Jsi_ValueMakeNull(interp, &argValue);


        } else {
            Jsi_ValueMakeStringKey(interp, &argValue, argStr);
        }
        if (JSI_OK != Jsi_OptionsSet(interp, InterpOptions, interp, argv[iocnt]+2, argValue, 0)) {
            Jsi_InterpDelete(interp);
            return NULL;
        }




    }
    if (!interp->strKeyTbl)
        interp->strKeyTbl = jsiIntData.mainInterp->strKeyTbl;
    if (opts) {
        interp->inopts = opts = Jsi_ValueDupJSON(interp, opts);
        if (Jsi_OptionsProcess(interp, InterpOptions, interp, opts, 0) < 0) {
            Jsi_DecrRefCount(interp, opts);
................................................................................
    interp->threadId = Jsi_CurrentThread();
    if (interp->parent && interp->subthread==0 && interp->threadId != interp->parent->threadId) {
        interp->threadId = interp->parent->threadId;
#ifndef JSI_MEM_DEBUG
        Jsi_LogWarn("non-threaded sub-interp created by different thread than parent");
#endif
    }


    if (!interp->parent) {


        if (interp->debugOpts.msgCallback)
            Jsi_LogWarn("ignoring msgCallback");
        if (interp->debugOpts.putsCallback)
            Jsi_LogWarn("ignoring putsCallback");
        if (interp->busyCallback)
            Jsi_LogWarn("ignoring busyCallback");
        if (interp->recvCallback)
................................................................................

    if (!interp->isSafe) {
        JSIDOINIT(Load);
#if JSI__SIGNAL==1
        JSIDOINIT(Signal);
#endif
    }
    if (interp->isSafe == 0 || interp->safeWriteDirs!=NULL || interp->safeReadDirs!=NULL) {
#if JSI__FILESYS==1
        JSIDOINIT(FileCmds);
        JSIDOINIT(Filesys);
#endif
    }
#if JSI__SQLITE==1
    JSIDOINIT2(Sqlite);
................................................................................
    if (JSI_USER_EXTENSION (interp, 0) != JSI_OK) {
        fprintf(stderr, "extension load failed");
        return jsi_DoExit(interp, 1);
    }
#endif
    Jsi_PkgProvide(interp, "Jsi", JSI_VERSION, NULL);
    if (argc > 0) {
        char *ss = argv[0];
        char epath[PATH_MAX] = ""; // Path of executable
#ifdef __WIN32

        if (GetModuleFileName(NULL, epath, sizeof(epath))>0)
            ss = epath;
#else
#ifndef PROC_SELF_DIR
................................................................................
    if (!mntDir)
        ret = Jsi_ValueNew(interp);
    else {
        vmnt = Jsi_ValueNewStringKey(interp, mntDir);
        Jsi_IncrRefCount(interp, vmnt);
        Jsi_HashSet(interp->genValueTbl, vmnt, vmnt);
    }



    rc =Jsi_Mount(interp, vexe, vmnt, &ret);

    if (rc != JSI_OK)
        return rc;
    Jsi_DString dStr, bStr;
    Jsi_DSInit(&dStr);
    Jsi_DSInit(&bStr);
    if (!mntDir) {
        mntDir = Jsi_KeyAdd(interp, Jsi_ValueString(interp, ret, NULL));







>







 







|
>







 







>
>







 







|





|
<



<
<
<
<
<
<
<
<
>
|
>
>
>
>
>
>
>
>
|
|
>
|
|
<
|
|
<
|
>
|
<
<
<
<
|
<
<
<
<
<
<
>
>
>
|
<
<
<
>
>
>
|
|
|
|
>
>
>
>
>
>
>
|
|
|
|
|
|
|
<
>
|
|
|
<
>
|
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
>
|
>
>
|
>
>
|
|
|
|
<
<
<
<
<
<
>
>
|
<
>
>
|
<
<
>
>
|
<
>
>
>
>
>
>
>
>
>
>
|
<
<
<
>
>
|
<
<
<
<
<
<
<
>
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

>
>
>
>
|
<
<
<
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

<







 







>







 







|
>


<
|



|
|
>
|
<
>
|
>
>
>







 







|
|

|
>


>


|



>


|




>


<
>




>


<
>
>
>
>
|
>
>
>
>
>
|
|
|
<
|
|
<
<
|
|
>
>
|
|
|
|
|
|
|
>
>
>
>







 







>
>

>
>







 







|







 







|







 







>
>
>

>







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
...
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
...
664
665
666
667
668
669
670
671
672
673
674
675
676
677

678
679
680








681
682
683
684
685
686
687
688
689
690
691
692
693
694
695

696
697

698
699
700




701






702
703
704
705



706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726

727
728
729
730

731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759






760
761
762

763
764
765


766
767
768

769
770
771
772
773
774
775
776
777
778
779



780
781
782







783
784
785


















786
787
788
789
790
791



792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835

836
837
838
839
840
841
842
....
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
....
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096

1097
1098
1099
1100
1101
1102
1103
1104

1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
....
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180

1181
1182
1183
1184
1185
1186
1187
1188

1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201

1202
1203


1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
....
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
....
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
....
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
....
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
    JSI_OPT(BOOL,  jsi_SubOptions, mutexUnlock, .help="Unlock own mutex when evaling in other interps (true)", jsi_IIOF),
    JSI_OPT(BOOL,  jsi_SubOptions, noproto,     .help="Disable support of the OOP symbols:  __proto__, prototype, constructor, etc"),
    JSI_OPT(BOOL,  jsi_SubOptions, noReadline,  .help="In interactive mode disable use of readline" ),
    JSI_OPT(BOOL,  jsi_SubOptions, outUndef,     .help="In interactive mode output result values that are undefined"),
    JSI_OPT_END(jsi_SubOptions, .help="Lesser sub-feature options")
};

static const char *jsi_SafeModeStrs[] = { "none", "read", "write", "write2", NULL };
static const char *jsi_TypeChkStrs[] = { "parse", "run", "all", "error", "strict", "noundef", "nowith", "funcsig", NULL };
static const char *jsi_callTraceStrs[] = { "funcs", "cmds", "new", "return", "args", "notrunc", "noparent", "full", "before", NULL};
const char *jsi_AssertModeStrs[] = { "throw", "log", "puts", NULL};

static Jsi_OptionSpec InterpOptions[] = {
    JSI_OPT(ARRAY, Jsi_Interp, args,        .help="The console.arguments for interp", jsi_IIOF),
    JSI_OPT(BOOL,  Jsi_Interp, asserts,     .help="Enable assert" ),
................................................................................
    JSI_OPT(FUNC,  Jsi_Interp, onComplete,  .help="Function to return commands completions for interactive mode.  Default uses Info.completions ", .flags=0, .custom=0, .data=(void*)"prefix:string, start:number, end:number" ),
    JSI_OPT(FUNC,  Jsi_Interp, onEval,      .help="Function to get control for interactive evals", .flags=0, .custom=0, .data=(void*)"cmd:string" ),
    JSI_OPT(FUNC,  Jsi_Interp, onExit,      .help="Command to call in parent on exit, returns true to continue", jsi_IIOF , .custom=0, .data=(void*)""),
    JSI_OPT(INT,   Jsi_Interp, opTrace,     .help="Set debugging level for OPCODE execution"),
    JSI_OPT(ARRAY, Jsi_Interp, pkgDirs,     .help="list of library directories for require() to search" ),
    JSI_OPT(BOOL,  Jsi_Interp, profile,     .help="On exit generate profile of function calls"),
    JSI_OPT(CUSTOM,Jsi_Interp, recvCallback,.help="Command to recv 'send' msgs from parent interp", .flags=0, .custom=Jsi_Opt_SwitchParentFunc, .data=(void*)"msg:string"),
    JSI_OPT(VALUE, Jsi_Interp, retValue,    .help="Return value from last eval", jsi_IIRO),
    JSI_OPT(CUSTOM,Jsi_Interp, safeMode,    .help="Set isSafe mode and setup safeReadDirs and/or safeWriteDirs for pwd and script-dir", jsi_IIOF, .custom=Jsi_Opt_SwitchEnum, .data=jsi_SafeModeStrs ),
    JSI_OPT(ARRAY, Jsi_Interp, safeReadDirs,.help="In safe mode, files/dirs to allow reads from", jsi_IIOF),
    JSI_OPT(ARRAY, Jsi_Interp, safeWriteDirs,.help="In safe mode, files/dirs to allow writes to", jsi_IIOF),
    JSI_OPT(STRKEY, Jsi_Interp,safeExecPattern,.help="In safe mode, regexp pattern allow exec of commands", jsi_IIOF),
    JSI_OPT(STRKEY,Jsi_Interp, scriptStr,   .help="Interp init script string", jsi_IIOF),
    JSI_OPT(STRING,Jsi_Interp, scriptFile,  .help="Interp init script file"),
    JSI_OPT(STRING,Jsi_Interp, stdinStr,    .help="String to use as stdin for console.input()"),
    JSI_OPT(STRING,Jsi_Interp, stdoutStr,   .help="String to collect stdout for puts()"),
................................................................................
        argv = opts->argv;
    }
    if (!interp)
        interp = Jsi_InterpNew(opts);
    if (!interp)
        return NULL;
    Jsi_InterpOnDelete(interp, &jsi_InterpDelete, (void*)&jsi_InterpDelete);
    argc -= interp->iskips;
    argv += interp->iskips;

#ifndef NO_JAZ
    /* Mount zip at end of executable */
    Jsi_Value *v = Jsi_Executable(interp);
    const char *exeFile = (v?Jsi_ValueString(interp, v, NULL):NULL);
    int jsFound = 0;
    if (v && (argc != 2 || Jsi_Strcmp(argv[1], "--nozvfs"))) {
................................................................................
            else if (rc != 0) {
                fprintf(stderr, "Error\n");
                return jsi_DoExit(interp, 1);
            }
        }
    }
#endif
    const char *ai1, *iext = (argc<=1?NULL:Jsi_Strrchr(argv[1], '.'));
    if (interp->selfZvfs && iext && Jsi_Strcmp(iext,".fossil")==0) {
        rc = Jsi_EvalString(interp, "runModule('Jsi_Archive');", JSI_EVAL_ISMAIN);
        goto done;
    }
    Jsi_ShiftArgs(interp, NULL);
    if (argc <= 1) {

        if (interp->opts.no_interactive)
            return interp;
        rc = Jsi_Interactive(interp, JSI_OUTPUT_QUOTE|JSI_OUTPUT_NEWLINES);








        goto done;
    }
    ai1 = argv[1];
    if (!Jsi_Strcmp(ai1, "-help")) {
        dohelp:
        puts("USAGE:\n  jsish [PREFIX-OPTS] [COMMAND-OPTS|FILE] ...\n"
          "\nPREFIX-OPTS:\n"
          "  --F\t\tTrace all function calls/returns.\n"
          "  --I OPT:VAL\tInterp option: equivalent to Interp.conf({OPT:VAL}).\n"
          "  --T OPT\tTypecheck option: equivalent to \"use OPT\".\n"
          "  --U\t\tDisplay unittest output, minus pass/fail compare.\n"
          "  --V\t\tSame as --U, but adds file and line number to output.\n"
          "\nCOMMAND-OPTS:\n"
          "  -a\t\tArchive: mount an archive (zip, sqlar or fossil repo) and run module.\n"
          "  -c\t\tCData: generate .c or JSON output from a .jsc description.\n"

          "  -d\t\tDebug: console script debugger.\n"
          "  -e STRING\tEval: run javascript in STRING and exit.\n"

          "  -g\t\tGendeep: generate html output from markdeep source.\n"
          "  -h\t\tHelp: show this help.\n"
          "  -m MOD\tModule: invoke runModule, after source if file.\n"




          "  -s\t\tSafe: runs script in safe sub-interp.\n"






          "  -u\t\tUnitTest: test script file(s) or directories .js/.jsi files.\n"
          "  -w\t\tWget: web client to download file from url.\n"
          "  -v\t\tVersion: show version info.\n"
          "  -z\t\tZip: append/manage zip files at end of executable.\n"



          "  -D\t\tDebugUI: web-gui script debugger.\n"
          "  -S\t\tSqliteUI: web-gui for sqlite database file.\n"
          "  -W\t\tWebsrv: web server to serve out content.\n"
          "\nInterp options may also be set via the environment eg. JSI_INTERP_OPTS='{coverage:true}'\n"
           );
        return jsi_DoExit(interp, 1);
    }
    if (!Jsi_Strcmp(ai1, "-version"))
        ai1 = "-v";
    if (ai1[0] == '-') {
        switch (ai1[1]) {
            case 'a':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Archive');", JSI_EVAL_ISMAIN);
                break;
            case 'c':
                rc = Jsi_EvalString(interp, "runModule('Jsi_CData');", JSI_EVAL_ISMAIN);
                break;
            case 'd':
                interp->debugOpts.isDebugger = 1;
                rc = Jsi_EvalString(interp, "runModule('Jsi_Debug');", JSI_EVAL_ISMAIN);
                break;

            case 'D':
                interp->debugOpts.isDebugger = 1;
                rc = Jsi_EvalString(interp, "runModule('Jsi_DebugUI');", JSI_EVAL_ISMAIN);
                break;

            case 'e':
                if (argc != 3)
                    rc = Jsi_LogError("missing argument");
                else
                    rc = Jsi_EvalString(interp, argv[2], JSI_EVAL_NOSKIPBANG);
                break;
            case 'g':
                rc = Jsi_EvalString(interp, "runModule('Jsi_GenDeep');", JSI_EVAL_ISMAIN);
                break;
            case 'h':
                goto dohelp;
            case 'm':
                if (argc <= 2)
                    rc = Jsi_LogError("missing argument");
                else if (argv[2][0] == '-')
                    rc = Jsi_EvalString(interp, "runModule('Jsi_Module');", JSI_EVAL_ISMAIN);
                else {
                    Jsi_DString dStr = {};
                    const char *cps, *cpe;
                    cps = Jsi_Strrchr(argv[2], '/');
                    if (cps) cps++; else cps = argv[2];
                    cpe = Jsi_Strrchr(cps, '.');
                    int len = (cpe?cpe-cps:(int)Jsi_Strlen(cps));
                    if (cpe)
                        Jsi_DSPrintf(&dStr, "source(\"%s\");", argv[2]);
                    Jsi_DSPrintf(&dStr, "puts(runModule(\"%.*s\",console.args.slice(1)));", len, cps);
                    rc = Jsi_EvalString(interp, Jsi_DSValue(&dStr), JSI_EVAL_NOSKIPBANG);
                    Jsi_DSFree(&dStr);
                }






                break;
            case 's':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Safe');", JSI_EVAL_ISMAIN);

                break;
            case 'S':
                rc = Jsi_EvalString(interp, "runModule('Jsi_SqliteUI');", JSI_EVAL_ISMAIN);


                break;
            case 'u':
                rc = Jsi_EvalString(interp, "exit(runModule('Jsi_UnitTest'));", JSI_EVAL_ISMAIN);

                break;
            case 'v': {
                char str[200] = "\n";
                Jsi_Channel chan = Jsi_Open(interp, Jsi_ValueNewStringKey(interp, "/zvfs/lib/sourceid.txt"), "r");
                if (chan)
                    Jsi_Read(interp, chan, str, sizeof(str));
                printf("%u.%u.%u %.4" JSI_NUMGFMT " %s", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE, Jsi_Version(), str);
                return jsi_DoExit(interp, 1);
            }
            case 'w':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Wget');", JSI_EVAL_ISMAIN);



                break;
            case 'W':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Websrv');", JSI_EVAL_ISMAIN);







                break;
            case 'z':
                rc = Jsi_EvalString(interp, "runModule('Jsi_Zip');", JSI_EVAL_ISMAIN);


















                break;
            default:
                puts("usage: jsish [ --I OPT:VAL | --T OPT | --U | --V | --F ] | -e STRING |\n\t"
                "| -a | -c | -d | -D | -h | -m | -s | -S | -u  | -v |-w | -W | -z | FILE ...\nUse -help for long help.");
                return jsi_DoExit(interp, 1);
        }



    } else {
        const char *ext = Jsi_Strrchr(argv[1], '.');

        /* Support running "main.jsi" from a zip file. */
        if (ext && (Jsi_Strcmp(ext,".zip")==0 ||Jsi_Strcmp(ext,".jsz")==0 ) ) {
            rc = Jsi_EvalZip(interp, argv[1], NULL, &jsFound);
            if (rc<0) {
                fprintf(stderr, "zip mount failed\n");
                return jsi_DoExit(interp, 1);
            }
            if (!(jsFound&JSI_ZIP_MAIN)) {
                fprintf(stderr, "main.jsi not found\n");
                return jsi_DoExit(interp, 1);
            }
        } else {
            if (argc>1) {
                jsi_vf = Jsi_ValueNewStringKey(interp, argv[1]);
                Jsi_IncrRefCount(interp, jsi_vf);
            }
            rc = Jsi_EvalFile(interp, jsi_vf, JSI_EVAL_ARGV0|JSI_EVAL_AUTOINDEX);
            if (jsi_vf) {
                Jsi_DecrRefCount(interp, jsi_vf);
                jsi_vf = NULL;
            }

        }
    }
    if (jsi_deleted) //TODO: rationalize jsi_deleted, jsi_exitCode, etc
        return jsi_DoExit(rc==JSI_EXIT?NULL:interp, jsi_exitCode);
    if (rc == 0) {
        /* Skip output from an ending semicolon which evaluates to undefined */
        Jsi_Value *ret = Jsi_ReturnValue(interp);
        if (!Jsi_ValueIsType(interp, ret, JSI_VT_UNDEF)) {
            Jsi_DString dStr = {};
            fputs(Jsi_ValueGetDString(interp, ret, &dStr, 0), stdout);
            Jsi_DSFree(&dStr);
            fputs("\n", stdout);
        }
    } else {
        if (!interp->parent && !interp->isHelp)
            fputs("ERROR\n", stderr);
        return jsi_DoExit(interp, 1);
    }


done:
    if (rc == JSI_EXIT) {
        if (opts)
            opts->exitCode = jsi_exitCode;
        return NULL;
    }
    if (jsi_deleted == 0 && interp->opts.auto_delete) {
................................................................................
        iopts->interp = interp;
        interp->opts = *iopts;
    }
    interp->logOpts.file = 1;
    interp->logOpts.func = 1;
    int argc = interp->opts.argc;
    char **argv = interp->opts.argv;
    char *argv0 = (argv?argv[0]:NULL);
    interp->parent = parent;
    interp->topInterp = (parent == NULL ? interp: parent->topInterp);
    if (jsiIntData.mainInterp == NULL)
        jsiIntData.mainInterp = interp->topInterp;
    interp->mainInterp = jsiIntData.mainInterp; // The first interps handles exit.
    interp->memDebug = interp->opts.mem_debug;
    if (parent) {
................................................................................
    const char *ocp2;
    if (ocp && ((ocp2=Jsi_Strstr(ocp,"memDebug:"))))
        interp->memDebug=strtol(ocp+sizeof("memDebug:"), NULL, 0);
    if (ocp && ((ocp2=Jsi_Strstr(ocp,"compat:"))))
        interp->subOpts.compat=(ocp[sizeof("compat:")]=='t');
    for (iocnt = 1; (iocnt+1)<argc; iocnt+=2)
    {
        const char *aio = argv[iocnt];
        if (Jsi_Strcmp(aio, "--T") == 0) {
            continue;
        }

        if (Jsi_Strcmp(aio, "--F") == 0 || Jsi_Strcmp(aio, "--U") == 0 || Jsi_Strcmp(aio, "--V") == 0) {
            iocnt--;
            continue;
        }
        if (!Jsi_Strcmp(aio, "--I")) {
            const char *aio2 = argv[iocnt+1];
            if (!Jsi_Strncmp("memDebug:", aio2, sizeof("memDebug")))
                interp->memDebug=strtol(aio2+sizeof("memDebug"), NULL, 0);

            else if (!Jsi_Strncmp("compat", aio2, sizeof("compat")))
                interp->subOpts.compat=strtol(aio2+sizeof("compat"), NULL, 0);
            continue;
        }
        break;
    }
    SIGINIT(interp,INTERP);
    interp->NullValue = Jsi_ValueNewNull(interp);
    Jsi_IncrRefCount(interp, interp->NullValue);
#ifdef __WIN32
    Jsi_DString cwdStr;
    Jsi_DSInit(&cwdStr);
................................................................................
    mapType = JSI_MAP_TREE;
#endif

    if (interp == jsiIntData.mainInterp || interp->threadId != jsiIntData.mainInterp->threadId) {
        interp->strKeyTbl = Jsi_MapNew(interp,  mapType, JSI_KEYS_STRING, NULL);
        interp->subOpts.privKeys = 1;
    }
    // Handle interp options: -T value and -Ixxx value
    for (iocnt = 1; (iocnt+1)<argc && !interp->parent; iocnt+=2)
    {
        const char *aio = argv[iocnt];
        if (Jsi_Strcmp(aio, "--F") == 0) {
            interp->traceCall |= (jsi_callTraceFuncs |jsi_callTraceArgs |jsi_callTraceReturn | jsi_callTraceBefore | jsi_callTraceFullPath);
            iocnt--;
            interp->iskips++;
            continue;
        }
        if (Jsi_Strcmp(aio, "--U") == 0) {
            interp->asserts = 1;
            interp->unitTest = 1;
            iocnt--;
            interp->iskips++;
            continue;
        }
        if (Jsi_Strcmp(aio, "--V") == 0) {
            interp->asserts = 1;
            interp->unitTest = 5;
            interp->tracePuts = 1;
            iocnt--;
            interp->iskips++;
            continue;
        }

        if (Jsi_Strcmp(aio, "--T") == 0) {
            if (jsi_ParseTypeCheckStr(interp, argv[iocnt+1]) != JSI_OK) {
                Jsi_InterpDelete(interp);
                return NULL;
            }
            interp->iskips+=2;
            continue;
        }

        if (!Jsi_Strcmp(aio, "--I"))  {
            bool bv = 1;
            char *aio2 = argv[iocnt+1], *aioc = Jsi_Strchr(aio2, ':'),
                argNamS[50], *argNam = aio2;
            const char *argVal;
            if (aioc) {
                argNam = argNamS;
                argVal = aioc+1;
                snprintf(argNamS, sizeof(argNamS), "%.*s", (int)(aioc-aio2), aio2);
            }
            DECL_VALINIT(argV);
            Jsi_Value *argValue = &argV;
            Jsi_Number dv;

            if (!aioc || Jsi_GetBool(interp, argVal, &bv) == JSI_OK) {
                Jsi_ValueMakeBool(interp, &argValue, bv);


            } else if (!Jsi_Strcmp("null", argVal)) {
                Jsi_ValueMakeNull(interp, &argValue);
            } else if (Jsi_GetDouble(interp, argVal, &dv) == JSI_OK) {
                Jsi_ValueMakeNumber(interp, &argValue, dv);
            } else {
                Jsi_ValueMakeStringKey(interp, &argValue, argVal);
            }
            if (JSI_OK != Jsi_OptionsSet(interp, InterpOptions, interp, argNam, argValue, 0)) {
                Jsi_InterpDelete(interp);
                return NULL;
            }
            interp->iskips+=2;
            continue;
        }
        break;
    }
    if (!interp->strKeyTbl)
        interp->strKeyTbl = jsiIntData.mainInterp->strKeyTbl;
    if (opts) {
        interp->inopts = opts = Jsi_ValueDupJSON(interp, opts);
        if (Jsi_OptionsProcess(interp, InterpOptions, interp, opts, 0) < 0) {
            Jsi_DecrRefCount(interp, opts);
................................................................................
    interp->threadId = Jsi_CurrentThread();
    if (interp->parent && interp->subthread==0 && interp->threadId != interp->parent->threadId) {
        interp->threadId = interp->parent->threadId;
#ifndef JSI_MEM_DEBUG
        Jsi_LogWarn("non-threaded sub-interp created by different thread than parent");
#endif
    }
    if (interp->safeMode != jsi_safe_None)
        interp->isSafe = interp->startSafe = 1;
    if (!interp->parent) {
        if (interp->isSafe)
            interp->startSafe = 1;
        if (interp->debugOpts.msgCallback)
            Jsi_LogWarn("ignoring msgCallback");
        if (interp->debugOpts.putsCallback)
            Jsi_LogWarn("ignoring putsCallback");
        if (interp->busyCallback)
            Jsi_LogWarn("ignoring busyCallback");
        if (interp->recvCallback)
................................................................................

    if (!interp->isSafe) {
        JSIDOINIT(Load);
#if JSI__SIGNAL==1
        JSIDOINIT(Signal);
#endif
    }
    if (interp->isSafe == 0 || interp->startSafe || interp->safeWriteDirs!=NULL || interp->safeReadDirs!=NULL) {
#if JSI__FILESYS==1
        JSIDOINIT(FileCmds);
        JSIDOINIT(Filesys);
#endif
    }
#if JSI__SQLITE==1
    JSIDOINIT2(Sqlite);
................................................................................
    if (JSI_USER_EXTENSION (interp, 0) != JSI_OK) {
        fprintf(stderr, "extension load failed");
        return jsi_DoExit(interp, 1);
    }
#endif
    Jsi_PkgProvide(interp, "Jsi", JSI_VERSION, NULL);
    if (argc > 0) {
        char *ss = argv0;
        char epath[PATH_MAX] = ""; // Path of executable
#ifdef __WIN32

        if (GetModuleFileName(NULL, epath, sizeof(epath))>0)
            ss = epath;
#else
#ifndef PROC_SELF_DIR
................................................................................
    if (!mntDir)
        ret = Jsi_ValueNew(interp);
    else {
        vmnt = Jsi_ValueNewStringKey(interp, mntDir);
        Jsi_IncrRefCount(interp, vmnt);
        Jsi_HashSet(interp->genValueTbl, vmnt, vmnt);
    }
    bool osafe = interp->isSafe;
    if (interp->startSafe)
        interp->isSafe = 0;
    rc =Jsi_Mount(interp, vexe, vmnt, &ret);
    interp->isSafe = osafe;
    if (rc != JSI_OK)
        return rc;
    Jsi_DString dStr, bStr;
    Jsi_DSInit(&dStr);
    Jsi_DSInit(&bStr);
    if (!mntDir) {
        mntDir = Jsi_KeyAdd(interp, Jsi_ValueString(interp, ret, NULL));

Changes to src/jsiLoad.c.

7
8
9
10
11
12
13

14
15
16
17
18
19
20
..
43
44
45
46
47
48
49







50
51
52
53
54
55
56

#ifdef __WIN32
#define dlsym(l,s) GetProcAddress(l,s)
#define dlclose(l) FreeLibrary(l)
#include <windows.h>
#else
#include <dlfcn.h>

#endif

#ifndef RTLD_NOW
    #define RTLD_NOW 0
#endif
#ifndef RTLD_LOCAL
    #define RTLD_LOCAL 0
................................................................................
Jsi_RC Jsi_LoadLibrary(Jsi_Interp *interp, const char *pathName, bool noInit)
{
    if (interp->noLoad)
        return Jsi_LogError("shared lib load is disabled");
#ifdef __WIN32
    HMODULE handle = LoadLibrary(pathName);
#else







    void *handle = dlopen(pathName, RTLD_NOW | RTLD_LOCAL);
#endif
    if (handle == NULL) {
        // FYI: Valgrind shows a mem-leak here.
        Jsi_LogError("loading extension \"%s\": %s", pathName, dlerror());
        return JSI_ERROR;
    }







>







 







>
>
>
>
>
>
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

#ifdef __WIN32
#define dlsym(l,s) GetProcAddress(l,s)
#define dlclose(l) FreeLibrary(l)
#include <windows.h>
#else
#include <dlfcn.h>
#include <sys/statvfs.h>
#endif

#ifndef RTLD_NOW
    #define RTLD_NOW 0
#endif
#ifndef RTLD_LOCAL
    #define RTLD_LOCAL 0
................................................................................
Jsi_RC Jsi_LoadLibrary(Jsi_Interp *interp, const char *pathName, bool noInit)
{
    if (interp->noLoad)
        return Jsi_LogError("shared lib load is disabled");
#ifdef __WIN32
    HMODULE handle = LoadLibrary(pathName);
#else
#if JSI__LOADNOEXEC
#warning "NOTE: Allowing load from noexec FS"
#else
    struct statvfs vfs;
    if (statvfs(pathName, &vfs) == 0 && vfs.f_flag&ST_NOEXEC)
        return Jsi_LogError("shared libs may not be loaded from a noexec FS");
#endif
    void *handle = dlopen(pathName, RTLD_NOW | RTLD_LOCAL);
#endif
    if (handle == NULL) {
        // FYI: Valgrind shows a mem-leak here.
        Jsi_LogError("loading extension \"%s\": %s", pathName, dlerror());
        return JSI_ERROR;
    }

Changes to src/jsiObj.c.

244
245
246
247
248
249
250
251


252
253
254
255
256
257
258
...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
                Jsi_DecrRefCount(interp, obj->arr[i]);
        Jsi_Free(obj->arr);
        obj->arr = NULL;
    }
    obj->tree = NULL;
    if (obj->clearProto)
        Jsi_DecrRefCount(interp, obj->__proto__);
    _JSI_MEMCLEAR(obj);


    Jsi_Free(obj);
}


/**************************** ARRAY ******************************/

Jsi_Value *jsi_ObjArrayLookup(Jsi_Interp *interp, Jsi_Obj *obj, const char *key) {
................................................................................
    return ++obj->refcnt;
}

int Jsi_ObjDecrRefCount(Jsi_Interp *interp, Jsi_Obj *obj)  {
    SIGASSERT(obj,OBJ);
    if (obj->refcnt<=0) {
#ifdef JSI_MEM_DEBUG
        Jsi_LogBug("Obj double free: %p", obj);
#endif
        return -2;
    }
    jsi_DebugObj(obj,"Decr", jsi_DebugValueCallIdx(), interp);
    int nref;
    if ((nref = --obj->refcnt) <= 0) {
        obj->refcnt = -1;







|
>
>







 







|







244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
                Jsi_DecrRefCount(interp, obj->arr[i]);
        Jsi_Free(obj->arr);
        obj->arr = NULL;
    }
    obj->tree = NULL;
    if (obj->clearProto)
        Jsi_DecrRefCount(interp, obj->__proto__);
#ifdef JSI_MEM_DEBUG
    memset(obj, 0, (sizeof(*obj)-sizeof(obj->VD)));
#endif
    Jsi_Free(obj);
}


/**************************** ARRAY ******************************/

Jsi_Value *jsi_ObjArrayLookup(Jsi_Interp *interp, Jsi_Obj *obj, const char *key) {
................................................................................
    return ++obj->refcnt;
}

int Jsi_ObjDecrRefCount(Jsi_Interp *interp, Jsi_Obj *obj)  {
    SIGASSERT(obj,OBJ);
    if (obj->refcnt<=0) {
#ifdef JSI_MEM_DEBUG
        fprintf(stderr, "Obj decr with ref %d: VD.Idx=%d\n", obj->refcnt, obj->VD.Idx);
#endif
        return -2;
    }
    jsi_DebugObj(obj,"Decr", jsi_DebugValueCallIdx(), interp);
    int nref;
    if ((nref = --obj->refcnt) <= 0) {
        obj->refcnt = -1;

Changes to src/jsiStubs.h.

1
2
3
4
5
6
7
8
9
10
11
12
...
431
432
433
434
435
436
437

438
439
440
441
442
443
444
...
848
849
850
851
852
853
854

855
856
857
858
859
860
861
....
1261
1262
1263
1264
1265
1266
1267

1268
1269
1270
1271
#ifndef __JSI_STUBS_H__
#define __JSI_STUBS_H__
#include "jsi.h"

#define JSI_STUBS_MD5 "37fc4b8060d88c7f408fceb123d7042a"

#undef JSI_EXTENSION_INI
#define JSI_EXTENSION_INI Jsi_Stubs *jsiStubsPtr = NULL;

#ifdef JSI__MUSL
#define JSI_STUBS_BLDFLAGS 1
#else
................................................................................
    Jsi_RC(*_Jsi_CDataStructInit)(Jsi_Interp *interp, uchar* data, const char *sname);
    Jsi_RC(*_Jsi_DllLookup)(Jsi_Interp *interp, const char *module, const char *name, void **ptr);
    Jsi_RC(*_Jsi_LoadLibrary)(Jsi_Interp *interp, const char *pathName, bool noInit);
    Jsi_StructSpec*(*_Jsi_CDataStruct)(Jsi_Interp *interp, const char *name);
    char*(*_Jsi_StrdupLen)(const char *str, int len);
    Jsi_RC(*_Jsi_FileRead)(Jsi_Interp *interp, Jsi_Value *name, Jsi_DString *dStr);
    Jsi_Value*(*_Jsi_ValueNewStringConst)(Jsi_Interp *interp, const char *s, int len);

    void *endPtr;
} Jsi_Stubs;

extern Jsi_Stubs* jsiStubsPtr;

#define __JSI_STUBS_INIT__\
    JSI_STUBS_SIG,    "jsi",    sizeof(Jsi_Stubs),     JSI_STUBS_BLDFLAGS,    JSI_STUBS_MD5,    NULL,\
................................................................................
    Jsi_CDataStructInit,\
    Jsi_DllLookup,\
    Jsi_LoadLibrary,\
    Jsi_CDataStruct,\
    Jsi_StrdupLen,\
    Jsi_FileRead,\
    Jsi_ValueNewStringConst,\

    NULL

#ifdef JSI_USE_STUBS

#define Jsi_InterpNew(n0) JSISTUBCALL(jsiStubsPtr, _Jsi_InterpNew(n0))
#define Jsi_InterpDelete(n0) JSISTUBCALL(jsiStubsPtr, _Jsi_InterpDelete(n0))
#define Jsi_InterpOnDelete(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_InterpOnDelete(n0,n1,n2))
................................................................................
#define Jsi_CDataStructInit(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_CDataStructInit(n0,n1,n2))
#define Jsi_DllLookup(n0,n1,n2,n3) JSISTUBCALL(jsiStubsPtr, _Jsi_DllLookup(n0,n1,n2,n3))
#define Jsi_LoadLibrary(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_LoadLibrary(n0,n1,n2))
#define Jsi_CDataStruct(n0,n1) JSISTUBCALL(jsiStubsPtr, _Jsi_CDataStruct(n0,n1))
#define Jsi_StrdupLen(n0,n1) JSISTUBCALL(jsiStubsPtr, _Jsi_StrdupLen(n0,n1))
#define Jsi_FileRead(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_FileRead(n0,n1,n2))
#define Jsi_ValueNewStringConst(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_ValueNewStringConst(n0,n1,n2))


#endif

#endif




|







 







>







 







>







 







>




1
2
3
4
5
6
7
8
9
10
11
12
...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
...
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
....
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
#ifndef __JSI_STUBS_H__
#define __JSI_STUBS_H__
#include "jsi.h"

#define JSI_STUBS_MD5 "cf5de631c0f0ef3cbc53184129c5818e"

#undef JSI_EXTENSION_INI
#define JSI_EXTENSION_INI Jsi_Stubs *jsiStubsPtr = NULL;

#ifdef JSI__MUSL
#define JSI_STUBS_BLDFLAGS 1
#else
................................................................................
    Jsi_RC(*_Jsi_CDataStructInit)(Jsi_Interp *interp, uchar* data, const char *sname);
    Jsi_RC(*_Jsi_DllLookup)(Jsi_Interp *interp, const char *module, const char *name, void **ptr);
    Jsi_RC(*_Jsi_LoadLibrary)(Jsi_Interp *interp, const char *pathName, bool noInit);
    Jsi_StructSpec*(*_Jsi_CDataStruct)(Jsi_Interp *interp, const char *name);
    char*(*_Jsi_StrdupLen)(const char *str, int len);
    Jsi_RC(*_Jsi_FileRead)(Jsi_Interp *interp, Jsi_Value *name, Jsi_DString *dStr);
    Jsi_Value*(*_Jsi_ValueNewStringConst)(Jsi_Interp *interp, const char *s, int len);
    Jsi_RC(*_Jsi_PathNormalize)(Jsi_Interp *interp, Jsi_Value **pathPtr);
    void *endPtr;
} Jsi_Stubs;

extern Jsi_Stubs* jsiStubsPtr;

#define __JSI_STUBS_INIT__\
    JSI_STUBS_SIG,    "jsi",    sizeof(Jsi_Stubs),     JSI_STUBS_BLDFLAGS,    JSI_STUBS_MD5,    NULL,\
................................................................................
    Jsi_CDataStructInit,\
    Jsi_DllLookup,\
    Jsi_LoadLibrary,\
    Jsi_CDataStruct,\
    Jsi_StrdupLen,\
    Jsi_FileRead,\
    Jsi_ValueNewStringConst,\
    Jsi_PathNormalize,\
    NULL

#ifdef JSI_USE_STUBS

#define Jsi_InterpNew(n0) JSISTUBCALL(jsiStubsPtr, _Jsi_InterpNew(n0))
#define Jsi_InterpDelete(n0) JSISTUBCALL(jsiStubsPtr, _Jsi_InterpDelete(n0))
#define Jsi_InterpOnDelete(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_InterpOnDelete(n0,n1,n2))
................................................................................
#define Jsi_CDataStructInit(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_CDataStructInit(n0,n1,n2))
#define Jsi_DllLookup(n0,n1,n2,n3) JSISTUBCALL(jsiStubsPtr, _Jsi_DllLookup(n0,n1,n2,n3))
#define Jsi_LoadLibrary(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_LoadLibrary(n0,n1,n2))
#define Jsi_CDataStruct(n0,n1) JSISTUBCALL(jsiStubsPtr, _Jsi_CDataStruct(n0,n1))
#define Jsi_StrdupLen(n0,n1) JSISTUBCALL(jsiStubsPtr, _Jsi_StrdupLen(n0,n1))
#define Jsi_FileRead(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_FileRead(n0,n1,n2))
#define Jsi_ValueNewStringConst(n0,n1,n2) JSISTUBCALL(jsiStubsPtr, _Jsi_ValueNewStringConst(n0,n1,n2))
#define Jsi_PathNormalize(n0,n1) JSISTUBCALL(jsiStubsPtr, _Jsi_PathNormalize(n0,n1))

#endif

#endif

Changes to src/jsiValue.c.

37
38
39
40
41
42
43
44



45

46
47
48
49
50
51
52
...
196
197
198
199
200
201
202

203
204
205
206
207
208
209
210
211
    assert(v->refCnt>=0);
    jsi_DebugValue(v,"Incr", jsi_DebugValueCallIdx(), interp);
    return ++(v->refCnt);
}

int Jsi_DecrRefCount(Jsi_Interp* interp, Jsi_Value *v) {
    SIGASSERT(v,VALUE);
    if (v->refCnt<0)



        return -2;

    int ref;
    jsi_DebugValue(v,"Decr", jsi_DebugValueCallIdx(), interp);
    if ((ref = --(v->refCnt)) <= 0) {
        v->refCnt = -1;
        Jsi_ValueFree(interp, v);
    }
    return ref;
................................................................................
#ifdef JSI_MEM_DEBUG
    //if (v->VD.interp != interp)  //TODO: InterpAliasCmd leaking Values.
     //   fprintf(stderr, "cross interp delete: %p\n", v);
    if (v->VD.hPtr) {
        if (!Jsi_HashEntryDelete(v->VD.hPtr))
            fprintf(stderr, "Value not in hash\n");
    }

#endif
    _JSI_MEMCLEAR(v);
    Jsi_Free(v);
}

/* Reset a value back to undefined, releasing string/obj if necessary. */
void Jsi_ValueReset(Jsi_Interp *interp, Jsi_Value **vPtr) {
    Jsi_Value *v = *vPtr;
    SIGASSERTV(v,VALUE);







|
>
>
>

>







 







>

<







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
...
200
201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
    assert(v->refCnt>=0);
    jsi_DebugValue(v,"Incr", jsi_DebugValueCallIdx(), interp);
    return ++(v->refCnt);
}

int Jsi_DecrRefCount(Jsi_Interp* interp, Jsi_Value *v) {
    SIGASSERT(v,VALUE);
    if (v->refCnt<=0) {
#ifdef JSI_MEM_DEBUG
        fprintf(stderr, "Value decr with ref %d: VD.Idx=%d\n", v->refCnt, v->VD.Idx);
#endif
        return -2;
    }
    int ref;
    jsi_DebugValue(v,"Decr", jsi_DebugValueCallIdx(), interp);
    if ((ref = --(v->refCnt)) <= 0) {
        v->refCnt = -1;
        Jsi_ValueFree(interp, v);
    }
    return ref;
................................................................................
#ifdef JSI_MEM_DEBUG
    //if (v->VD.interp != interp)  //TODO: InterpAliasCmd leaking Values.
     //   fprintf(stderr, "cross interp delete: %p\n", v);
    if (v->VD.hPtr) {
        if (!Jsi_HashEntryDelete(v->VD.hPtr))
            fprintf(stderr, "Value not in hash\n");
    }
    memset(v, 0, (sizeof(*v)-sizeof(v->VD)));
#endif

    Jsi_Free(v);
}

/* Reset a value back to undefined, releasing string/obj if necessary. */
void Jsi_ValueReset(Jsi_Interp *interp, Jsi_Value **vPtr) {
    Jsi_Value *v = *vPtr;
    SIGASSERTV(v,VALUE);

Changes to src/jsiWebSocket.c.

787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
....
1307
1308
1309
1310
1311
1312
1313
1314

1315

1316
1317
1318
1319
1320
1321
1322
....
2596
2597
2598
2599
2600
2601
2602


2603
2604
2605
2606
2607
2608
2609
....
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
    if (!n && sb.st_size>0) {
        char fdir[PATH_MAX];
        const char* cr = cmdPtr->curRoot, *fpath;
        if (!Jsi_FSNative(interp, name) || ((fpath= Jsi_Realpath(interp, name, fdir))
            && cr && !Jsi_Strncmp(fpath, cr, Jsi_Strlen(cr))))
            rc = Jsi_FileRead(interp, name, dStr);
        else
            fprintf(stderr, "Skip read file %s\n", Jsi_ValueString(interp, name, NULL));
    }
    if (cmdPtr->noWarn)
        return JSI_OK;
    return rc;
}

static Jsi_RC jsi_wsTemplateFill(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, Jsi_Value *fn, Jsi_DString *dStr,
................................................................................
    } else {
        // Need to read data for non-native files.
        Jsi_DString dStr = {}, fStr = {};
        if (isMdi)
            rc = jsi_wsTemplateFill(interp, cmdPtr, fname, &fStr, (essi?1:0));
        else
            rc = jsi_wsFileRead(interp, fname, &fStr, cmdPtr);
        if (rc != JSI_OK)

            goto nofile;

        int hrc = jsi_wsServeHeader(pss, wsi, (int)Jsi_DSLength(&fStr), 200, Jsi_DSValue(&hStr), mime, &dStr);
        if (hrc>=0) {
            Jsi_DSAppendLen(&dStr, Jsi_DSValue(&fStr), Jsi_DSLength(&fStr));
            char *strVal = Jsi_DSValue(&dStr);
            int strLen = Jsi_DSLength(&dStr);
            hrc = jsi_wswrite(pss, wsi, (unsigned char*)strVal, strLen, LWS_WRITE_HTTP);
        }
................................................................................
    cmdPtr->includeFile = "include.shtml";
    if ((arg != NULL && !Jsi_ValueIsNull(interp,arg))
        && Jsi_OptionsProcess(interp, WSOptions, cmdPtr, arg, 0) < 0) {
bail:
        jsi_wswebsocketObjFree(interp, cmdPtr);
        return JSI_ERROR;
    }


    if (cmdPtr->headers && (Jsi_ValueGetLength(interp, cmdPtr->headers)%2)) {
        Jsi_LogError("Odd header length");
        goto bail;
    }
    const char *up = cmdPtr->urlPrefix, *ur = cmdPtr->urlRedirect;
    if (up && ur && Jsi_Strncmp(ur, up, Jsi_Strlen(up))) {
        Jsi_LogError("urlRedirect does not start with urlPrefix");
................................................................................

    if (cmdPtr->client) {
        struct lws_client_connect_info lci = {};
        lci.context = cmdPtr->context;
        lci.address = cmdPtr->address ? Jsi_ValueString(cmdPtr->interp, cmdPtr->address, NULL) : "127.0.0.1";
        lci.port = cmdPtr->port;
        lci.ssl_connection = cmdPtr->use_ssl;
        lci.path = cmdPtr->rootdir?Jsi_ValueString(cmdPtr->interp, cmdPtr->rootdir, NULL):"/";
        lci.host = cmdPtr->clientHost?cmdPtr->clientHost:lws_canonical_hostname( cmdPtr->context );
        lci.origin = cmdPtr->clientOrigin?cmdPtr->clientOrigin:"origin";
        lci.protocol = cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].name;
        lci.ietf_version_or_minus_one = cmdPtr->ietf_version;
#if (LWS_LIBRARY_VERSION_MAJOR>1)
        if (cmdPtr->post)
            lci.method = "POST";







|







 







|
>

>







 







>
>







 







|







787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
....
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
....
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
....
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
    if (!n && sb.st_size>0) {
        char fdir[PATH_MAX];
        const char* cr = cmdPtr->curRoot, *fpath;
        if (!Jsi_FSNative(interp, name) || ((fpath= Jsi_Realpath(interp, name, fdir))
            && cr && !Jsi_Strncmp(fpath, cr, Jsi_Strlen(cr))))
            rc = Jsi_FileRead(interp, name, dStr);
        else
            fprintf(stderr, "Skip read file %s in %s\n", Jsi_ValueString(interp, name, NULL), (cr?cr:""));
    }
    if (cmdPtr->noWarn)
        return JSI_OK;
    return rc;
}

static Jsi_RC jsi_wsTemplateFill(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, Jsi_Value *fn, Jsi_DString *dStr,
................................................................................
    } else {
        // Need to read data for non-native files.
        Jsi_DString dStr = {}, fStr = {};
        if (isMdi)
            rc = jsi_wsTemplateFill(interp, cmdPtr, fname, &fStr, (essi?1:0));
        else
            rc = jsi_wsFileRead(interp, fname, &fStr, cmdPtr);
        if (rc != JSI_OK) {
            Jsi_DSFree(&fStr);
            goto nofile;
        }
        int hrc = jsi_wsServeHeader(pss, wsi, (int)Jsi_DSLength(&fStr), 200, Jsi_DSValue(&hStr), mime, &dStr);
        if (hrc>=0) {
            Jsi_DSAppendLen(&dStr, Jsi_DSValue(&fStr), Jsi_DSLength(&fStr));
            char *strVal = Jsi_DSValue(&dStr);
            int strLen = Jsi_DSLength(&dStr);
            hrc = jsi_wswrite(pss, wsi, (unsigned char*)strVal, strLen, LWS_WRITE_HTTP);
        }
................................................................................
    cmdPtr->includeFile = "include.shtml";
    if ((arg != NULL && !Jsi_ValueIsNull(interp,arg))
        && Jsi_OptionsProcess(interp, WSOptions, cmdPtr, arg, 0) < 0) {
bail:
        jsi_wswebsocketObjFree(interp, cmdPtr);
        return JSI_ERROR;
    }
    Jsi_PathNormalize(interp, &cmdPtr->rootdir);

    if (cmdPtr->headers && (Jsi_ValueGetLength(interp, cmdPtr->headers)%2)) {
        Jsi_LogError("Odd header length");
        goto bail;
    }
    const char *up = cmdPtr->urlPrefix, *ur = cmdPtr->urlRedirect;
    if (up && ur && Jsi_Strncmp(ur, up, Jsi_Strlen(up))) {
        Jsi_LogError("urlRedirect does not start with urlPrefix");
................................................................................

    if (cmdPtr->client) {
        struct lws_client_connect_info lci = {};
        lci.context = cmdPtr->context;
        lci.address = cmdPtr->address ? Jsi_ValueString(cmdPtr->interp, cmdPtr->address, NULL) : "127.0.0.1";
        lci.port = cmdPtr->port;
        lci.ssl_connection = cmdPtr->use_ssl;
        lci.path = Jsi_ValueString(cmdPtr->interp, cmdPtr->rootdir, NULL);
        lci.host = cmdPtr->clientHost?cmdPtr->clientHost:lws_canonical_hostname( cmdPtr->context );
        lci.origin = cmdPtr->clientOrigin?cmdPtr->clientOrigin:"origin";
        lci.protocol = cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].name;
        lci.ietf_version_or_minus_one = cmdPtr->ietf_version;
#if (LWS_LIBRARY_VERSION_MAJOR>1)
        if (cmdPtr->post)
            lci.method = "POST";

Changes to src/main.c.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
..
42
43
44
45
46
47
48





/* JSI main program */
#ifndef JSI_AMALGAMATION
#include "jsi.h"
#endif
#include <string.h>
#include <stdlib.h>


int main(int argc, char **argv)
{
     // A replacement for shebang "#!/usr/bin/env".
    Jsi_DString sStr = {};
    FILE *fp = NULL;
    if (argc >= 3 && Jsi_Strchr(argv[1], ' ') && Jsi_Strstr(argv[1], "%s")) {
        Jsi_DString tStr = {};
        int i;
................................................................................
    Jsi_InterpOpts opts = {.argc=argc, .argv=argv};
    Jsi_Interp *interp = Jsi_Main(&opts);
    if (!interp) return opts.exitCode;
    Jsi_InterpDelete(interp);
    Jsi_DSFree(&sStr);
    exit(0);
}












>
|







 







>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
43
44
45
46
47
48
49
50
51
52
53
54
/* JSI main program */
#ifndef JSI_AMALGAMATION
#include "jsi.h"
#endif
#include <string.h>
#include <stdlib.h>

#ifndef JSI_LITE_ONLY
int jsi_main(int argc, char **argv)
{
     // A replacement for shebang "#!/usr/bin/env".
    Jsi_DString sStr = {};
    FILE *fp = NULL;
    if (argc >= 3 && Jsi_Strchr(argv[1], ' ') && Jsi_Strstr(argv[1], "%s")) {
        Jsi_DString tStr = {};
        int i;
................................................................................
    Jsi_InterpOpts opts = {.argc=argc, .argv=argv};
    Jsi_Interp *interp = Jsi_Main(&opts);
    if (!interp) return opts.exitCode;
    Jsi_InterpDelete(interp);
    Jsi_DSFree(&sStr);
    exit(0);
}

#if JSI__MAIN
int main(int argc, char **argv) { return jsi_main(argc, argv); }
#endif
#endif

Changes to tests/badfunc.jsi.

19
20
21
22
23
24
25
26
27
28
29
30
31
;Info.funcs(x);
;Info.data(x);
;Info.data();

/*
=!EXPECTSTART!=
e ==> 'xx', functions are: bad big ugly.
e2 ==> 'xx' sub-commands are: atime chdir chmod copy dirname executable exists extension glob isdir isfile isrelative join link lstat mkdir mtime owned pwd read readable readlink realpath remove rename rootname size stat tail tempfile truncate type writable write.
Info.funcs(x) ==> [ "bad", "big", "ugly" ]
Info.data(x) ==> [ "a", "b" ]
Info.data() ==> [ "x" ]
=!EXPECTEND!=
*/







|





19
20
21
22
23
24
25
26
27
28
29
30
31
;Info.funcs(x);
;Info.data(x);
;Info.data();

/*
=!EXPECTSTART!=
e ==> 'xx', functions are: bad big ugly.
e2 ==> 'xx' sub-commands are: atime chdir chmod copy dirname executable exists extension glob isdir isfile isrelative join link lstat mkdir mknod mtime owned pwd read readable readlink realpath remove rename rootname size stat tail tempfile truncate type writable write.
Info.funcs(x) ==> [ "bad", "big", "ugly" ]
Info.data(x) ==> [ "a", "b" ]
Info.data() ==> [ "x" ]
=!EXPECTEND!=
*/

Added tools/fossil/Makefile.static.































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/usr/bin/make
#
# This is the top-level makefile for Fossil when the build is occurring
# on a unix platform.  This works out-of-the-box on most unix platforms.
# But you are free to vary some of the definitions if desired.
#
#### The toplevel directory of the source tree.  Fossil can be built
#    in a directory that is separate from the source tree.  Just change
#    the following to point from the build directory to the src/ folder.
#
SRCDIR = ./src

#### The directory into which object code files should be written.
#
#
OBJDIR = ./bld

#### C Compiler and options for use in building executables that
#    will run on the platform that is doing the build.  This is used
#    to compile code-generator programs as part of the build process.
#    See TCC below for the C compiler for building the finished binary.
#
BCC = gcc
BCCFLAGS = $(CFLAGS)

#### The suffix to add to final executable file.  When cross-compiling
#    to windows, make this ".exe".  Otherwise leave it blank.
#
E =

#### C Compile and options for use in building executables that
#    will run on the target platform.  This is usually the same
#    as BCC, unless you are cross-compiling.  This C compiler builds
#    the finished binary for fossil.  The BCC compiler above is used
#    for building intermediate code-generator tools.
#
#TCC = gcc -O6
#TCC = gcc -g -O0 -Wall -fprofile-arcs -ftest-coverage
#TCC = gcc -g -Os -Wall
TCC = musl-gcc -g -Os -Wall -static -DJSI__MUSL=1 -DJSI__MINIZ=1

# To use the included miniz library
# FOSSIL_ENABLE_MINIZ = 1
TCC += -DFOSSIL_ENABLE_MINIZ

# To add support for HTTPS
#TCC += -DFOSSIL_ENABLE_SSL

# To enable legacy mv/rm support
TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1

#### We sometimes add the -static option here so that we can build a
#    static executable that will run in a chroot jail.
#LIB = -static
TCC += -DFOSSIL_DYNAMIC_BUILD=1

TCCFLAGS = $(CFLAGS)

#### Extra arguments for linking the finished binary.  Fossil needs
#    to link against the Z-Lib compression library unless the miniz
#    library in the source tree is being used.  There are no other
#    required dependencies.
#ZLIB_LIB.0 = -lz
ZLIB_LIB.0 = 
ZLIB_LIB.1 =
ZLIB_LIB.  = $(ZLIB_LIB.0)

# If using zlib:
LIB += $(ZLIB_LIB.$(FOSSIL_ENABLE_MINIZ)) $(LDFLAGS)

# If using HTTPS:
#LIB += -lcrypto -lssl

#### Tcl shell for use in running the fossil testsuite.  If you do not
#    care about testing the end result, this can be blank.
#
TCLSH = tclsh

# You should not need to change anything below this line
###############################################################################
#
# Automatic platform-specific options.
HOST_OS_CMD = uname -s
HOST_OS = $(HOST_OS_CMD:sh)

LIB.SunOS= -lsocket -lnsl
LIB += $(LIB.$(HOST_OS))

TCC.DragonFly += -DUSE_PREAD
TCC.FreeBSD += -DUSE_PREAD
TCC.NetBSD += -DUSE_PREAD
TCC.OpenBSD += -DUSE_PREAD
TCC += $(TCC.$(HOST_OS))

include $(SRCDIR)/main.mk

Added tools/fossil/main.c.diff.

































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
--- main.c.orig	2019-01-15 11:03:38.198955797 -0800
+++ main.c	2019-01-17 08:29:37.977503492 -0800
@@ -594,6 +594,17 @@
   }
 }
 
+#if 0
+#undef MINIZ_HEADER_FILE_ONLY
+#include "miniz.c"
+#include "linenoise.c"
+
+#else
+#undef MINIZ_HEADER_FILE_ONLY
+#include "jsi.c"
+#include "linenoise.c"
+#endif
+
 /*
 ** This procedure runs first.
 */
@@ -611,6 +622,11 @@
   const CmdOrPage *pCmd = 0;
   int rc;
 
+  const char *ce = strrchr(argv[0], '/');
+  ce = (ce?ce+1:argv[0]);
+  if (!strcmp("jsish", ce))
+      return jsi_main(argc, argv);
+
 #if !defined(_WIN32_WCE)
   if( fossil_getenv("FOSSIL_BREAK") ){
     if( isatty(0) && isatty(2) ){

Added tools/mkjail.sh.

































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#!/bin/sh
# Create and mount jail for jsish, or unmount if exists
if [ `whoami` != "root" ]; then
  echo "must run as root or use sudo"
  exit 1
fi

DST=home001
if [ "$1" != "" ]; then
  DST=$1
fi
FMNT=`findmnt $DST`
if [ "$FMNT" != "" ]; then
    echo "Unmounting $DST"
    umount $DST/bin
    umount $DST/bin
    umount $DST
    exit 0;
fi
echo Mounting $DST
if [ ! -d bin ]; then
    mkdir bin 
    mknod -m 0666 bin/null c 1 3 
    mknod -m 0666 bin/random c 1 8
    mknod -m 0666 bin/urandom c 1 9 
    cat <<EOF > bin/hosts
127.0.0.1       localhost
EOF
    cat <<EOF > bin/resolv.conf 
nameserver 127.0.1.1
search local
EOF
fi

mkdir -p $DST
(cd $DST
  mkdir -p bin
  ln -sf bin etc
  ln -sf bin dev
)
mount --bind $DST $DST
mount --bind bin $DST/bin
mount -o remount,nodev,noexec,nosuid $DST $DST
mount -o bind,ro bin $DST/bin

findmnt | fgrep [
exit 0
#

Changes to tools/mkref.jsi.

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
            break;
        default:
            throw("bad id");
            continue;
        }
    }
    index + "\n";
    var rlnks = '', resfx = '', repre = '<B>JSI REFERENCE</B> (See <a href="#System">System</a> for globals)\n';
    if (self.md) {
        //rlnks = '(Related: [Functions](functions.wiki), [Syntax](language.wiki)).\n';
        //repre = '<a name="TOC"></a>\n(insert mainmenu.md.html here)\n'+repre;
        index = '';
        //rv = rv.map(['$', '\\$']);
        //resfx = '\n<!-- Markdeep: --><style class="fallback">body{visibility:hidden;}</style>';
           // + '<script>window.markdeepOptions={tocStyle:"medium"}</script>'







|







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
            break;
        default:
            throw("bad id");
            continue;
        }
    }
    index + "\n";
    var rlnks = '', resfx = '', repre = '<B>JSI REFERENCE</B>: <a href="#System">System</a> contains global methods\n';
    if (self.md) {
        //rlnks = '(Related: [Functions](functions.wiki), [Syntax](language.wiki)).\n';
        //repre = '<a name="TOC"></a>\n(insert mainmenu.md.html here)\n'+repre;
        index = '';
        //rv = rv.map(['$', '\\$']);
        //resfx = '\n<!-- Markdeep: --><style class="fallback">body{visibility:hidden;}</style>';
           // + '<script>window.markdeepOptions={tocStyle:"medium"}</script>'

Changes to tools/protos.jsi.

1
2
3
4
5
6
7
8
..
95
96
97
98
99
100
101

102
103
104
105
106
107
108
//JSI Command Prototypes: version 2.7.1
throw("NOT EXECUTABLE: USE FILE IN GEANY EDITOR FOR CMD LINE COMPLETION + GOTO TAG");

var Array = function(cmd,args) {};
Array.prototype.concat = function(...):array {};
Array.prototype.every = function(callback:function):any {};
Array.prototype.fill = function(value:any, start:number=0, end:number=-1):array {};
Array.prototype.filter = function(callback:function, this:object=void):array {};
................................................................................
File.prototype.isdir = function(file:string):boolean {};
File.prototype.isfile = function(file:string):boolean {};
File.prototype.isrelative = function(file:string):boolean {};
File.prototype.join = function(path:string, path:string):string {};
File.prototype.link = function(src:string, dest:string, ishard:boolean=false):any {};
File.prototype.lstat = function(file:string):object {};
File.prototype.mkdir = function(file:string):any {};

File.prototype.mtime = function(file:string):number {};
File.prototype.owned = function(file:string):boolean {};
File.prototype.pwd = function():string {};
File.prototype.read = function(file:string, mode:string='rb'):string {};
File.prototype.readable = function(file:string):boolean {};
File.prototype.readlink = function(file:string):string {};
File.prototype.realpath = function(file:string):string {};
|







 







>







1
2
3
4
5
6
7
8
..
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//JSI Command Prototypes: version 2.7.2
throw("NOT EXECUTABLE: USE FILE IN GEANY EDITOR FOR CMD LINE COMPLETION + GOTO TAG");

var Array = function(cmd,args) {};
Array.prototype.concat = function(...):array {};
Array.prototype.every = function(callback:function):any {};
Array.prototype.fill = function(value:any, start:number=0, end:number=-1):array {};
Array.prototype.filter = function(callback:function, this:object=void):array {};
................................................................................
File.prototype.isdir = function(file:string):boolean {};
File.prototype.isfile = function(file:string):boolean {};
File.prototype.isrelative = function(file:string):boolean {};
File.prototype.join = function(path:string, path:string):string {};
File.prototype.link = function(src:string, dest:string, ishard:boolean=false):any {};
File.prototype.lstat = function(file:string):object {};
File.prototype.mkdir = function(file:string):any {};
File.prototype.mknod = function(file:string, mode:number, dev:number):any {};
File.prototype.mtime = function(file:string):number {};
File.prototype.owned = function(file:string):boolean {};
File.prototype.pwd = function():string {};
File.prototype.read = function(file:string, mode:string='rb'):string {};
File.prototype.readable = function(file:string):boolean {};
File.prototype.readlink = function(file:string):string {};
File.prototype.realpath = function(file:string):string {};

Changes to www/reference.wiki.

1
2
3
4
5
6
7
8
9
10
...
450
451
452
453
454
455
456

457
458
459
460
461
462
463
...
642
643
644
645
646
647
648

649
650
651
652
653
654
655
....
1354
1355
1356
1357
1358
1359
1360

1361
1362
1363
1364
1365
1366
1367
<title>Reference</title>
<p>
<B>JSI REFERENCE</B> (See <a href="#System">System</a> for globals)
(Related: [./functions.wiki|Functions], [./language.wiki|Syntax]).
<p>
<a name="TOC"></a>
<a href='#Array'>Array</a>
<a href='#Boolean'>Boolean</a>
<a href='#CData'>CData</a>
<a href='#CEnum'>CEnum</a>
................................................................................
<tr><td>isdir</td><td>isdir(file:string):boolean </td><td>Return true if file is a directory.</td></tr>
<tr><td>isfile</td><td>isfile(file:string):boolean </td><td>Return true if file is a normal file.</td></tr>
<tr><td>isrelative</td><td>isrelative(file:string):boolean </td><td>Return true if file path is relative.</td></tr>
<tr><td>join</td><td>join(path:string, path:string):string </td><td>Join two file realpaths, or just second if an absolute path.</td></tr>
<tr><td>link</td><td>link(src:string, dest:string, ishard:boolean=false) </td><td>Link a file. The second argument is the destination file to be created. If a third bool argument is true, a hard link is created.</td></tr>
<tr><td>lstat</td><td>lstat(file:string):object </td><td>Return status info for file.</td></tr>
<tr><td>mkdir</td><td>mkdir(file:string) </td><td>Create a directory.</td></tr>

<tr><td>mtime</td><td>mtime(file:string):number </td><td>Return file modified time.</td></tr>
<tr><td>owned</td><td>owned(file:string):boolean </td><td>Return true if file is owned by user.</td></tr>
<tr><td>pwd</td><td>pwd():string </td><td>Return current directory.</td></tr>
<tr><td>read</td><td>read(file:string, mode:string='rb'):string </td><td>Read a file.</td></tr>
<tr><td>readable</td><td>readable(file:string):boolean </td><td>Return true if file is readable.</td></tr>
<tr><td>readlink</td><td>readlink(file:string):string </td><td>Read file link destination.</td></tr>
<tr><td>realpath</td><td>realpath(file:string):string </td><td>Return absolute file name minus .., ./ etc.</td></tr>
................................................................................
<tr><td>onEval</td><td><i>FUNC</i></td><td>Function to get control for interactive evals. @function(cmd:string)</td><td><i></i></td></tr>
<tr><td>onExit</td><td><i>FUNC</i></td><td>Command to call in parent on exit, returns true to continue. @function()</td><td><i>initOnly</i></td></tr>
<tr><td>opTrace</td><td><i>INT</i></td><td>Set debugging level for OPCODE execution.</td><td><i></i></td></tr>
<tr><td>pkgDirs</td><td><i>ARRAY</i></td><td>list of library directories for require() to search.</td><td><i></i></td></tr>
<tr><td>profile</td><td><i>BOOL</i></td><td>On exit generate profile of function calls.</td><td><i></i></td></tr>
<tr><td>recvCallback</td><td><i>CUSTOM</i></td><td>Command to recv 'send' msgs from parent interp.</td><td><i></i></td></tr>
<tr><td>retValue</td><td><i>VALUE</i></td><td>Return value from last eval.</td><td><i>readOnly</i></td></tr>

<tr><td>safeReadDirs</td><td><i>ARRAY</i></td><td>In safe mode, files/dirs to allow reads from.</td><td><i>initOnly</i></td></tr>
<tr><td>safeWriteDirs</td><td><i>ARRAY</i></td><td>In safe mode, files/dirs to allow writes to.</td><td><i>initOnly</i></td></tr>
<tr><td>safeExecPattern</td><td><i>STRKEY</i></td><td>In safe mode, regexp pattern allow exec of commands.</td><td><i>initOnly</i></td></tr>
<tr><td>scriptStr</td><td><i>STRKEY</i></td><td>Interp init script string.</td><td><i>initOnly</i></td></tr>
<tr><td>scriptFile</td><td><i>STRING</i></td><td>Interp init script file.</td><td><i></i></td></tr>
<tr><td>stdinStr</td><td><i>STRING</i></td><td>String to use as stdin for console.input().</td><td><i></i></td></tr>
<tr><td>stdoutStr</td><td><i>STRING</i></td><td>String to collect stdout for puts().</td><td><i></i></td></tr>
................................................................................
<table border="1" class="optstbl table">
<tr><th>Option</th> <th>Type</th> <th>Description</th><th>Flags</th></tr>
<tr><td>bg</td><td><i>BOOL</i></td><td>Run command in background using system() and return OS code.</td><td><i></i></td></tr>
<tr><td>chdir</td><td><i>STRING</i></td><td>Change to directory.</td><td><i></i></td></tr>
<tr><td>inputStr</td><td><i>STRING</i></td><td>Use string as input and return OS code.</td><td><i></i></td></tr>
<tr><td>noError</td><td><i>BOOL</i></td><td>Suppress all OS errors.</td><td><i></i></td></tr>
<tr><td>noRedir</td><td><i>BOOL</i></td><td>Disable redirect and shell escapes in command.</td><td><i></i></td></tr>

<tr><td>trim</td><td><i>BOOL</i></td><td>Trim trailing whitespace from output.</td><td><i></i></td></tr>
<tr><td>retAll</td><td><i>BOOL</i></td><td>Return the OS return code and data as an object.</td><td><i></i></td></tr>
<tr><td>retCode</td><td><i>BOOL</i></td><td>Return only the OS return code.</td><td><i></i></td></tr>
</table>


<a name="System.sourceOptions"></a>


|







 







>







 







>







 







>







1
2
3
4
5
6
7
8
9
10
...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
...
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
....
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
<title>Reference</title>
<p>
<B>JSI REFERENCE</B>: <a href="#System">System</a> contains global methods
(Related: [./functions.wiki|Functions], [./language.wiki|Syntax]).
<p>
<a name="TOC"></a>
<a href='#Array'>Array</a>
<a href='#Boolean'>Boolean</a>
<a href='#CData'>CData</a>
<a href='#CEnum'>CEnum</a>
................................................................................
<tr><td>isdir</td><td>isdir(file:string):boolean </td><td>Return true if file is a directory.</td></tr>
<tr><td>isfile</td><td>isfile(file:string):boolean </td><td>Return true if file is a normal file.</td></tr>
<tr><td>isrelative</td><td>isrelative(file:string):boolean </td><td>Return true if file path is relative.</td></tr>
<tr><td>join</td><td>join(path:string, path:string):string </td><td>Join two file realpaths, or just second if an absolute path.</td></tr>
<tr><td>link</td><td>link(src:string, dest:string, ishard:boolean=false) </td><td>Link a file. The second argument is the destination file to be created. If a third bool argument is true, a hard link is created.</td></tr>
<tr><td>lstat</td><td>lstat(file:string):object </td><td>Return status info for file.</td></tr>
<tr><td>mkdir</td><td>mkdir(file:string) </td><td>Create a directory.</td></tr>
<tr><td>mknod</td><td>mknod(file:string, mode:number, dev:number) </td><td>Create unix device file using mknod.</td></tr>
<tr><td>mtime</td><td>mtime(file:string):number </td><td>Return file modified time.</td></tr>
<tr><td>owned</td><td>owned(file:string):boolean </td><td>Return true if file is owned by user.</td></tr>
<tr><td>pwd</td><td>pwd():string </td><td>Return current directory.</td></tr>
<tr><td>read</td><td>read(file:string, mode:string='rb'):string </td><td>Read a file.</td></tr>
<tr><td>readable</td><td>readable(file:string):boolean </td><td>Return true if file is readable.</td></tr>
<tr><td>readlink</td><td>readlink(file:string):string </td><td>Read file link destination.</td></tr>
<tr><td>realpath</td><td>realpath(file:string):string </td><td>Return absolute file name minus .., ./ etc.</td></tr>
................................................................................
<tr><td>onEval</td><td><i>FUNC</i></td><td>Function to get control for interactive evals. @function(cmd:string)</td><td><i></i></td></tr>
<tr><td>onExit</td><td><i>FUNC</i></td><td>Command to call in parent on exit, returns true to continue. @function()</td><td><i>initOnly</i></td></tr>
<tr><td>opTrace</td><td><i>INT</i></td><td>Set debugging level for OPCODE execution.</td><td><i></i></td></tr>
<tr><td>pkgDirs</td><td><i>ARRAY</i></td><td>list of library directories for require() to search.</td><td><i></i></td></tr>
<tr><td>profile</td><td><i>BOOL</i></td><td>On exit generate profile of function calls.</td><td><i></i></td></tr>
<tr><td>recvCallback</td><td><i>CUSTOM</i></td><td>Command to recv 'send' msgs from parent interp.</td><td><i></i></td></tr>
<tr><td>retValue</td><td><i>VALUE</i></td><td>Return value from last eval.</td><td><i>readOnly</i></td></tr>
<tr><td>safeMode</td><td><i>STRKEY</i></td><td>Set isSafe mode and setup safeReadDirs and/or safeWriteDirs for pwd and script-dir. (one of: <b>none</b>, <b>read</b>, <b>write</b>, <b>write2</b>)</td><td><i>initOnly</i></td></tr>
<tr><td>safeReadDirs</td><td><i>ARRAY</i></td><td>In safe mode, files/dirs to allow reads from.</td><td><i>initOnly</i></td></tr>
<tr><td>safeWriteDirs</td><td><i>ARRAY</i></td><td>In safe mode, files/dirs to allow writes to.</td><td><i>initOnly</i></td></tr>
<tr><td>safeExecPattern</td><td><i>STRKEY</i></td><td>In safe mode, regexp pattern allow exec of commands.</td><td><i>initOnly</i></td></tr>
<tr><td>scriptStr</td><td><i>STRKEY</i></td><td>Interp init script string.</td><td><i>initOnly</i></td></tr>
<tr><td>scriptFile</td><td><i>STRING</i></td><td>Interp init script file.</td><td><i></i></td></tr>
<tr><td>stdinStr</td><td><i>STRING</i></td><td>String to use as stdin for console.input().</td><td><i></i></td></tr>
<tr><td>stdoutStr</td><td><i>STRING</i></td><td>String to collect stdout for puts().</td><td><i></i></td></tr>
................................................................................
<table border="1" class="optstbl table">
<tr><th>Option</th> <th>Type</th> <th>Description</th><th>Flags</th></tr>
<tr><td>bg</td><td><i>BOOL</i></td><td>Run command in background using system() and return OS code.</td><td><i></i></td></tr>
<tr><td>chdir</td><td><i>STRING</i></td><td>Change to directory.</td><td><i></i></td></tr>
<tr><td>inputStr</td><td><i>STRING</i></td><td>Use string as input and return OS code.</td><td><i></i></td></tr>
<tr><td>noError</td><td><i>BOOL</i></td><td>Suppress all OS errors.</td><td><i></i></td></tr>
<tr><td>noRedir</td><td><i>BOOL</i></td><td>Disable redirect and shell escapes in command.</td><td><i></i></td></tr>
<tr><td>noShell</td><td><i>BOOL</i></td><td>Do not use native popen which invokes via /bin/sh.</td><td><i></i></td></tr>
<tr><td>trim</td><td><i>BOOL</i></td><td>Trim trailing whitespace from output.</td><td><i></i></td></tr>
<tr><td>retAll</td><td><i>BOOL</i></td><td>Return the OS return code and data as an object.</td><td><i></i></td></tr>
<tr><td>retCode</td><td><i>BOOL</i></td><td>Return only the OS return code.</td><td><i></i></td></tr>
</table>


<a name="System.sourceOptions"></a>