/**
This file will be automatically included by the th1ish binary if
found during startup and if named the same as the binary with a
".th1ish" extension. If the binary name ends in ".exe" or ".EXE"
then that part of the name is ignored.
This script gets run in the global scope.
This particular init script performs the following:
- Extends api.PathFinder with more useful functionality.
- If loadModule() and/or import() are defined then it adds
api.loadModule() and api.import(), both of which use PathFinder
instances to search for modules/scripts. Their PathFinder
instances are available via api.(loadModule|import).path.
- If getenv() is defined then environment variables
TH1ISH_SCRIPT_PATH and TH1ISH_MODULE_PATH are treated as colon- or
simecolon-delimited directories to search for scripts/modules. By
default only the current directory is searched. TH1ISH_AUTOLOAD_MODULES
will be treated as a comma-separated list of module names
to load at startup.
*/
scope {
if('undefined' === typename api){return}
else if(api.loadModule && api.loadModule.impl){
/* Been there, done that. */
return api
}
if('function' === typename strftime){
api.string.strftime = strftime
}
if('undefined' !== typename api.PathFinder){
const PF = api.PathFinder
const loadModule = ('function' === typename loadModule) ? loadModule : undefined
const import = ('function' === typename import) ? import : undefined
const getenv = ('function' === typename getenv) ? getenv : undefined
const pwd = getenv ? [getenv 'PWD'] : undefined
/**
Utility function which splits str on
sep and appends the resulting array
to dest. dest need not be an array but
must have a push() method which acts like
Array.push() does.
Returns this object.
If v does not contain sep then it falls
back to trying this.splitEntriesTo.fallbackSeparator.
*/
PF.splitEntriesTo = proc(dest,str,sep=':'){
!str && return this
assert 'function' === typename dest.push
const fb = argv.callee.fallbackSeparator
(fb!==sep) &&
/* BUG: when conventional call() syntax is DISabled
i'm getting a 'str.indexOf is not a function' exception
here. ??? */
([str.indexOf sep] < 0) &&
([str.indexOf fb] >= 0) &&
(sep = fb)
var i = 0, li = [str.split sep]
const n = [li.length]
for {}{i<n}{i+=1}{
[dest.push li.(i)]
}
return this
}
PF.splitEntriesTo.fallbackSeparator = ';'
/**
setPathFromEnv() expects an environment variable
name as an argument. If such an environment variable
is found then it is treated as a delimited list and
replaces the current list of paths. This modifies
this.prefix directly, as opposed to creating a new
array, so it may have side-effects on client-taken
references. See splitEntriesTo() for details on
the splitting.
Returns a truthy value if finds an environment variable,
else a falsy value.
*/
if(getenv){
PF.setPathFromEnv = $proc(v, sep=':'){
const e = getenv(v)
e || return
if(this.prefix) {[this.prefix.length 0]}
else {this.prefix = array[]}
[this.splitEntriesTo this.prefix e sep]
return true
}.importSymbols 'getenv'
}
if(loadModule){
/**
api.loadModule() is a loadModule() proxy which uses the
api.loadModule.path search path to search for DLLs.
If onLoad is a function then after loading the module,
onLoad(namespace, baseName, fullname) is called.
Returns this object.
*/
api.loadModule = proc(baseName,namespace = this, onLoad){
const ce = argv.callee
const n = [ce.path.search baseName] ||
throw ["No loadable module found named [".concat baseName '].']
[ce.impl n /*baseName?*/ namespace]
('function' === typename onLoad) && scope {
[onLoad namespace baseName n]
}
return this
}
api.loadModule.impl = loadModule
const pf = api.loadModule.path = [PF]
pf.suffix = array['.so','.dll']
if(pf.setPathFromEnv && ![pf.setPathFromEnv 'TH1ISH_MODULE_PATH']) {
/**
^^^ Reminder to self: FIXME: if (ie->skipNLLevel>0) then
that behaves _completely_ wrong, causing pf's state
to be very unexpected when splitEntriesTo is called.
*/
pf.prefix = array['','.']
pwd && [pf.prefix.push pwd]
}
}
if(import){
/**
api.import() is an import() proxy which uses the
api.import.path search path to search for scripts.
Returns the result of calling import() or throws
on error.
*/
api.import = proc(baseName){
const ce = argv.callee
var n
if( Fossil.file.isAccessible(baseName) ){
/* Workaround for a problem on my hoster */
return [ce.impl baseName]
}else{
n = [ce.path.search baseName] ||
throw ["No script file found named [".concat
baseName '].']
}
return [ce.impl n]
}
api.import.impl = import
const pf = api.import.path = [PF]
pf.suffix = array['.th1ish', '.fossi1ish']
if(!pf.setPathFromEnv || ![pf.setPathFromEnv 'TH1ISH_SCRIPT_PATH']) {
pf.prefix = array['','.'];
pwd && [pf.prefix.push pwd];
}
pf.prefix.unshift('' /* workaround for passing absolute paths to it */)
}
// Try to pre-load some plugins...
while('function' === typename api.loadModule){
//const testOnLoad = proc(ns,bn,fn){print("Loaded module",bn,"from",fn)}
var list = getenv ? [getenv 'TH1ISH_AUTOLOAD_MODULES'] : false
list && (list = [list.split ','])
list || break
{ Here's one potential list:
list = array [
'cpdoish',
'linenoiseish',
'pcreish',
'uuidish',
'sha1ish',
'popenish'
]
}
[list.eachIndex proc(v,i){
v = (v ? [v.trim] : 0)
v || return
//var ex = catch{[api.loadModule v api /*testOnLoad*/]}
//ex && print(__SRC,"Warning: module not loaded:",ex.message || v)
[api.loadModule v api /*testOnLoad*/]
}]
/* BUG:
the above call with an outer [...] syntax IF we use
an inlined array rather than an array reference.
Evaluates to the array instead of calling the function.
It never reaches the '.' after the array closer.
Not sure (===no idea) why. Conventional call syntax
works.
*/
break
}
}
api
}